Merge pull request #107317 from neolit123/1.24-change-kubeadm-cr-auto-detection

kubeadm: adapt CRI detection and defaults after the dockershim removal
This commit is contained in:
Kubernetes Prow Robot 2022-01-17 11:00:50 -08:00 committed by GitHub
commit 8a25964153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 90 additions and 220 deletions

View File

@ -169,7 +169,7 @@ limitations under the License.
// - system:bootstrappers:kubeadm:default-node-token // - system:bootstrappers:kubeadm:default-node-token
// nodeRegistration: // nodeRegistration:
// name: "ec2-10-100-0-1" // name: "ec2-10-100-0-1"
// criSocket: "unix:///var/run/dockershim.sock" // criSocket: "unix:///var/run/containerd/containerd.sock"
// taints: // taints:
// - key: "kubeadmNode" // - key: "kubeadmNode"
// value: "master" // value: "master"

View File

@ -173,7 +173,7 @@ limitations under the License.
// - system:bootstrappers:kubeadm:default-node-token // - system:bootstrappers:kubeadm:default-node-token
// nodeRegistration: // nodeRegistration:
// name: "ec2-10-100-0-1" // name: "ec2-10-100-0-1"
// criSocket: "unix:///var/run/dockershim.sock" // criSocket: "unix:///var/run/containerd/containerd.sock"
// taints: // taints:
// - key: "kubeadmNode" // - key: "kubeadmNode"
// value: "master" // value: "master"

View File

@ -579,7 +579,7 @@ func TestValidateJoinConfiguration(t *testing.T) {
}, },
NodeRegistration: kubeadmapi.NodeRegistrationOptions{ NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "aaa", Name: "aaa",
CRISocket: "unix:///var/run/dockershim.sock", CRISocket: "unix:///var/run/containerd/containerd.sock",
}, },
}, true}, }, true},
{&kubeadmapi.JoinConfiguration{ // Pass with JoinControlPlane {&kubeadmapi.JoinConfiguration{ // Pass with JoinControlPlane
@ -594,7 +594,7 @@ func TestValidateJoinConfiguration(t *testing.T) {
}, },
NodeRegistration: kubeadmapi.NodeRegistrationOptions{ NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "aaa", Name: "aaa",
CRISocket: "unix:///var/run/dockershim.sock", CRISocket: "unix:///var/run/containerd/containerd.sock",
}, },
ControlPlane: &kubeadmapi.JoinControlPlane{ ControlPlane: &kubeadmapi.JoinControlPlane{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{ LocalAPIEndpoint: kubeadmapi.APIEndpoint{
@ -615,7 +615,7 @@ func TestValidateJoinConfiguration(t *testing.T) {
}, },
NodeRegistration: kubeadmapi.NodeRegistrationOptions{ NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "aaa", Name: "aaa",
CRISocket: "unix:///var/run/dockershim.sock", CRISocket: "unix:///var/run/containerd/containerd.sock",
}, },
ControlPlane: &kubeadmapi.JoinControlPlane{ ControlPlane: &kubeadmapi.JoinControlPlane{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{ LocalAPIEndpoint: kubeadmapi.APIEndpoint{
@ -636,7 +636,7 @@ func TestValidateJoinConfiguration(t *testing.T) {
}, },
NodeRegistration: kubeadmapi.NodeRegistrationOptions{ NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "aaa", Name: "aaa",
CRISocket: "/var/run/dockershim.sock", CRISocket: "unix:///var/run/containerd/containerd.sock",
}, },
ControlPlane: &kubeadmapi.JoinControlPlane{ ControlPlane: &kubeadmapi.JoinControlPlane{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{ LocalAPIEndpoint: kubeadmapi.APIEndpoint{

View File

@ -359,7 +359,7 @@ kind: InitConfiguration
localAPIEndpoint: localAPIEndpoint:
advertiseAddress: 192.0.2.1 advertiseAddress: 192.0.2.1
nodeRegistration: nodeRegistration:
criSocket: /path/to/dockershim.sock criSocket: "unix:///var/run/containerd/containerd.sock"
--- ---
apiVersion: %[1]s apiVersion: %[1]s
kind: ClusterConfiguration kind: ClusterConfiguration

View File

@ -211,7 +211,7 @@ func getDefaultNodeConfigBytes() ([]byte, error) {
}, },
}, },
NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{ NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{
CRISocket: constants.DefaultDockerCRISocket, // avoid CRI detection CRISocket: constants.DefaultCRISocket, // avoid CRI detection
}, },
}) })
if err != nil { if err != nil {

View File

@ -363,10 +363,10 @@ func TestImagesPull(t *testing.T) {
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
}, },
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/docker", nil }, LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
} }
containerRuntime, err := utilruntime.NewContainerRuntime(&fexec, constants.DefaultDockerCRISocket) containerRuntime, err := utilruntime.NewContainerRuntime(&fexec, constants.DefaultCRISocket)
if err != nil { if err != nil {
t.Errorf("unexpected NewContainerRuntime error: %v", err) t.Errorf("unexpected NewContainerRuntime error: %v", err)
} }

View File

@ -29,7 +29,6 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun" dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
) )
@ -49,17 +48,10 @@ var (
Additionally, a control plane component may have crashed or exited when started by the container runtime. Additionally, a control plane component may have crashed or exited when started by the container runtime.
To troubleshoot, list all containers using your preferred container runtimes CLI. To troubleshoot, list all containers using your preferred container runtimes CLI.
{{ if .IsDocker }} Here is one example how you may list all running Kubernetes containers by using crictl:
Here is one example how you may list all Kubernetes containers running in docker:
- 'docker ps -a | grep kube | grep -v pause'
Once you have found the failing container, you can inspect its logs with:
- 'docker logs CONTAINERID'
{{ else }}
Here is one example how you may list all Kubernetes containers running in cri-o/containerd using crictl:
- 'crictl --runtime-endpoint {{ .Socket }} ps -a | grep kube | grep -v pause' - 'crictl --runtime-endpoint {{ .Socket }} ps -a | grep kube | grep -v pause'
Once you have found the failing container, you can inspect its logs with: Once you have found the failing container, you can inspect its logs with:
- 'crictl --runtime-endpoint {{ .Socket }} logs CONTAINERID' - 'crictl --runtime-endpoint {{ .Socket }} logs CONTAINERID'
{{ end }}
`))) `)))
) )
@ -107,11 +99,9 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
context := struct { context := struct {
Error string Error string
Socket string Socket string
IsDocker bool
}{ }{
Error: fmt.Sprintf("%v", err), Error: fmt.Sprintf("%v", err),
Socket: data.Cfg().NodeRegistration.CRISocket, Socket: data.Cfg().NodeRegistration.CRISocket,
IsDocker: data.Cfg().NodeRegistration.CRISocket == kubeadmconstants.DefaultDockerCRISocket,
} }
kubeletFailTempl.Execute(data.OutputWriter(), context) kubeletFailTempl.Execute(data.OutputWriter(), context)

View File

@ -96,6 +96,8 @@ func runCleanupNode(c workflow.RunData) error {
fmt.Println("[reset] Would remove Kubernetes-managed containers") fmt.Println("[reset] Would remove Kubernetes-managed containers")
} }
// TODO: remove the dockershim directory cleanup in 1.25
// https://github.com/kubernetes/kubeadm/issues/2626
r.AddDirsToClean("/var/lib/dockershim", "/var/run/kubernetes", "/var/lib/cni") r.AddDirsToClean("/var/lib/dockershim", "/var/run/kubernetes", "/var/lib/cni")
// Remove contents from the config and pki directories // Remove contents from the config and pki directories

View File

@ -20,6 +20,13 @@ limitations under the License.
package constants package constants
const ( const (
// DefaultDockerCRISocket defines the default Docker CRI socket // CRISocketContainerd is the containerd CRI endpoint
DefaultDockerCRISocket = "unix:///var/run/dockershim.sock" CRISocketContainerd = "unix:///var/run/containerd/containerd.sock"
// CRISocketCRIO is the cri-o CRI endpoint
CRISocketCRIO = "unix:///var/run/crio/crio.sock"
// CRISocketDocker is the cri-dockerd CRI endpoint
CRISocketDocker = "unix:///var/run/cri-dockerd.sock"
// DefaultCRISocket defines the default CRI socket
DefaultCRISocket = CRISocketContainerd
) )

View File

@ -20,6 +20,14 @@ limitations under the License.
package constants package constants
const ( const (
// DefaultDockerCRISocket defines the default Docker CRI socket // CRISocketContainerd is the containerd CRI endpoint
DefaultDockerCRISocket = "npipe:////./pipe/docker_engine" CRISocketContainerd = "npipe:////./pipe/containerd-containerd"
// CRISocketCRIO is the cri-o CRI endpoint
// NOTE: this is a placeholder as CRI-O does not support Windows
CRISocketCRIO = "npipe:////./pipe/cri-o"
// CRISocketDocker is the cri-dockerd CRI endpoint
CRISocketDocker = "npipe:////./pipe/cri-dockerd"
// DefaultCRISocket defines the default CRI socket
DefaultCRISocket = CRISocketContainerd
) )

View File

@ -21,6 +21,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -102,7 +103,13 @@ func buildKubeletArgMapCommon(opts kubeletFlagsOpts) map[string]string {
// Once that happens only the "remote" branch option should be left. // Once that happens only the "remote" branch option should be left.
// TODO: https://github.com/kubernetes/kubeadm/issues/2626 // TODO: https://github.com/kubernetes/kubeadm/issues/2626
hasDockershim := opts.kubeletVersion.Major() == 1 && opts.kubeletVersion.Minor() < 24 hasDockershim := opts.kubeletVersion.Major() == 1 && opts.kubeletVersion.Minor() < 24
if opts.nodeRegOpts.CRISocket == constants.DefaultDockerCRISocket && hasDockershim { var dockerSocket string
if runtime.GOOS == "windows" {
dockerSocket = "npipe:////./pipe/dockershim"
} else {
dockerSocket = "unix:///var/run/dockershim.sock"
}
if opts.nodeRegOpts.CRISocket == dockerSocket && hasDockershim {
kubeletFlags["network-plugin"] = "cni" kubeletFlags["network-plugin"] = "cni"
} else { } else {
kubeletFlags["container-runtime"] = "remote" kubeletFlags["container-runtime"] = "remote"

View File

@ -52,7 +52,7 @@ func TestAnnotateCRISocket(t *testing.T) {
}, },
{ {
name: "CRI-socket annotation needs to be updated", name: "CRI-socket annotation needs to be updated",
currentCRISocketAnnotation: "unix:///var/run/dockershim.sock", currentCRISocketAnnotation: "unix:///foo/bar",
newCRISocketAnnotation: "unix:///run/containerd/containerd.sock", newCRISocketAnnotation: "unix:///run/containerd/containerd.sock",
expectedPatch: `{"metadata":{"annotations":{"kubeadm.alpha.kubernetes.io/cri-socket":"unix:///run/containerd/containerd.sock"}}}`, expectedPatch: `{"metadata":{"annotations":{"kubeadm.alpha.kubernetes.io/cri-socket":"unix:///run/containerd/containerd.sock"}}}`,
}, },

View File

@ -507,9 +507,7 @@ func (subnet HTTPProxyCIDRCheck) Check() (warnings, errorList []error) {
} }
// SystemVerificationCheck defines struct used for running the system verification node check in test/e2e_node/system // SystemVerificationCheck defines struct used for running the system verification node check in test/e2e_node/system
type SystemVerificationCheck struct { type SystemVerificationCheck struct{}
IsDocker bool
}
// Name will return SystemVerification as name for SystemVerificationCheck // Name will return SystemVerification as name for SystemVerificationCheck
func (SystemVerificationCheck) Name() string { func (SystemVerificationCheck) Name() string {
@ -530,11 +528,6 @@ func (sysver SystemVerificationCheck) Check() (warnings, errorList []error) {
var validators = []system.Validator{ var validators = []system.Validator{
&system.KernelValidator{Reporter: reporter}} &system.KernelValidator{Reporter: reporter}}
// run the docker validator only with docker runtime
if sysver.IsDocker {
validators = append(validators, &system.DockerValidator{Reporter: reporter})
}
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
//add linux validators //add linux validators
validators = append(validators, validators = append(validators,
@ -1025,26 +1018,19 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.JoinConfigura
// kubeadm init and join commands // kubeadm init and join commands
func addCommonChecks(execer utilsexec.Interface, k8sVersion string, nodeReg *kubeadmapi.NodeRegistrationOptions, checks []Checker) []Checker { func addCommonChecks(execer utilsexec.Interface, k8sVersion string, nodeReg *kubeadmapi.NodeRegistrationOptions, checks []Checker) []Checker {
containerRuntime, err := utilruntime.NewContainerRuntime(execer, nodeReg.CRISocket) containerRuntime, err := utilruntime.NewContainerRuntime(execer, nodeReg.CRISocket)
isDocker := false
if err != nil { if err != nil {
klog.Warningf("[preflight] WARNING: Couldn't create the interface used for talking to the container runtime: %v\n", err) klog.Warningf("[preflight] WARNING: Couldn't create the interface used for talking to the container runtime: %v\n", err)
} else { } else {
checks = append(checks, ContainerRuntimeCheck{runtime: containerRuntime}) checks = append(checks, ContainerRuntimeCheck{runtime: containerRuntime})
if containerRuntime.IsDocker() {
isDocker = true
checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true})
}
} }
// non-windows checks // non-windows checks
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
if !isDocker {
checks = append(checks, InPathCheck{executable: "crictl", mandatory: true, exec: execer})
}
checks = append(checks, checks = append(checks,
FileContentCheck{Path: bridgenf, Content: []byte{'1'}}, FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
FileContentCheck{Path: ipv4Forward, Content: []byte{'1'}}, FileContentCheck{Path: ipv4Forward, Content: []byte{'1'}},
SwapCheck{}, SwapCheck{},
InPathCheck{executable: "crictl", mandatory: true, exec: execer},
InPathCheck{executable: "conntrack", mandatory: true, exec: execer}, InPathCheck{executable: "conntrack", mandatory: true, exec: execer},
InPathCheck{executable: "ip", mandatory: true, exec: execer}, InPathCheck{executable: "ip", mandatory: true, exec: execer},
InPathCheck{executable: "iptables", mandatory: true, exec: execer}, InPathCheck{executable: "iptables", mandatory: true, exec: execer},
@ -1057,7 +1043,7 @@ func addCommonChecks(execer utilsexec.Interface, k8sVersion string, nodeReg *kub
InPathCheck{executable: "touch", mandatory: false, exec: execer}) InPathCheck{executable: "touch", mandatory: false, exec: execer})
} }
checks = append(checks, checks = append(checks,
SystemVerificationCheck{IsDocker: isDocker}, SystemVerificationCheck{},
HostnameCheck{nodeName: nodeReg.Name}, HostnameCheck{nodeName: nodeReg.Name},
KubeletVersionCheck{KubernetesVersion: k8sVersion, exec: execer}, KubeletVersionCheck{KubernetesVersion: k8sVersion, exec: execer},
ServiceCheck{Service: "kubelet", CheckIfActive: false}, ServiceCheck{Service: "kubelet", CheckIfActive: false},

View File

@ -917,10 +917,10 @@ func TestImagePullCheck(t *testing.T) {
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
}, },
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/docker", nil }, LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
} }
containerRuntime, err := utilruntime.NewContainerRuntime(&fexec, constants.DefaultDockerCRISocket) containerRuntime, err := utilruntime.NewContainerRuntime(&fexec, constants.DefaultCRISocket)
if err != nil { if err != nil {
t.Errorf("unexpected NewContainerRuntime error: %v", err) t.Errorf("unexpected NewContainerRuntime error: %v", err)
} }

View File

@ -195,7 +195,7 @@ func DefaultedStaticInitConfiguration() (*kubeadmapi.InitConfiguration, error) {
LocalAPIEndpoint: kubeadmapiv1.APIEndpoint{AdvertiseAddress: "1.2.3.4"}, LocalAPIEndpoint: kubeadmapiv1.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
BootstrapTokens: []bootstraptokenv1.BootstrapToken{PlaceholderToken}, BootstrapTokens: []bootstraptokenv1.BootstrapToken{PlaceholderToken},
NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{ NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{
CRISocket: kubeadmconstants.DefaultDockerCRISocket, // avoid CRI detection CRISocket: kubeadmconstants.DefaultCRISocket, // avoid CRI detection
Name: "node", Name: "node",
}, },
} }

View File

@ -27,9 +27,16 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
) )
// defaultKnownCRISockets holds the set of known CRI endpoints
var defaultKnownCRISockets = []string{
constants.CRISocketContainerd,
constants.CRISocketCRIO,
constants.CRISocketDocker,
}
// ContainerRuntime is an interface for working with container runtimes // ContainerRuntime is an interface for working with container runtimes
type ContainerRuntime interface { type ContainerRuntime interface {
IsDocker() bool Socket() string
IsRunning() error IsRunning() error
ListKubeContainers() ([]string, error) ListKubeContainers() ([]string, error)
RemoveContainers(containers []string) error RemoveContainers(containers []string) error
@ -43,39 +50,19 @@ type CRIRuntime struct {
criSocket string criSocket string
} }
// DockerRuntime is a struct that interfaces with the Docker daemon
type DockerRuntime struct {
exec utilsexec.Interface
}
// NewContainerRuntime sets up and returns a ContainerRuntime struct // NewContainerRuntime sets up and returns a ContainerRuntime struct
func NewContainerRuntime(execer utilsexec.Interface, criSocket string) (ContainerRuntime, error) { func NewContainerRuntime(execer utilsexec.Interface, criSocket string) (ContainerRuntime, error) {
var toolName string toolName := "crictl"
var runtime ContainerRuntime runtime := &CRIRuntime{execer, criSocket}
if criSocket != constants.DefaultDockerCRISocket {
toolName = "crictl"
runtime = &CRIRuntime{execer, criSocket}
} else {
toolName = "docker"
runtime = &DockerRuntime{execer}
}
if _, err := execer.LookPath(toolName); err != nil { if _, err := execer.LookPath(toolName); err != nil {
return nil, errors.Wrapf(err, "%s is required for container runtime", toolName) return nil, errors.Wrapf(err, "%s is required by the container runtime", toolName)
} }
return runtime, nil return runtime, nil
} }
// IsDocker returns true if the runtime is docker // Socket returns the CRI socket endpoint
func (runtime *CRIRuntime) IsDocker() bool { func (runtime *CRIRuntime) Socket() string {
return false return runtime.criSocket
}
// IsDocker returns true if the runtime is docker
func (runtime *DockerRuntime) IsDocker() bool {
return true
} }
// IsRunning checks if runtime is running // IsRunning checks if runtime is running
@ -86,14 +73,6 @@ func (runtime *CRIRuntime) IsRunning() error {
return nil return nil
} }
// IsRunning checks if runtime is running
func (runtime *DockerRuntime) IsRunning() error {
if out, err := runtime.exec.Command("docker", "info").CombinedOutput(); err != nil {
return errors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
}
return nil
}
// ListKubeContainers lists running k8s CRI pods // ListKubeContainers lists running k8s CRI pods
func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) { func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) {
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pods", "-q").CombinedOutput() out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pods", "-q").CombinedOutput()
@ -105,12 +84,6 @@ func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) {
return pods, nil return pods, nil
} }
// ListKubeContainers lists running k8s containers
func (runtime *DockerRuntime) ListKubeContainers() ([]string, error) {
output, err := runtime.exec.Command("docker", "ps", "-a", "--filter", "name=k8s_", "-q").CombinedOutput()
return strings.Fields(string(output)), err
}
// RemoveContainers removes running k8s pods // RemoveContainers removes running k8s pods
func (runtime *CRIRuntime) RemoveContainers(containers []string) error { func (runtime *CRIRuntime) RemoveContainers(containers []string) error {
errs := []error{} errs := []error{}
@ -129,24 +102,6 @@ func (runtime *CRIRuntime) RemoveContainers(containers []string) error {
return errorsutil.NewAggregate(errs) return errorsutil.NewAggregate(errs)
} }
// RemoveContainers removes running containers
func (runtime *DockerRuntime) RemoveContainers(containers []string) error {
errs := []error{}
for _, container := range containers {
out, err := runtime.exec.Command("docker", "stop", container).CombinedOutput()
if err != nil {
// don't stop on errors, try to remove as many containers as possible
errs = append(errs, errors.Wrapf(err, "failed to stop running container %s: output: %s, error", container, string(out)))
} else {
out, err = runtime.exec.Command("docker", "rm", "--volumes", container).CombinedOutput()
if err != nil {
errs = append(errs, errors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
}
}
}
return errorsutil.NewAggregate(errs)
}
// PullImage pulls the image // PullImage pulls the image
func (runtime *CRIRuntime) PullImage(image string) error { func (runtime *CRIRuntime) PullImage(image string) error {
var err error var err error
@ -160,47 +115,15 @@ func (runtime *CRIRuntime) PullImage(image string) error {
return errors.Wrapf(err, "output: %s, error", out) return errors.Wrapf(err, "output: %s, error", out)
} }
// PullImage pulls the image
func (runtime *DockerRuntime) PullImage(image string) error {
var err error
var out []byte
for i := 0; i < constants.PullImageRetry; i++ {
out, err = runtime.exec.Command("docker", "pull", image).CombinedOutput()
if err == nil {
return nil
}
}
return errors.Wrapf(err, "output: %s, error", out)
}
// ImageExists checks to see if the image exists on the system // ImageExists checks to see if the image exists on the system
func (runtime *CRIRuntime) ImageExists(image string) (bool, error) { func (runtime *CRIRuntime) ImageExists(image string) (bool, error) {
err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "inspecti", image).Run() err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "inspecti", image).Run()
return err == nil, nil return err == nil, nil
} }
// ImageExists checks to see if the image exists on the system
func (runtime *DockerRuntime) ImageExists(image string) (bool, error) {
err := runtime.exec.Command("docker", "inspect", image).Run()
return err == nil, nil
}
// detectCRISocketImpl is separated out only for test purposes, DON'T call it directly, use DetectCRISocket instead // detectCRISocketImpl is separated out only for test purposes, DON'T call it directly, use DetectCRISocket instead
func detectCRISocketImpl(isSocket func(string) bool) (string, error) { func detectCRISocketImpl(isSocket func(string) bool, knownCRISockets []string) (string, error) {
foundCRISockets := []string{} foundCRISockets := []string{}
knownCRISockets := []string{
// Docker and containerd sockets are special cased below, hence not to be included here
"unix:///var/run/crio/crio.sock",
}
if isSocket(dockerSocket) {
// the path in dockerSocket is not CRI compatible, hence we should replace it with a CRI compatible socket
foundCRISockets = append(foundCRISockets, constants.DefaultDockerCRISocket)
} else if isSocket(containerdSocket) {
// Docker 18.09 gets bundled together with containerd, thus having both dockerSocket and containerdSocket present.
// For compatibility reasons, we use the containerd socket only if Docker is not detected.
foundCRISockets = append(foundCRISockets, containerdSocket)
}
for _, socket := range knownCRISockets { for _, socket := range knownCRISockets {
if isSocket(socket) { if isSocket(socket) {
@ -210,18 +133,20 @@ func detectCRISocketImpl(isSocket func(string) bool) (string, error) {
switch len(foundCRISockets) { switch len(foundCRISockets) {
case 0: case 0:
// Fall back to Docker if no CRI is detected, we can error out later on if we need it // Fall back to the default socket if no CRI is detected, we can error out later on if we need it
return constants.DefaultDockerCRISocket, nil return constants.DefaultCRISocket, nil
case 1: case 1:
// Precisely one CRI found, use that // Precisely one CRI found, use that
return foundCRISockets[0], nil return foundCRISockets[0], nil
default: default:
// Multiple CRIs installed? // Multiple CRIs installed?
return "", errors.Errorf("Found multiple CRI sockets, please use --cri-socket to select one: %s", strings.Join(foundCRISockets, ", ")) return "", errors.Errorf("Found multiple CRI endpoints on the host. Please define which one do you wish "+
"to use by setting the 'criSocket' field in the kubeadm configuration file: %s",
strings.Join(foundCRISockets, ", "))
} }
} }
// DetectCRISocket uses a list of known CRI sockets to detect one. If more than one or none is discovered, an error is returned. // DetectCRISocket uses a list of known CRI sockets to detect one. If more than one or none is discovered, an error is returned.
func DetectCRISocket() (string, error) { func DetectCRISocket() (string, error) {
return detectCRISocketImpl(isExistingSocket) return detectCRISocketImpl(isExistingSocket, defaultKnownCRISockets)
} }

View File

@ -42,30 +42,23 @@ func TestNewContainerRuntime(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
execer fakeexec.FakeExec execer fakeexec.FakeExec
criSocket string
isDocker bool
isError bool isError bool
}{ }{
{"valid: default cri socket", execLookPathOK, constants.DefaultDockerCRISocket, true, false}, {"valid: crictl present", execLookPathOK, false},
{"valid: cri-o socket url", execLookPathOK, "unix:///var/run/crio/crio.sock", false, false}, {"invalid: no crictl", execLookPathErr, true},
{"invalid: no crictl", execLookPathErr, "unix:///var/run/crio/crio.sock", false, true},
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
runtime, err := NewContainerRuntime(&tc.execer, tc.criSocket) _, err := NewContainerRuntime(&tc.execer, "unix:///some/socket.sock")
if err != nil { if err != nil {
if !tc.isError { if !tc.isError {
t.Fatalf("unexpected NewContainerRuntime error. criSocket: %s, error: %v", tc.criSocket, err) t.Fatalf("unexpected NewContainerRuntime error. error: %v", err)
} }
return // expected error occurs, impossible to test runtime further return // expected error occurs, impossible to test runtime further
} }
if tc.isError && err == nil { if tc.isError && err == nil {
t.Fatalf("unexpected NewContainerRuntime success. criSocket: %s", tc.criSocket) t.Fatal("unexpected NewContainerRuntime success")
}
isDocker := runtime.IsDocker()
if tc.isDocker != isDocker {
t.Fatalf("unexpected isDocker() result %v for the criSocket %s", isDocker, tc.criSocket)
} }
}) })
} }
@ -96,11 +89,6 @@ func TestIsRunning(t *testing.T) {
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil }, LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
} }
dockerExecer := fakeexec.FakeExec{
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/docker", nil },
}
cases := []struct { cases := []struct {
name string name string
criSocket string criSocket string
@ -109,8 +97,6 @@ func TestIsRunning(t *testing.T) {
}{ }{
{"valid: CRI-O is running", "unix:///var/run/crio/crio.sock", criExecer, false}, {"valid: CRI-O is running", "unix:///var/run/crio/crio.sock", criExecer, false},
{"invalid: CRI-O is not running", "unix:///var/run/crio/crio.sock", criExecer, true}, {"invalid: CRI-O is not running", "unix:///var/run/crio/crio.sock", criExecer, true},
{"valid: docker is running", constants.DefaultDockerCRISocket, dockerExecer, false},
{"invalid: docker is not running", constants.DefaultDockerCRISocket, dockerExecer, true},
} }
for _, tc := range cases { for _, tc := range cases {
@ -150,7 +136,6 @@ func TestListKubeContainers(t *testing.T) {
}{ }{
{"valid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", false}, {"valid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", false},
{"invalid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", true}, {"invalid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", true},
{"valid: list containers using docker", constants.DefaultDockerCRISocket, false},
} }
for _, tc := range cases { for _, tc := range cases {
@ -204,9 +189,6 @@ func TestRemoveContainers(t *testing.T) {
{"valid: remove containers using CRI", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, false}, // Test case 1 {"valid: remove containers using CRI", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, false}, // Test case 1
{"invalid: CRI rmp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true}, {"invalid: CRI rmp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true},
{"invalid: CRI stopp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true}, {"invalid: CRI stopp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true},
{"valid: remove containers using docker", constants.DefaultDockerCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, false},
{"invalid: docker rm failure", constants.DefaultDockerCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, true},
{"invalid: docker stop failure", constants.DefaultDockerCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, true},
} }
for _, tc := range cases { for _, tc := range cases {
@ -259,8 +241,6 @@ func TestPullImage(t *testing.T) {
}{ }{
{"valid: pull image using CRI", "unix:///var/run/crio/crio.sock", "image1", false}, {"valid: pull image using CRI", "unix:///var/run/crio/crio.sock", "image1", false},
{"invalid: CRI pull error", "unix:///var/run/crio/crio.sock", "image2", true}, {"invalid: CRI pull error", "unix:///var/run/crio/crio.sock", "image2", true},
{"valid: pull image using docker", constants.DefaultDockerCRISocket, "image1", false},
{"invalid: docker pull error", constants.DefaultDockerCRISocket, "image2", true},
} }
for _, tc := range cases { for _, tc := range cases {
@ -302,9 +282,7 @@ func TestImageExists(t *testing.T) {
result bool result bool
}{ }{
{"valid: test if image exists using CRI", "unix:///var/run/crio/crio.sock", "image1", false}, {"valid: test if image exists using CRI", "unix:///var/run/crio/crio.sock", "image1", false},
{"invalid: CRI inspecti failure", "unix:///var/run/crio/crio.sock", "image2", true}, {"invalid: CRI inspect failure", "unix:///var/run/crio/crio.sock", "image2", true},
{"valid: test if image exists using docker", constants.DefaultDockerCRISocket, "image1", false},
{"invalid: docker inspect failure", constants.DefaultDockerCRISocket, "image2", true},
} }
for _, tc := range cases { for _, tc := range cases {
@ -395,45 +373,22 @@ func TestDetectCRISocketImpl(t *testing.T) {
expectedSocket string expectedSocket string
}{ }{
{ {
name: "No existing sockets, use Docker", name: "No existing sockets, use default",
existingSockets: []string{}, existingSockets: []string{},
expectedError: false, expectedError: false,
expectedSocket: constants.DefaultDockerCRISocket, expectedSocket: constants.DefaultCRISocket,
}, },
{ {
name: "One valid CRI socket leads to success", name: "One valid CRI socket leads to success",
existingSockets: []string{"unix:///var/run/crio/crio.sock"}, existingSockets: []string{"unix:///foo/bar.sock"},
expectedError: false, expectedError: false,
expectedSocket: "unix:///var/run/crio/crio.sock", expectedSocket: "unix:///foo/bar.sock",
}, },
{ {
name: "Correct Docker CRI socket is returned", name: "Multiple CRI sockets lead to an error",
existingSockets: []string{"unix:///var/run/docker.sock"},
expectedError: false,
expectedSocket: constants.DefaultDockerCRISocket,
},
{
name: "CRI and Docker sockets lead to an error",
existingSockets: []string{ existingSockets: []string{
"unix:///var/run/docker.sock", "unix:///foo/bar.sock",
"unix:///var/run/crio/crio.sock", "unix:///foo/baz.sock",
},
expectedError: true,
},
{
name: "Docker and containerd lead to Docker being used",
existingSockets: []string{
"unix:///var/run/docker.sock",
"unix:///run/containerd/containerd.sock",
},
expectedError: false,
expectedSocket: constants.DefaultDockerCRISocket,
},
{
name: "A couple of CRI sockets lead to an error",
existingSockets: []string{
"unix:///var/run/crio/crio.sock",
"unix:///run/containerd/containerd.sock",
}, },
expectedError: true, expectedError: true,
}, },
@ -447,9 +402,9 @@ func TestDetectCRISocketImpl(t *testing.T) {
return true return true
} }
} }
return false return false
}) }, test.existingSockets)
if (err != nil) != test.expectedError { if (err != nil) != test.expectedError {
t.Fatalf("detectCRISocketImpl returned unexpected result\n\tExpected error: %t\n\tGot error: %t", test.expectedError, err != nil) t.Fatalf("detectCRISocketImpl returned unexpected result\n\tExpected error: %t\n\tGot error: %t", test.expectedError, err != nil)
} }

View File

@ -24,11 +24,6 @@ import (
"net/url" "net/url"
) )
const (
dockerSocket = "unix:///var/run/docker.sock" // The Docker socket is not CRI compatible
containerdSocket = "unix:///run/containerd/containerd.sock"
)
// isExistingSocket checks if path exists and is domain socket // isExistingSocket checks if path exists and is domain socket
func isExistingSocket(path string) bool { func isExistingSocket(path string) bool {
u, err := url.Parse(path) u, err := url.Parse(path)

View File

@ -25,11 +25,6 @@ import (
winio "github.com/Microsoft/go-winio" winio "github.com/Microsoft/go-winio"
) )
const (
dockerSocket = "npipe:////./pipe/docker_engine" // The Docker socket is not CRI compatible
containerdSocket = "npipe:////./pipe/containerd-containerd" // Proposed containerd named pipe for Windows
)
// isExistingSocket checks if path exists and is domain socket // isExistingSocket checks if path exists and is domain socket
func isExistingSocket(path string) bool { func isExistingSocket(path string) bool {
u, err := url.Parse(path) u, err := url.Parse(path)