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:
Rostislav M. Georgiev
2018-09-19 15:08:29 +03:00
parent dc3edee5f5
commit f97770b175
29 changed files with 347 additions and 81 deletions

View File

@@ -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)
}
})
}
}