From a7b07c01df197d40c677709d953ad0d538d49b75 Mon Sep 17 00:00:00 2001 From: Ken Robertson Date: Thu, 14 Jan 2016 15:16:07 -0800 Subject: [PATCH] Update container runtime to properly report the engine and version This address a TODO when collecting the node version information so it will properly report the configured runtime and its version. Previously, this was hardcoded to "docker://" and the docker version, and would show "docker://1.9.1" even when the kubelet was configured to use rkt. With this change, it will use the runtime's Type() and Version() data. This also changes the container.Runtime interface to add an APIVersion() method. This can be used when the runtime has separate versions for the engine and the API, such as with Docker. The Docker minimum version validation has been updated to use APIVersion(), and DockerManager.Version() now returns the engine version. --- pkg/kubelet/container/fake_runtime.go | 9 +++++++++ pkg/kubelet/container/runtime.go | 3 +++ pkg/kubelet/container/runtime_mock.go | 5 +++++ pkg/kubelet/dockertools/docker_test.go | 11 ++++++++++- pkg/kubelet/dockertools/manager.go | 21 ++++++++++++++++++--- pkg/kubelet/kubelet.go | 19 ++++++++----------- pkg/kubelet/kubelet_test.go | 20 ++++++++++---------- pkg/kubelet/rkt/rkt.go | 4 ++++ 8 files changed, 67 insertions(+), 25 deletions(-) diff --git a/pkg/kubelet/container/fake_runtime.go b/pkg/kubelet/container/fake_runtime.go index ac10dbebd79..a684b3ad293 100644 --- a/pkg/kubelet/container/fake_runtime.go +++ b/pkg/kubelet/container/fake_runtime.go @@ -43,6 +43,7 @@ type FakeRuntime struct { StartedContainers []string KilledContainers []string VersionInfo string + APIVersionInfo string RuntimeType string Err error InspectErr error @@ -154,6 +155,14 @@ func (f *FakeRuntime) Version() (Version, error) { return &FakeVersion{Version: f.VersionInfo}, f.Err } +func (f *FakeRuntime) APIVersion() (Version, error) { + f.Lock() + defer f.Unlock() + + f.CalledFunctions = append(f.CalledFunctions, "APIVersion") + return &FakeVersion{Version: f.APIVersionInfo}, f.Err +} + func (f *FakeRuntime) GetPods(all bool) ([]*Pod, error) { f.Lock() defer f.Unlock() diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index ec215654d19..4ae4c0dfb3a 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -82,6 +82,9 @@ type Runtime interface { // Version returns the version information of the container runtime. Version() (Version, error) + // APIVersion returns the API version information of the container + // runtime. This may be different from the runtime engine's version. + APIVersion() (Version, error) // GetPods returns a list containers group by pods. The boolean parameter // specifies whether the runtime returns all containers including those already // exited and dead containers (used for garbage collection). diff --git a/pkg/kubelet/container/runtime_mock.go b/pkg/kubelet/container/runtime_mock.go index 1cc9298b165..fd5ddad88d2 100644 --- a/pkg/kubelet/container/runtime_mock.go +++ b/pkg/kubelet/container/runtime_mock.go @@ -47,6 +47,11 @@ func (r *Mock) Version() (Version, error) { return args.Get(0).(Version), args.Error(1) } +func (r *Mock) APIVersion() (Version, error) { + args := r.Called() + return args.Get(0).(Version), args.Error(1) +} + func (r *Mock) GetPods(all bool) ([]*Pod, error) { args := r.Called(all) return args.Get(0).([]*Pod), args.Error(1) diff --git a/pkg/kubelet/dockertools/docker_test.go b/pkg/kubelet/dockertools/docker_test.go index 6249038d257..569e40ac436 100644 --- a/pkg/kubelet/dockertools/docker_test.go +++ b/pkg/kubelet/dockertools/docker_test.go @@ -160,7 +160,16 @@ func TestVersion(t *testing.T) { if err != nil { t.Errorf("got error while getting docker server version - %s", err) } - expectedVersion, _ := docker.NewAPIVersion("1.15") + expectedVersion, _ := docker.NewAPIVersion("1.1.3") + if e, a := expectedVersion.String(), version.String(); e != a { + t.Errorf("invalid docker server version. expected: %v, got: %v", e, a) + } + + version, err = manager.APIVersion() + if err != nil { + t.Errorf("got error while getting docker server version - %s", err) + } + expectedVersion, _ = docker.NewAPIVersion("1.15") if e, a := expectedVersion.String(), version.String(); e != a { t.Errorf("invalid docker server version. expected: %v, got: %v", e, a) } diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index 2f64966ed70..45c8af8682a 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -980,11 +980,26 @@ func (dm *DockerManager) Version() (kubecontainer.Version, error) { return nil, fmt.Errorf("docker: failed to get docker version: %v", err) } + engineVersion := env.Get("Version") + version, err := docker.NewAPIVersion(engineVersion) + if err != nil { + glog.Errorf("docker: failed to parse docker server version %q: %v", engineVersion, err) + return nil, fmt.Errorf("docker: failed to parse docker server version %q: %v", engineVersion, err) + } + return dockerVersion(version), nil +} + +func (dm *DockerManager) APIVersion() (kubecontainer.Version, error) { + env, err := dm.client.Version() + if err != nil { + return nil, fmt.Errorf("docker: failed to get docker version: %v", err) + } + apiVersion := env.Get("ApiVersion") version, err := docker.NewAPIVersion(apiVersion) if err != nil { - glog.Errorf("docker: failed to parse docker server version %q: %v", apiVersion, err) - return nil, fmt.Errorf("docker: failed to parse docker server version %q: %v", apiVersion, err) + glog.Errorf("docker: failed to parse docker api version %q: %v", apiVersion, err) + return nil, fmt.Errorf("docker: failed to parse docker api version %q: %v", apiVersion, err) } return dockerVersion(version), nil } @@ -993,7 +1008,7 @@ func (dm *DockerManager) Version() (kubecontainer.Version, error) { var dockerAPIVersionWithExec = "1.15" func (dm *DockerManager) nativeExecSupportExists() (bool, error) { - version, err := dm.Version() + version, err := dm.APIVersion() if err != nil { return false, err } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index d5bbf795b3d..dc8a659e8fd 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -2438,14 +2438,6 @@ func (kl *Kubelet) LatestLoopEntryTime() time.Time { return val.(time.Time) } -// Returns the container runtime version for this Kubelet. -func (kl *Kubelet) GetContainerRuntimeVersion() (kubecontainer.Version, error) { - if kl.containerRuntime == nil { - return nil, fmt.Errorf("no container runtime") - } - return kl.containerRuntime.Version() -} - func (kl *Kubelet) validatePodPhase(podStatus *api.PodStatus) error { switch podStatus.Phase { case api.PodRunning, api.PodSucceeded, api.PodFailed: @@ -2768,8 +2760,13 @@ func (kl *Kubelet) setNodeStatusVersionInfo(node *api.Node) { } else { node.Status.NodeInfo.KernelVersion = verinfo.KernelVersion node.Status.NodeInfo.OSImage = verinfo.ContainerOsVersion - // TODO: Determine the runtime is docker or rocket - node.Status.NodeInfo.ContainerRuntimeVersion = "docker://" + verinfo.DockerVersion + + runtimeVersion := "Unknown" + if runtimeVer, err := kl.containerRuntime.Version(); err == nil { + runtimeVersion = runtimeVer.String() + } + node.Status.NodeInfo.ContainerRuntimeVersion = fmt.Sprintf("%s://%s", kl.containerRuntime.Type(), runtimeVersion) + node.Status.NodeInfo.KubeletVersion = version.Get().String() // TODO: kube-proxy might be different version from kubelet in the future node.Status.NodeInfo.KubeProxyVersion = version.Get().String() @@ -2959,7 +2956,7 @@ func (kl *Kubelet) setNodeStatus(node *api.Node) error { func (kl *Kubelet) isContainerRuntimeVersionCompatible() error { switch kl.GetRuntime().Type() { case "docker": - version, err := kl.GetContainerRuntimeVersion() + version, err := kl.GetRuntime().APIVersion() if err != nil { return nil } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index fdcb6280b19..0831c6de6fb 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -2521,6 +2521,9 @@ func updateDiskSpacePolicy(kubelet *Kubelet, mockCadvisor *cadvisor.Mock, rootCa func TestUpdateNewNodeStatus(t *testing.T) { testKubelet := newTestKubelet(t) kubelet := testKubelet.kubelet + fakeRuntime := testKubelet.fakeRuntime + fakeRuntime.RuntimeType = "docker" + fakeRuntime.VersionInfo = "1.5.0" kubeClient := testKubelet.fakeKubeClient kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{ {ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}}, @@ -2538,7 +2541,6 @@ func TestUpdateNewNodeStatus(t *testing.T) { versionInfo := &cadvisorapi.VersionInfo{ KernelVersion: "3.16.0-0.bpo.4-amd64", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", - DockerVersion: "1.5.0", } mockCadvisor.On("VersionInfo").Return(versionInfo, nil) @@ -2662,7 +2664,6 @@ func TestUpdateNewNodeOutOfDiskStatusWithTransitionFrequency(t *testing.T) { versionInfo := &cadvisorapi.VersionInfo{ KernelVersion: "3.16.0-0.bpo.4-amd64", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", - DockerVersion: "1.5.0", } mockCadvisor.On("VersionInfo").Return(versionInfo, nil) @@ -2724,7 +2725,8 @@ func testDockerRuntimeVersion(t *testing.T) { kubelet := testKubelet.kubelet fakeRuntime := testKubelet.fakeRuntime fakeRuntime.RuntimeType = "docker" - fakeRuntime.VersionInfo = "1.18" + fakeRuntime.VersionInfo = "1.5.0" + fakeRuntime.APIVersionInfo = "1.18" kubeClient := testKubelet.fakeKubeClient kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{ {ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}}, @@ -2741,7 +2743,6 @@ func testDockerRuntimeVersion(t *testing.T) { versionInfo := &cadvisorapi.VersionInfo{ KernelVersion: "3.16.0-0.bpo.4-amd64", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", - DockerVersion: "1.5.0", } mockCadvisor.On("VersionInfo").Return(versionInfo, nil) @@ -2866,6 +2867,9 @@ func testDockerRuntimeVersion(t *testing.T) { func TestUpdateExistingNodeStatus(t *testing.T) { testKubelet := newTestKubelet(t) kubelet := testKubelet.kubelet + fakeRuntime := testKubelet.fakeRuntime + fakeRuntime.RuntimeType = "docker" + fakeRuntime.VersionInfo = "1.5.0" kubeClient := testKubelet.fakeKubeClient kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{ { @@ -2916,7 +2920,6 @@ func TestUpdateExistingNodeStatus(t *testing.T) { versionInfo := &cadvisorapi.VersionInfo{ KernelVersion: "3.16.0-0.bpo.4-amd64", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", - DockerVersion: "1.5.0", } mockCadvisor.On("VersionInfo").Return(versionInfo, nil) @@ -3179,10 +3182,8 @@ func TestUpdateNodeStatusWithoutContainerRuntime(t *testing.T) { kubelet := testKubelet.kubelet kubeClient := testKubelet.fakeKubeClient fakeRuntime := testKubelet.fakeRuntime - // This causes returning an error from GetContainerRuntimeVersion() which - // simulates that container runtime is down. - fakeRuntime.VersionInfo = "" - + fakeRuntime.RuntimeType = "docker" + fakeRuntime.VersionInfo = "1.5.0" kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{ {ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}}, }}).ReactionChain @@ -3199,7 +3200,6 @@ func TestUpdateNodeStatusWithoutContainerRuntime(t *testing.T) { versionInfo := &cadvisorapi.VersionInfo{ KernelVersion: "3.16.0-0.bpo.4-amd64", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", - DockerVersion: "1.5.0", } mockCadvisor.On("VersionInfo").Return(versionInfo, nil) diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index c2859f9373e..b9e149ce09f 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -971,6 +971,10 @@ func (r *Runtime) Version() (kubecontainer.Version, error) { return r.binVersion, nil } +func (r *Runtime) APIVersion() (kubecontainer.Version, error) { + return r.binVersion, nil +} + // SyncPod syncs the running pod to match the specified desired pod. func (r *Runtime) SyncPod(pod *api.Pod, podStatus api.PodStatus, internalPodStatus *kubecontainer.PodStatus, pullSecrets []api.Secret, backOff *util.Backoff) error { // TODO: (random-liu) Stop using running pod in SyncPod()