Merge pull request #20020 from apcera/cleanup-docker-engine-check

kubelet: Update engine version parsing to handle semantic versioning
This commit is contained in:
Eric Paris 2016-01-26 11:44:02 -05:00
commit 8e1ca68760
3 changed files with 95 additions and 25 deletions

View File

@ -275,7 +275,7 @@ func getDockerClient(dockerEndpoint string) (*docker.Client, error) {
func ConnectToDockerOrDie(dockerEndpoint string) DockerInterface { func ConnectToDockerOrDie(dockerEndpoint string) DockerInterface {
if dockerEndpoint == "fake://" { if dockerEndpoint == "fake://" {
return &FakeDockerClient{ return &FakeDockerClient{
VersionInfo: docker.Env{"ApiVersion=1.18"}, VersionInfo: docker.Env{"ApiVersion=1.18", "Version=1.6.0"},
} }
} }
client, err := getDockerClient(dockerEndpoint) client, err := getDockerClient(dockerEndpoint)

View File

@ -31,6 +31,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/coreos/go-semver/semver"
docker "github.com/fsouza/go-dockerclient" docker "github.com/fsouza/go-dockerclient"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/golang/groupcache/lru" "github.com/golang/groupcache/lru"
@ -945,18 +946,47 @@ func (dm *DockerManager) podInfraContainerChanged(pod *api.Pod, podInfraContaine
return podInfraContainerStatus.Hash != kubecontainer.HashContainer(expectedPodInfraContainer), nil return podInfraContainerStatus.Hash != kubecontainer.HashContainer(expectedPodInfraContainer), nil
} }
type dockerVersion docker.APIVersion // dockerVersion implementes kubecontainer.Version interface by implementing
// Compare() and String() (which is implemented by the underlying semver.Version)
func NewVersion(input string) (dockerVersion, error) { // TODO: this code is the same as rktVersion and may make sense to be moved to
version, err := docker.NewAPIVersion(input) // somewhere shared.
return dockerVersion(version), err type dockerVersion struct {
*semver.Version
} }
func (dv dockerVersion) String() string { func newDockerVersion(version string) (dockerVersion, error) {
sem, err := semver.NewVersion(version)
if err != nil {
return dockerVersion{}, err
}
return dockerVersion{sem}, nil
}
func (r dockerVersion) Compare(other string) (int, error) {
v, err := semver.NewVersion(other)
if err != nil {
return -1, err
}
if r.LessThan(*v) {
return -1, nil
}
if v.LessThan(*r.Version) {
return 1, nil
}
return 0, nil
}
// dockerVersion implementes kubecontainer.Version interface by implementing
// Compare() and String() on top og go-dockerclient's APIVersion. This version
// string doesn't conform to semantic versioning, as it is only "x.y"
type dockerAPIVersion docker.APIVersion
func (dv dockerAPIVersion) String() string {
return docker.APIVersion(dv).String() return docker.APIVersion(dv).String()
} }
func (dv dockerVersion) Compare(other string) (int, error) { func (dv dockerAPIVersion) Compare(other string) (int, error) {
a := docker.APIVersion(dv) a := docker.APIVersion(dv)
b, err := docker.NewAPIVersion(other) b, err := docker.NewAPIVersion(other)
if err != nil { if err != nil {
@ -982,12 +1012,12 @@ func (dm *DockerManager) Version() (kubecontainer.Version, error) {
} }
engineVersion := env.Get("Version") engineVersion := env.Get("Version")
version, err := docker.NewAPIVersion(engineVersion) version, err := newDockerVersion(engineVersion)
if err != nil { if err != nil {
glog.Errorf("docker: failed to parse docker server version %q: %v", engineVersion, err) 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 nil, fmt.Errorf("docker: failed to parse docker server version %q: %v", engineVersion, err)
} }
return dockerVersion(version), nil return version, nil
} }
func (dm *DockerManager) APIVersion() (kubecontainer.Version, error) { func (dm *DockerManager) APIVersion() (kubecontainer.Version, error) {
@ -1002,7 +1032,7 @@ func (dm *DockerManager) APIVersion() (kubecontainer.Version, error) {
glog.Errorf("docker: failed to parse docker api 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 nil, fmt.Errorf("docker: failed to parse docker api version %q: %v", apiVersion, err)
} }
return dockerVersion(version), nil return dockerAPIVersion(version), nil
} }
// The first version of docker that supports exec natively is 1.3.0 == API 1.15 // The first version of docker that supports exec natively is 1.3.0 == API 1.15

View File

@ -2719,26 +2719,59 @@ func TestUpdateNewNodeOutOfDiskStatusWithTransitionFrequency(t *testing.T) {
} }
} }
// FIXME: Enable me.. func TestDockerRuntimeVersion(t *testing.T) {
func testDockerRuntimeVersion(t *testing.T) {
testKubelet := newTestKubelet(t) testKubelet := newTestKubelet(t)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
fakeRuntime := testKubelet.fakeRuntime fakeRuntime := testKubelet.fakeRuntime
fakeRuntime.RuntimeType = "docker" fakeRuntime.RuntimeType = "docker"
fakeRuntime.VersionInfo = "1.5.0" fakeRuntime.VersionInfo = "1.10.0-rc1-fc24"
fakeRuntime.APIVersionInfo = "1.18" fakeRuntime.APIVersionInfo = "1.22"
kubeClient := testKubelet.fakeKubeClient kubeClient := testKubelet.fakeKubeClient
kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{ kubeClient.ReactionChain = testclient.NewSimpleFake(&api.NodeList{Items: []api.Node{
{ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}}, {
ObjectMeta: api.ObjectMeta{Name: testKubeletHostname},
Spec: api.NodeSpec{},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{
Type: api.NodeOutOfDisk,
Status: api.ConditionFalse,
Reason: "KubeletHasSufficientDisk",
Message: fmt.Sprintf("kubelet has sufficient disk space available"),
LastHeartbeatTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
LastTransitionTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: api.NodeReady,
Status: api.ConditionTrue,
Reason: "KubeletReady",
Message: fmt.Sprintf("kubelet is posting ready status"),
LastHeartbeatTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
LastTransitionTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
Capacity: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(3000, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(20E9, resource.BinarySI),
api.ResourcePods: *resource.NewQuantity(0, resource.DecimalSI),
},
Allocatable: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(2800, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(19900E6, resource.BinarySI),
api.ResourcePods: *resource.NewQuantity(0, resource.DecimalSI),
},
},
},
}}).ReactionChain }}).ReactionChain
mockCadvisor := testKubelet.fakeCadvisor
mockCadvisor.On("Start").Return(nil)
machineInfo := &cadvisorapi.MachineInfo{ machineInfo := &cadvisorapi.MachineInfo{
MachineID: "123", MachineID: "123",
SystemUUID: "abc", SystemUUID: "abc",
BootID: "1b3", BootID: "1b3",
NumCores: 2, NumCores: 2,
MemoryCapacity: 1024, MemoryCapacity: 20E9,
} }
mockCadvisor := testKubelet.fakeCadvisor
mockCadvisor.On("MachineInfo").Return(machineInfo, nil) mockCadvisor.On("MachineInfo").Return(machineInfo, nil)
versionInfo := &cadvisorapi.VersionInfo{ versionInfo := &cadvisorapi.VersionInfo{
KernelVersion: "3.16.0-0.bpo.4-amd64", KernelVersion: "3.16.0-0.bpo.4-amd64",
@ -2779,13 +2812,18 @@ func testDockerRuntimeVersion(t *testing.T) {
BootID: "1b3", BootID: "1b3",
KernelVersion: "3.16.0-0.bpo.4-amd64", KernelVersion: "3.16.0-0.bpo.4-amd64",
OSImage: "Debian GNU/Linux 7 (wheezy)", OSImage: "Debian GNU/Linux 7 (wheezy)",
ContainerRuntimeVersion: "docker://1.5.0", ContainerRuntimeVersion: "docker://1.10.0-rc1-fc24",
KubeletVersion: version.Get().String(), KubeletVersion: version.Get().String(),
KubeProxyVersion: version.Get().String(), KubeProxyVersion: version.Get().String(),
}, },
Capacity: api.ResourceList{ Capacity: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI), api.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI), api.ResourceMemory: *resource.NewQuantity(20E9, resource.BinarySI),
api.ResourcePods: *resource.NewQuantity(0, resource.DecimalSI),
},
Allocatable: api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(1800, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(19900E6, resource.BinarySI),
api.ResourcePods: *resource.NewQuantity(0, resource.DecimalSI), api.ResourcePods: *resource.NewQuantity(0, resource.DecimalSI),
}, },
Addresses: []api.NodeAddress{ Addresses: []api.NodeAddress{
@ -2805,6 +2843,7 @@ func testDockerRuntimeVersion(t *testing.T) {
}, },
} }
kubelet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, false, "", kubelet.isContainerRuntimeVersionCompatible)
kubelet.updateRuntimeUp() kubelet.updateRuntimeUp()
if err := kubelet.updateNodeStatus(); err != nil { if err := kubelet.updateNodeStatus(); err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
@ -2836,13 +2875,14 @@ func testDockerRuntimeVersion(t *testing.T) {
t.Errorf("unexpected node condition order. NodeReady should be last.") t.Errorf("unexpected node condition order. NodeReady should be last.")
} }
if !reflect.DeepEqual(expectedNode, updatedNode) { if !api.Semantic.DeepEqual(expectedNode, updatedNode) {
t.Errorf("unexpected objects: %s", util.ObjectDiff(expectedNode, updatedNode)) t.Errorf("expected \n%v\n, got \n%v", expectedNode, updatedNode)
} }
// Downgrade docker version, node should be NotReady // Downgrade docker version, node should be NotReady
fakeRuntime.RuntimeType = "docker" fakeRuntime.RuntimeType = "docker"
fakeRuntime.VersionInfo = "1.17" fakeRuntime.VersionInfo = "1.5.0"
fakeRuntime.APIVersionInfo = "1.17"
kubelet.updateRuntimeUp() kubelet.updateRuntimeUp()
if err := kubelet.updateNodeStatus(); err != nil { if err := kubelet.updateNodeStatus(); err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
@ -2858,8 +2898,8 @@ func testDockerRuntimeVersion(t *testing.T) {
if !ok { if !ok {
t.Errorf("unexpected object type") t.Errorf("unexpected object type")
} }
if updatedNode.Status.Conditions[0].Reason != "KubeletNotReady" && if updatedNode.Status.Conditions[1].Reason != "KubeletNotReady" &&
!strings.Contains(updatedNode.Status.Conditions[0].Message, "container runtime version is older than") { !strings.Contains(updatedNode.Status.Conditions[1].Message, "container runtime version is older than") {
t.Errorf("unexpect NodeStatus due to container runtime version") t.Errorf("unexpect NodeStatus due to container runtime version")
} }
} }