kubeadm: Detect CRIs automatically
In order to allow for a smoother UX with CRIs different than Docker, we have to make the --cri-socket command line flag optional when just one CRI is installed. This change does that by doing the following: - Introduce a new runtime function (DetectCRISocket) that will attempt to detect a CRI socket, or return an appropriate error. - Default to using the above function if --cri-socket is not specified and CRISocket in NodeRegistrationOptions is empty. - Stop static defaulting to DefaultCRISocket. And rename it to DefaultDockerCRISocket. Its use is now narrowed to "Docker or not" distinguishment and tests. - Introduce AddCRISocketFlag function that adds --cri-socket flag to a flagSet. Use that in all commands, that support --cri-socket. - Remove the deprecated --cri-socket-path flag from kubeadm config images pull and deprecate --cri-socket in kubeadm upgrade apply. Signed-off-by: Rostislav M. Georgiev <rostislavg@vmware.com>
This commit is contained in:
@@ -17,12 +17,16 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
@@ -41,7 +45,7 @@ func TestNewContainerRuntime(t *testing.T) {
|
||||
isDocker bool
|
||||
isError bool
|
||||
}{
|
||||
{"valid: default cri socket", execLookPathOK, kubeadmapiv1beta1.DefaultCRISocket, true, false},
|
||||
{"valid: default cri socket", execLookPathOK, constants.DefaultDockerCRISocket, true, false},
|
||||
{"valid: cri-o socket url", execLookPathOK, "unix:///var/run/crio/crio.sock", false, false},
|
||||
{"valid: cri-o socket path", execLookPathOK, "/var/run/crio/crio.sock", false, false},
|
||||
{"invalid: no crictl", execLookPathErr, "unix:///var/run/crio/crio.sock", false, true},
|
||||
@@ -105,8 +109,8 @@ func TestIsRunning(t *testing.T) {
|
||||
}{
|
||||
{"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},
|
||||
{"valid: docker is running", kubeadmapiv1beta1.DefaultCRISocket, dockerExecer, false},
|
||||
{"invalid: docker is not running", kubeadmapiv1beta1.DefaultCRISocket, dockerExecer, true},
|
||||
{"valid: docker is running", constants.DefaultDockerCRISocket, dockerExecer, false},
|
||||
{"invalid: docker is not running", constants.DefaultDockerCRISocket, dockerExecer, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -146,7 +150,7 @@ func TestListKubeContainers(t *testing.T) {
|
||||
}{
|
||||
{"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},
|
||||
{"valid: list containers using docker", kubeadmapiv1beta1.DefaultCRISocket, false},
|
||||
{"valid: list containers using docker", constants.DefaultDockerCRISocket, false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -199,8 +203,8 @@ 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
|
||||
{"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},
|
||||
{"valid: remove containers using docker", kubeadmapiv1beta1.DefaultCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, false},
|
||||
{"invalid: remove containers using docker", kubeadmapiv1beta1.DefaultCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, true},
|
||||
{"valid: remove containers using docker", constants.DefaultDockerCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, false},
|
||||
{"invalid: remove containers using docker", constants.DefaultDockerCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -243,8 +247,8 @@ func TestPullImage(t *testing.T) {
|
||||
}{
|
||||
{"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},
|
||||
{"valid: pull image using docker", kubeadmapiv1beta1.DefaultCRISocket, "image1", false},
|
||||
{"invalide: docer pull error", kubeadmapiv1beta1.DefaultCRISocket, "image2", true},
|
||||
{"valid: pull image using docker", constants.DefaultDockerCRISocket, "image1", false},
|
||||
{"invalide: docer pull error", constants.DefaultDockerCRISocket, "image2", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -287,8 +291,8 @@ func TestImageExists(t *testing.T) {
|
||||
}{
|
||||
{"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},
|
||||
{"valid: test if image exists using docker", kubeadmapiv1beta1.DefaultCRISocket, "image1", false},
|
||||
{"invalid: docker inspect failure", kubeadmapiv1beta1.DefaultCRISocket, "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 {
|
||||
@@ -305,3 +309,142 @@ func TestImageExists(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsExistingSocket(t *testing.T) {
|
||||
// this test is not expected to work on Windows
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
|
||||
const tempPrefix = "test.kubeadm.runtime.isExistingSocket."
|
||||
tests := []struct {
|
||||
name string
|
||||
proc func(*testing.T)
|
||||
}{
|
||||
{
|
||||
name: "Valid domain socket is detected as such",
|
||||
proc: func(t *testing.T) {
|
||||
tmpFile, err := ioutil.TempFile("", tempPrefix)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error by TempFile: %v", err)
|
||||
}
|
||||
theSocket := tmpFile.Name()
|
||||
os.Remove(theSocket)
|
||||
tmpFile.Close()
|
||||
|
||||
con, err := net.Listen("unix", theSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error while dialing a socket: %v", err)
|
||||
}
|
||||
defer con.Close()
|
||||
|
||||
if !isExistingSocket(theSocket) {
|
||||
t.Fatalf("isExistingSocket(%q) gave unexpected result. Should have been true, instead of false", theSocket)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Regular file is not a domain socket",
|
||||
proc: func(t *testing.T) {
|
||||
tmpFile, err := ioutil.TempFile("", tempPrefix)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error by TempFile: %v", err)
|
||||
}
|
||||
theSocket := tmpFile.Name()
|
||||
defer os.Remove(theSocket)
|
||||
tmpFile.Close()
|
||||
|
||||
if isExistingSocket(theSocket) {
|
||||
t.Fatalf("isExistingSocket(%q) gave unexpected result. Should have been false, instead of true", theSocket)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Non existent socket is not a domain socket",
|
||||
proc: func(t *testing.T) {
|
||||
const theSocket = "/non/existent/socket"
|
||||
if isExistingSocket(theSocket) {
|
||||
t.Fatalf("isExistingSocket(%q) gave unexpected result. Should have been false, instead of true", theSocket)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, test.proc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectCRISocketImpl(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existingSockets []string
|
||||
expectedError bool
|
||||
expectedSocket string
|
||||
}{
|
||||
{
|
||||
name: "No existing sockets, use Docker",
|
||||
existingSockets: []string{},
|
||||
expectedError: false,
|
||||
expectedSocket: constants.DefaultDockerCRISocket,
|
||||
},
|
||||
{
|
||||
name: "One valid CRI socket leads to success",
|
||||
existingSockets: []string{"/var/run/crio/crio.sock"},
|
||||
expectedError: false,
|
||||
expectedSocket: "/var/run/crio/crio.sock",
|
||||
},
|
||||
{
|
||||
name: "Correct Docker CRI socket is returned",
|
||||
existingSockets: []string{"/var/run/docker.sock"},
|
||||
expectedError: false,
|
||||
expectedSocket: constants.DefaultDockerCRISocket,
|
||||
},
|
||||
{
|
||||
name: "CRI and Docker sockets lead to an error",
|
||||
existingSockets: []string{
|
||||
"/var/run/docker.sock",
|
||||
"/var/run/crio/crio.sock",
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "Docker and containerd lead to Docker being used",
|
||||
existingSockets: []string{
|
||||
"/var/run/docker.sock",
|
||||
"/run/containerd/containerd.sock",
|
||||
},
|
||||
expectedError: false,
|
||||
expectedSocket: constants.DefaultDockerCRISocket,
|
||||
},
|
||||
{
|
||||
name: "A couple of CRI sockets lead to an error",
|
||||
existingSockets: []string{
|
||||
"/var/run/crio/crio.sock",
|
||||
"/run/containerd/containerd.sock",
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
socket, err := detectCRISocketImpl(func(path string) bool {
|
||||
for _, existing := range test.existingSockets {
|
||||
if path == existing {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
if (err != nil) != test.expectedError {
|
||||
t.Fatalf("detectCRISocketImpl returned unexpected result\n\tExpected error: %t\n\tGot error: %t", test.expectedError, err != nil)
|
||||
}
|
||||
if !test.expectedError && socket != test.expectedSocket {
|
||||
t.Fatalf("detectCRISocketImpl returned unexpected CRI socket\n\tExpected socket: %s\n\tReturned socket: %s",
|
||||
test.expectedSocket, socket)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user