mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Merge pull request #22467 from Random-Liu/push-down-runtime-version-check
Auto commit by PR queue bot
This commit is contained in:
commit
fa0bf812d6
@ -57,7 +57,10 @@ type Runtime interface {
|
|||||||
Version() (Version, error)
|
Version() (Version, error)
|
||||||
// APIVersion returns the API version information of the container
|
// APIVersion returns the API version information of the container
|
||||||
// runtime. This may be different from the runtime engine's version.
|
// runtime. This may be different from the runtime engine's version.
|
||||||
|
// TODO(random-liu): We should fold this into Version()
|
||||||
APIVersion() (Version, error)
|
APIVersion() (Version, error)
|
||||||
|
// Status returns error if the runtime is unhealthy; nil otherwise.
|
||||||
|
Status() error
|
||||||
// GetPods returns a list containers group by pods. The boolean parameter
|
// GetPods returns a list containers group by pods. The boolean parameter
|
||||||
// specifies whether the runtime returns all containers including those already
|
// specifies whether the runtime returns all containers including those already
|
||||||
// exited and dead containers (used for garbage collection).
|
// exited and dead containers (used for garbage collection).
|
||||||
|
@ -48,6 +48,7 @@ type FakeRuntime struct {
|
|||||||
RuntimeType string
|
RuntimeType string
|
||||||
Err error
|
Err error
|
||||||
InspectErr error
|
InspectErr error
|
||||||
|
StatusErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeRuntime should implement Runtime.
|
// FakeRuntime should implement Runtime.
|
||||||
@ -108,6 +109,7 @@ func (f *FakeRuntime) ClearCalls() {
|
|||||||
f.RuntimeType = ""
|
f.RuntimeType = ""
|
||||||
f.Err = nil
|
f.Err = nil
|
||||||
f.InspectErr = nil
|
f.InspectErr = nil
|
||||||
|
f.StatusErr = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeRuntime) assertList(expect []string, test []string) error {
|
func (f *FakeRuntime) assertList(expect []string, test []string) error {
|
||||||
@ -168,6 +170,14 @@ func (f *FakeRuntime) APIVersion() (Version, error) {
|
|||||||
return &FakeVersion{Version: f.APIVersionInfo}, f.Err
|
return &FakeVersion{Version: f.APIVersionInfo}, f.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeRuntime) Status() error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
f.CalledFunctions = append(f.CalledFunctions, "Status")
|
||||||
|
return f.StatusErr
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeRuntime) GetPods(all bool) ([]*Pod, error) {
|
func (f *FakeRuntime) GetPods(all bool) ([]*Pod, error) {
|
||||||
f.Lock()
|
f.Lock()
|
||||||
defer f.Unlock()
|
defer f.Unlock()
|
||||||
|
@ -53,6 +53,11 @@ func (r *Mock) APIVersion() (Version, error) {
|
|||||||
return args.Get(0).(Version), args.Error(1)
|
return args.Get(0).(Version), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Mock) Status() error {
|
||||||
|
args := r.Called()
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Mock) GetPods(all bool) ([]*Pod, error) {
|
func (r *Mock) GetPods(all bool) ([]*Pod, error) {
|
||||||
args := r.Called(all)
|
args := r.Called(all)
|
||||||
return args.Get(0).([]*Pod), args.Error(1)
|
return args.Get(0).([]*Pod), args.Error(1)
|
||||||
|
@ -68,6 +68,26 @@ func NewFakeDockerClientWithVersion(version, apiVersion string) *FakeDockerClien
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeDockerClient) InjectError(fn string, err error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.Errors[fn] = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeDockerClient) InjectErrors(errs map[string]error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
for fn, err := range errs {
|
||||||
|
f.Errors[fn] = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeDockerClient) ClearErrors() {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
f.Errors = map[string]error{}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeDockerClient) ClearCalls() {
|
func (f *FakeDockerClient) ClearCalls() {
|
||||||
f.Lock()
|
f.Lock()
|
||||||
defer f.Unlock()
|
defer f.Unlock()
|
||||||
@ -382,7 +402,7 @@ func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeDockerClient) Version() (*docker.Env, error) {
|
func (f *FakeDockerClient) Version() (*docker.Env, error) {
|
||||||
return &f.VersionInfo, nil
|
return &f.VersionInfo, f.popError("version")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeDockerClient) Info() (*docker.Env, error) {
|
func (f *FakeDockerClient) Info() (*docker.Env, error) {
|
||||||
|
@ -60,7 +60,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
DockerType = "docker"
|
DockerType = "docker"
|
||||||
|
|
||||||
MinimumDockerAPIVersion = "1.18"
|
minimumDockerAPIVersion = "1.18"
|
||||||
|
|
||||||
// ndots specifies the minimum number of dots that a domain name must contain for the resolver to consider it as FQDN (fully-qualified)
|
// ndots specifies the minimum number of dots that a domain name must contain for the resolver to consider it as FQDN (fully-qualified)
|
||||||
// we want to able to consider SRV lookup names like _dns._udp.kube-dns.default.svc to be considered relative.
|
// we want to able to consider SRV lookup names like _dns._udp.kube-dns.default.svc to be considered relative.
|
||||||
@ -938,6 +938,30 @@ func (dm *DockerManager) APIVersion() (kubecontainer.Version, error) {
|
|||||||
return dockerAPIVersion(version), nil
|
return dockerAPIVersion(version), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status returns error if docker daemon is unhealthy, nil otherwise.
|
||||||
|
// Now we do this by checking whether:
|
||||||
|
// 1) `docker version` works
|
||||||
|
// 2) docker version is compatible with minimum requirement
|
||||||
|
func (dm *DockerManager) Status() error {
|
||||||
|
return dm.checkVersionCompatibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *DockerManager) checkVersionCompatibility() error {
|
||||||
|
version, err := dm.APIVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Verify the docker version.
|
||||||
|
result, err := version.Compare(minimumDockerAPIVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to compare current docker version %v with minimum support Docker version %q - %v", version, minimumDockerAPIVersion, err)
|
||||||
|
}
|
||||||
|
if result < 0 {
|
||||||
|
return fmt.Errorf("container runtime version is older than %s", minimumDockerAPIVersion)
|
||||||
|
}
|
||||||
|
return 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
|
||||||
var dockerAPIVersionWithExec = "1.15"
|
var dockerAPIVersionWithExec = "1.15"
|
||||||
|
|
||||||
|
@ -546,7 +546,7 @@ func TestKillContainerInPodWithError(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeDocker.SetFakeRunningContainers(containers)
|
fakeDocker.SetFakeRunningContainers(containers)
|
||||||
fakeDocker.Errors["stop"] = fmt.Errorf("sample error")
|
fakeDocker.InjectError("stop", fmt.Errorf("sample error"))
|
||||||
|
|
||||||
if err := manager.KillContainerInPod(kubecontainer.ContainerID{}, &pod.Spec.Containers[0], pod, "test kill container with error."); err == nil {
|
if err := manager.KillContainerInPod(kubecontainer.ContainerID{}, &pod.Spec.Containers[0], pod, "test kill container with error."); err == nil {
|
||||||
t.Errorf("expected error, found nil")
|
t.Errorf("expected error, found nil")
|
||||||
@ -1725,7 +1725,7 @@ func TestSyncPodWithFailure(t *testing.T) {
|
|||||||
ID: "9876",
|
ID: "9876",
|
||||||
Name: "/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0",
|
Name: "/k8s_POD." + strconv.FormatUint(generatePodInfraContainerHash(pod), 16) + "_foo_new_12345678_0",
|
||||||
}})
|
}})
|
||||||
fakeDocker.Errors = test.dockerError
|
fakeDocker.InjectErrors(test.dockerError)
|
||||||
puller.ErrorsToInject = test.pullerError
|
puller.ErrorsToInject = test.pullerError
|
||||||
pod.Spec.Containers = []api.Container{test.container}
|
pod.Spec.Containers = []api.Container{test.container}
|
||||||
result := runSyncPod(t, dm, fakeDocker, pod, nil, true)
|
result := runSyncPod(t, dm, fakeDocker, pod, nil, true)
|
||||||
@ -1846,3 +1846,44 @@ func TestSecurityOptsAreNilWithDockerV19(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.NotContains(t, newContainer.HostConfig.SecurityOpt, "seccomp:unconfined", "Pods with Docker versions < 1.10 must not have seccomp disabled by default")
|
assert.NotContains(t, newContainer.HostConfig.SecurityOpt, "seccomp:unconfined", "Pods with Docker versions < 1.10 must not have seccomp disabled by default")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckVersionCompatibility(t *testing.T) {
|
||||||
|
apiVersion, err := docker.NewAPIVersion(minimumDockerAPIVersion)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
type test struct {
|
||||||
|
version string
|
||||||
|
compatible bool
|
||||||
|
}
|
||||||
|
tests := []test{
|
||||||
|
// Minimum apiversion
|
||||||
|
{minimumDockerAPIVersion, true},
|
||||||
|
// Invalid apiversion
|
||||||
|
{"invalid_api_version", false},
|
||||||
|
}
|
||||||
|
for i := range apiVersion {
|
||||||
|
apiVersion[i]++
|
||||||
|
// Newer apiversion
|
||||||
|
tests = append(tests, test{apiVersion.String(), true})
|
||||||
|
apiVersion[i] -= 2
|
||||||
|
// Older apiversion
|
||||||
|
if apiVersion[i] >= 0 {
|
||||||
|
tests = append(tests, test{apiVersion.String(), false})
|
||||||
|
}
|
||||||
|
apiVersion[i]++
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
testCase := fmt.Sprintf("test case #%d test version %q", i, tt.version)
|
||||||
|
dm, fakeDocker := newTestDockerManagerWithHTTPClientWithVersion(&fakeHTTP{}, "", tt.version)
|
||||||
|
err := dm.checkVersionCompatibility()
|
||||||
|
assert.Equal(t, tt.compatible, err == nil, testCase)
|
||||||
|
if tt.compatible == true {
|
||||||
|
// Get docker version error
|
||||||
|
fakeDocker.InjectError("version", fmt.Errorf("injected version error"))
|
||||||
|
err := dm.checkVersionCompatibility()
|
||||||
|
assert.NotNil(t, err, testCase+" version error check")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -458,7 +458,7 @@ func NewMainKubelet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
klet.pleg = pleg.NewGenericPLEG(klet.containerRuntime, plegChannelCapacity, plegRelistPeriod, klet.podCache, util.RealClock{})
|
klet.pleg = pleg.NewGenericPLEG(klet.containerRuntime, plegChannelCapacity, plegRelistPeriod, klet.podCache, util.RealClock{})
|
||||||
klet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, configureCBR0, klet.isContainerRuntimeVersionCompatible)
|
klet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, configureCBR0)
|
||||||
klet.updatePodCIDR(podCIDR)
|
klet.updatePodCIDR(podCIDR)
|
||||||
|
|
||||||
// setup containerGC
|
// setup containerGC
|
||||||
@ -2692,7 +2692,7 @@ func (kl *Kubelet) GetPodByName(namespace, name string) (*api.Pod, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kl *Kubelet) updateRuntimeUp() {
|
func (kl *Kubelet) updateRuntimeUp() {
|
||||||
if _, err := kl.containerRuntime.Version(); err != nil {
|
if err := kl.containerRuntime.Status(); err != nil {
|
||||||
glog.Errorf("Container runtime sanity check failed: %v", err)
|
glog.Errorf("Container runtime sanity check failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -3115,26 +3115,6 @@ func SetNodeStatus(f func(*api.Node) error) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Why not combine this with container runtime health check?
|
|
||||||
func (kl *Kubelet) isContainerRuntimeVersionCompatible() error {
|
|
||||||
switch kl.GetRuntime().Type() {
|
|
||||||
case "docker":
|
|
||||||
version, err := kl.GetRuntime().APIVersion()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Verify the docker version.
|
|
||||||
result, err := version.Compare(dockertools.MinimumDockerAPIVersion)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to compare current docker version %v with minimum support Docker version %q - %v", version, dockertools.MinimumDockerAPIVersion, err)
|
|
||||||
}
|
|
||||||
if result < 0 {
|
|
||||||
return fmt.Errorf("container runtime version is older than %s", dockertools.MinimumDockerAPIVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryUpdateNodeStatus tries to update node status to master. If ReconcileCBR0
|
// tryUpdateNodeStatus tries to update node status to master. If ReconcileCBR0
|
||||||
// is set, this function will also confirm that cbr0 is configured correctly.
|
// is set, this function will also confirm that cbr0 is configured correctly.
|
||||||
func (kl *Kubelet) tryUpdateNodeStatus() error {
|
func (kl *Kubelet) tryUpdateNodeStatus() error {
|
||||||
|
@ -102,7 +102,8 @@ type TestKubelet struct {
|
|||||||
|
|
||||||
func newTestKubelet(t *testing.T) *TestKubelet {
|
func newTestKubelet(t *testing.T) *TestKubelet {
|
||||||
fakeRuntime := &containertest.FakeRuntime{}
|
fakeRuntime := &containertest.FakeRuntime{}
|
||||||
fakeRuntime.VersionInfo = "1.15"
|
fakeRuntime.RuntimeType = "test"
|
||||||
|
fakeRuntime.VersionInfo = "1.5.0"
|
||||||
fakeRuntime.ImageList = []kubecontainer.Image{
|
fakeRuntime.ImageList = []kubecontainer.Image{
|
||||||
{
|
{
|
||||||
ID: "abc",
|
ID: "abc",
|
||||||
@ -123,7 +124,7 @@ func newTestKubelet(t *testing.T) *TestKubelet {
|
|||||||
|
|
||||||
kubelet.hostname = testKubeletHostname
|
kubelet.hostname = testKubeletHostname
|
||||||
kubelet.nodeName = testKubeletHostname
|
kubelet.nodeName = testKubeletHostname
|
||||||
kubelet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, false, func() error { return nil })
|
kubelet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, false)
|
||||||
kubelet.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil))
|
kubelet.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil))
|
||||||
if tempDir, err := ioutil.TempDir("/tmp", "kubelet_test."); err != nil {
|
if tempDir, err := ioutil.TempDir("/tmp", "kubelet_test."); err != nil {
|
||||||
t.Fatalf("can't make a temp rootdir: %v", err)
|
t.Fatalf("can't make a temp rootdir: %v", err)
|
||||||
@ -2654,9 +2655,6 @@ func updateDiskSpacePolicy(kubelet *Kubelet, mockCadvisor *cadvisortest.Mock, ro
|
|||||||
func TestUpdateNewNodeStatus(t *testing.T) {
|
func TestUpdateNewNodeStatus(t *testing.T) {
|
||||||
testKubelet := newTestKubelet(t)
|
testKubelet := newTestKubelet(t)
|
||||||
kubelet := testKubelet.kubelet
|
kubelet := testKubelet.kubelet
|
||||||
fakeRuntime := testKubelet.fakeRuntime
|
|
||||||
fakeRuntime.RuntimeType = "docker"
|
|
||||||
fakeRuntime.VersionInfo = "1.5.0"
|
|
||||||
kubeClient := testKubelet.fakeKubeClient
|
kubeClient := testKubelet.fakeKubeClient
|
||||||
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
||||||
{ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}},
|
{ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}},
|
||||||
@ -2710,7 +2708,7 @@ func TestUpdateNewNodeStatus(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: "test://1.5.0",
|
||||||
KubeletVersion: version.Get().String(),
|
KubeletVersion: version.Get().String(),
|
||||||
KubeProxyVersion: version.Get().String(),
|
KubeProxyVersion: version.Get().String(),
|
||||||
},
|
},
|
||||||
@ -2852,197 +2850,9 @@ func TestUpdateNewNodeOutOfDiskStatusWithTransitionFrequency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerRuntimeVersion(t *testing.T) {
|
|
||||||
testKubelet := newTestKubelet(t)
|
|
||||||
kubelet := testKubelet.kubelet
|
|
||||||
fakeRuntime := testKubelet.fakeRuntime
|
|
||||||
fakeRuntime.RuntimeType = "docker"
|
|
||||||
fakeRuntime.VersionInfo = "1.10.0-rc1-fc24"
|
|
||||||
fakeRuntime.APIVersionInfo = "1.22"
|
|
||||||
kubeClient := testKubelet.fakeKubeClient
|
|
||||||
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
|
||||||
{
|
|
||||||
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
|
|
||||||
mockCadvisor := testKubelet.fakeCadvisor
|
|
||||||
mockCadvisor.On("Start").Return(nil)
|
|
||||||
machineInfo := &cadvisorapi.MachineInfo{
|
|
||||||
MachineID: "123",
|
|
||||||
SystemUUID: "abc",
|
|
||||||
BootID: "1b3",
|
|
||||||
NumCores: 2,
|
|
||||||
MemoryCapacity: 20E9,
|
|
||||||
}
|
|
||||||
mockCadvisor.On("MachineInfo").Return(machineInfo, nil)
|
|
||||||
versionInfo := &cadvisorapi.VersionInfo{
|
|
||||||
KernelVersion: "3.16.0-0.bpo.4-amd64",
|
|
||||||
ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)",
|
|
||||||
}
|
|
||||||
mockCadvisor.On("VersionInfo").Return(versionInfo, nil)
|
|
||||||
|
|
||||||
// Make kubelet report that it has sufficient disk space.
|
|
||||||
if err := updateDiskSpacePolicy(kubelet, mockCadvisor, 500, 500, 200, 200, 100, 100); err != nil {
|
|
||||||
t.Fatalf("can't update disk space manager: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedNode := &api.Node{
|
|
||||||
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.Time{},
|
|
||||||
LastTransitionTime: unversioned.Time{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: api.NodeReady,
|
|
||||||
Status: api.ConditionTrue,
|
|
||||||
Reason: "KubeletReady",
|
|
||||||
Message: fmt.Sprintf("kubelet is posting ready status"),
|
|
||||||
LastHeartbeatTime: unversioned.Time{},
|
|
||||||
LastTransitionTime: unversioned.Time{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NodeInfo: api.NodeSystemInfo{
|
|
||||||
MachineID: "123",
|
|
||||||
SystemUUID: "abc",
|
|
||||||
BootID: "1b3",
|
|
||||||
KernelVersion: "3.16.0-0.bpo.4-amd64",
|
|
||||||
OSImage: "Debian GNU/Linux 7 (wheezy)",
|
|
||||||
ContainerRuntimeVersion: "docker://1.10.0-rc1-fc24",
|
|
||||||
KubeletVersion: version.Get().String(),
|
|
||||||
KubeProxyVersion: version.Get().String(),
|
|
||||||
},
|
|
||||||
Capacity: api.ResourceList{
|
|
||||||
api.ResourceCPU: *resource.NewMilliQuantity(2000, resource.DecimalSI),
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
Addresses: []api.NodeAddress{
|
|
||||||
{Type: api.NodeLegacyHostIP, Address: "127.0.0.1"},
|
|
||||||
{Type: api.NodeInternalIP, Address: "127.0.0.1"},
|
|
||||||
},
|
|
||||||
Images: []api.ContainerImage{
|
|
||||||
{
|
|
||||||
Names: []string{"gcr.io/google_containers:v1", "gcr.io/google_containers:v2"},
|
|
||||||
SizeBytes: 123,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Names: []string{"gcr.io/google_containers:v3", "gcr.io/google_containers:v4"},
|
|
||||||
SizeBytes: 456,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
kubelet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, false, kubelet.isContainerRuntimeVersionCompatible)
|
|
||||||
kubelet.updateRuntimeUp()
|
|
||||||
if err := kubelet.updateNodeStatus(); err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
actions := kubeClient.Actions()
|
|
||||||
if len(actions) != 2 {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
if !actions[1].Matches("update", "nodes") || actions[1].GetSubresource() != "status" {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
updatedNode, ok := actions[1].(testclient.UpdateAction).GetObject().(*api.Node)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("unexpected object type")
|
|
||||||
}
|
|
||||||
for i, cond := range updatedNode.Status.Conditions {
|
|
||||||
if cond.LastHeartbeatTime.IsZero() {
|
|
||||||
t.Errorf("unexpected zero last probe timestamp")
|
|
||||||
}
|
|
||||||
if cond.LastTransitionTime.IsZero() {
|
|
||||||
t.Errorf("unexpected zero last transition timestamp")
|
|
||||||
}
|
|
||||||
updatedNode.Status.Conditions[i].LastHeartbeatTime = unversioned.Time{}
|
|
||||||
updatedNode.Status.Conditions[i].LastTransitionTime = unversioned.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version skew workaround. See: https://github.com/kubernetes/kubernetes/issues/16961
|
|
||||||
if updatedNode.Status.Conditions[len(updatedNode.Status.Conditions)-1].Type != api.NodeReady {
|
|
||||||
t.Errorf("unexpected node condition order. NodeReady should be last.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expectedNode, updatedNode) {
|
|
||||||
t.Errorf("expected \n%v\n, got \n%v", expectedNode, updatedNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Downgrade docker version, node should be NotReady
|
|
||||||
fakeRuntime.RuntimeType = "docker"
|
|
||||||
fakeRuntime.VersionInfo = "1.5.0"
|
|
||||||
fakeRuntime.APIVersionInfo = "1.17"
|
|
||||||
kubelet.updateRuntimeUp()
|
|
||||||
if err := kubelet.updateNodeStatus(); err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
actions = kubeClient.Actions()
|
|
||||||
if len(actions) != 4 {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
if !actions[1].Matches("update", "nodes") || actions[1].GetSubresource() != "status" {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
updatedNode, ok = actions[3].(testclient.UpdateAction).GetObject().(*api.Node)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("unexpected object type")
|
|
||||||
}
|
|
||||||
if updatedNode.Status.Conditions[1].Reason != "KubeletNotReady" &&
|
|
||||||
!strings.Contains(updatedNode.Status.Conditions[1].Message, "container runtime version is older than") {
|
|
||||||
t.Errorf("unexpect NodeStatus due to container runtime version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateExistingNodeStatus(t *testing.T) {
|
func TestUpdateExistingNodeStatus(t *testing.T) {
|
||||||
testKubelet := newTestKubelet(t)
|
testKubelet := newTestKubelet(t)
|
||||||
kubelet := testKubelet.kubelet
|
kubelet := testKubelet.kubelet
|
||||||
fakeRuntime := testKubelet.fakeRuntime
|
|
||||||
fakeRuntime.RuntimeType = "docker"
|
|
||||||
fakeRuntime.VersionInfo = "1.5.0"
|
|
||||||
kubeClient := testKubelet.fakeKubeClient
|
kubeClient := testKubelet.fakeKubeClient
|
||||||
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
||||||
{
|
{
|
||||||
@ -3129,7 +2939,7 @@ func TestUpdateExistingNodeStatus(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: "test://1.5.0",
|
||||||
KubeletVersion: version.Get().String(),
|
KubeletVersion: version.Get().String(),
|
||||||
KubeProxyVersion: version.Get().String(),
|
KubeProxyVersion: version.Get().String(),
|
||||||
},
|
},
|
||||||
@ -3350,13 +3160,11 @@ func TestUpdateExistingNodeOutOfDiskStatusWithTransitionFrequency(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateNodeStatusWithoutContainerRuntime(t *testing.T) {
|
func TestUpdateNodeStatusWithRuntimeStateError(t *testing.T) {
|
||||||
testKubelet := newTestKubelet(t)
|
testKubelet := newTestKubelet(t)
|
||||||
kubelet := testKubelet.kubelet
|
kubelet := testKubelet.kubelet
|
||||||
|
clock := testKubelet.fakeClock
|
||||||
kubeClient := testKubelet.fakeKubeClient
|
kubeClient := testKubelet.fakeKubeClient
|
||||||
fakeRuntime := testKubelet.fakeRuntime
|
|
||||||
fakeRuntime.RuntimeType = "docker"
|
|
||||||
fakeRuntime.VersionInfo = "1.5.0"
|
|
||||||
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
kubeClient.ReactionChain = fake.NewSimpleClientset(&api.NodeList{Items: []api.Node{
|
||||||
{ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}},
|
{ObjectMeta: api.ObjectMeta{Name: testKubeletHostname}},
|
||||||
}}).ReactionChain
|
}}).ReactionChain
|
||||||
@ -3394,14 +3202,7 @@ func TestUpdateNodeStatusWithoutContainerRuntime(t *testing.T) {
|
|||||||
LastHeartbeatTime: unversioned.Time{},
|
LastHeartbeatTime: unversioned.Time{},
|
||||||
LastTransitionTime: unversioned.Time{},
|
LastTransitionTime: unversioned.Time{},
|
||||||
},
|
},
|
||||||
{
|
{}, //placeholder
|
||||||
Type: api.NodeReady,
|
|
||||||
Status: api.ConditionFalse,
|
|
||||||
Reason: "KubeletNotReady",
|
|
||||||
Message: fmt.Sprintf("container runtime is down"),
|
|
||||||
LastHeartbeatTime: unversioned.Time{},
|
|
||||||
LastTransitionTime: unversioned.Time{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
NodeInfo: api.NodeSystemInfo{
|
NodeInfo: api.NodeSystemInfo{
|
||||||
MachineID: "123",
|
MachineID: "123",
|
||||||
@ -3409,7 +3210,7 @@ func TestUpdateNodeStatusWithoutContainerRuntime(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: "test://1.5.0",
|
||||||
KubeletVersion: version.Get().String(),
|
KubeletVersion: version.Get().String(),
|
||||||
KubeProxyVersion: version.Get().String(),
|
KubeProxyVersion: version.Get().String(),
|
||||||
},
|
},
|
||||||
@ -3439,42 +3240,77 @@ func TestUpdateNodeStatusWithoutContainerRuntime(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kubelet.runtimeState = newRuntimeState(time.Duration(0), false, func() error { return nil })
|
|
||||||
|
checkNodeStatus := func(status api.ConditionStatus, reason, message string) {
|
||||||
|
kubeClient.ClearActions()
|
||||||
|
if err := kubelet.updateNodeStatus(); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
actions := kubeClient.Actions()
|
||||||
|
if len(actions) != 2 {
|
||||||
|
t.Fatalf("unexpected actions: %v", actions)
|
||||||
|
}
|
||||||
|
if !actions[1].Matches("update", "nodes") || actions[1].GetSubresource() != "status" {
|
||||||
|
t.Fatalf("unexpected actions: %v", actions)
|
||||||
|
}
|
||||||
|
updatedNode, ok := actions[1].(testclient.UpdateAction).GetObject().(*api.Node)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unexpected action type. expected UpdateAction, got %#v", actions[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, cond := range updatedNode.Status.Conditions {
|
||||||
|
if cond.LastHeartbeatTime.IsZero() {
|
||||||
|
t.Errorf("unexpected zero last probe timestamp")
|
||||||
|
}
|
||||||
|
if cond.LastTransitionTime.IsZero() {
|
||||||
|
t.Errorf("unexpected zero last transition timestamp")
|
||||||
|
}
|
||||||
|
updatedNode.Status.Conditions[i].LastHeartbeatTime = unversioned.Time{}
|
||||||
|
updatedNode.Status.Conditions[i].LastTransitionTime = unversioned.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version skew workaround. See: https://github.com/kubernetes/kubernetes/issues/16961
|
||||||
|
if updatedNode.Status.Conditions[len(updatedNode.Status.Conditions)-1].Type != api.NodeReady {
|
||||||
|
t.Errorf("unexpected node condition order. NodeReady should be last.")
|
||||||
|
}
|
||||||
|
expectedNode.Status.Conditions[1] = api.NodeCondition{
|
||||||
|
Type: api.NodeReady,
|
||||||
|
Status: status,
|
||||||
|
Reason: reason,
|
||||||
|
Message: message,
|
||||||
|
LastHeartbeatTime: unversioned.Time{},
|
||||||
|
LastTransitionTime: unversioned.Time{},
|
||||||
|
}
|
||||||
|
if !api.Semantic.DeepEqual(expectedNode, updatedNode) {
|
||||||
|
t.Errorf("unexpected objects: %s", util.ObjectDiff(expectedNode, updatedNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readyMessage := "kubelet is posting ready status"
|
||||||
|
downMessage := "container runtime is down"
|
||||||
|
|
||||||
|
// Should report kubelet not ready if the runtime check is out of date
|
||||||
|
clock.SetTime(time.Now().Add(-maxWaitForContainerRuntime))
|
||||||
kubelet.updateRuntimeUp()
|
kubelet.updateRuntimeUp()
|
||||||
if err := kubelet.updateNodeStatus(); err != nil {
|
checkNodeStatus(api.ConditionFalse, "KubeletNotReady", downMessage)
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
actions := kubeClient.Actions()
|
|
||||||
if len(actions) != 2 {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
if !actions[1].Matches("update", "nodes") || actions[1].GetSubresource() != "status" {
|
|
||||||
t.Fatalf("unexpected actions: %v", actions)
|
|
||||||
}
|
|
||||||
updatedNode, ok := actions[1].(testclient.UpdateAction).GetObject().(*api.Node)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("unexpected action type. expected UpdateAction, got %#v", actions[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, cond := range updatedNode.Status.Conditions {
|
// Should report kubelet ready if the runtime check is updated
|
||||||
if cond.LastHeartbeatTime.IsZero() {
|
clock.SetTime(time.Now())
|
||||||
t.Errorf("unexpected zero last probe timestamp")
|
kubelet.updateRuntimeUp()
|
||||||
}
|
checkNodeStatus(api.ConditionTrue, "KubeletReady", readyMessage)
|
||||||
if cond.LastTransitionTime.IsZero() {
|
|
||||||
t.Errorf("unexpected zero last transition timestamp")
|
|
||||||
}
|
|
||||||
updatedNode.Status.Conditions[i].LastHeartbeatTime = unversioned.Time{}
|
|
||||||
updatedNode.Status.Conditions[i].LastTransitionTime = unversioned.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version skew workaround. See: https://github.com/kubernetes/kubernetes/issues/16961
|
// Should report kubelet not ready if the runtime check is out of date
|
||||||
if updatedNode.Status.Conditions[len(updatedNode.Status.Conditions)-1].Type != api.NodeReady {
|
clock.SetTime(time.Now().Add(-maxWaitForContainerRuntime))
|
||||||
t.Errorf("unexpected node condition order. NodeReady should be last.")
|
kubelet.updateRuntimeUp()
|
||||||
}
|
checkNodeStatus(api.ConditionFalse, "KubeletNotReady", downMessage)
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expectedNode, updatedNode) {
|
// Should report kubelet not ready if the runtime check failed
|
||||||
t.Errorf("unexpected objects: %s", util.ObjectDiff(expectedNode, updatedNode))
|
fakeRuntime := testKubelet.fakeRuntime
|
||||||
}
|
// Inject error into fake runtime status check, node should be NotReady
|
||||||
|
fakeRuntime.StatusErr = fmt.Errorf("injected runtime status error")
|
||||||
|
clock.SetTime(time.Now())
|
||||||
|
kubelet.updateRuntimeUp()
|
||||||
|
checkNodeStatus(api.ConditionFalse, "KubeletNotReady", downMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateNodeStatusError(t *testing.T) {
|
func TestUpdateNodeStatusError(t *testing.T) {
|
||||||
|
@ -184,13 +184,6 @@ func New(config *Config,
|
|||||||
rkt.imagePuller = kubecontainer.NewImagePuller(recorder, rkt, imageBackOff)
|
rkt.imagePuller = kubecontainer.NewImagePuller(recorder, rkt, imageBackOff)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rkt.checkVersion(minimumRktBinVersion, recommendedRktBinVersion, minimumAppcVersion, minimumRktApiVersion, minimumSystemdVersion); err != nil {
|
|
||||||
// TODO(yifan): Latest go-systemd version have the ability to close the
|
|
||||||
// dbus connection. However the 'docker/libcontainer' package is using
|
|
||||||
// the older go-systemd version, so we can't update the go-systemd version.
|
|
||||||
rkt.apisvcConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rkt, nil
|
return rkt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,7 +1055,12 @@ func (r *Runtime) Version() (kubecontainer.Version, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) APIVersion() (kubecontainer.Version, error) {
|
func (r *Runtime) APIVersion() (kubecontainer.Version, error) {
|
||||||
return r.binVersion, nil
|
return r.apiVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns error if rkt is unhealthy, nil otherwise.
|
||||||
|
func (r *Runtime) Status() error {
|
||||||
|
return r.checkVersion(minimumRktBinVersion, recommendedRktBinVersion, minimumAppcVersion, minimumRktApiVersion, minimumSystemdVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncPod syncs the running pod to match the specified desired pod.
|
// SyncPod syncs the running pod to match the specified desired pod.
|
||||||
|
@ -30,7 +30,6 @@ type runtimeState struct {
|
|||||||
internalError error
|
internalError error
|
||||||
cidr string
|
cidr string
|
||||||
initError error
|
initError error
|
||||||
runtimeCompatibility func() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *runtimeState) setRuntimeSync(t time.Time) {
|
func (s *runtimeState) setRuntimeSync(t time.Time) {
|
||||||
@ -85,16 +84,12 @@ func (s *runtimeState) errors() []string {
|
|||||||
if s.internalError != nil {
|
if s.internalError != nil {
|
||||||
ret = append(ret, s.internalError.Error())
|
ret = append(ret, s.internalError.Error())
|
||||||
}
|
}
|
||||||
if err := s.runtimeCompatibility(); err != nil {
|
|
||||||
ret = append(ret, err.Error())
|
|
||||||
}
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuntimeState(
|
func newRuntimeState(
|
||||||
runtimeSyncThreshold time.Duration,
|
runtimeSyncThreshold time.Duration,
|
||||||
configureNetwork bool,
|
configureNetwork bool,
|
||||||
runtimeCompatibility func() error,
|
|
||||||
) *runtimeState {
|
) *runtimeState {
|
||||||
var networkError error = nil
|
var networkError error = nil
|
||||||
if configureNetwork {
|
if configureNetwork {
|
||||||
@ -105,6 +100,5 @@ func newRuntimeState(
|
|||||||
baseRuntimeSyncThreshold: runtimeSyncThreshold,
|
baseRuntimeSyncThreshold: runtimeSyncThreshold,
|
||||||
networkError: networkError,
|
networkError: networkError,
|
||||||
internalError: nil,
|
internalError: nil,
|
||||||
runtimeCompatibility: runtimeCompatibility,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user