Commit 5df28fa1 authored by k8s-merge-robot's avatar k8s-merge-robot

Merge pull request #24647 from yifan-gu/rkt_gc

Automatic merge from submit-queue rkt: Refactor GarbageCollect to enforce GCPolicy. Previously, we uses `rkt gc` to garbage collect dead pods, which is very coarse, and can cause the dead pods to be removed too aggressively. This PR improves the garbage collection, now after one GC iteration: - The deleted pods will be removed. - If the number of containers exceeds gcPolicy.MaxContainers, then containers whose ages are older than gcPolicy.minAge will be removed. cc @kubernetes/sig-node @euank @sjpotter Pending on #23887 for the Godep updates. <!-- Reviewable:start --> --- This change is [<img src="http://reviewable.k8s.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](http://reviewable.k8s.io/reviews/kubernetes/kubernetes/24647) <!-- Reviewable:end -->
parents 6fe3498e 9d5bcf42
...@@ -230,7 +230,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string ...@@ -230,7 +230,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string
cadvisorInterface, cadvisorInterface,
configFilePath, configFilePath,
nil, nil,
containertest.FakeOS{}, &containertest.FakeOS{},
1*time.Second, /* FileCheckFrequency */ 1*time.Second, /* FileCheckFrequency */
1*time.Second, /* HTTPCheckFrequency */ 1*time.Second, /* HTTPCheckFrequency */
10*time.Second, /* MinimumGCAge */ 10*time.Second, /* MinimumGCAge */
...@@ -263,7 +263,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string ...@@ -263,7 +263,7 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string
cadvisorInterface, cadvisorInterface,
"", "",
nil, nil,
containertest.FakeOS{}, &containertest.FakeOS{},
1*time.Second, /* FileCheckFrequency */ 1*time.Second, /* FileCheckFrequency */
1*time.Second, /* HTTPCheckFrequency */ 1*time.Second, /* HTTPCheckFrequency */
10*time.Second, /* MinimumGCAge */ 10*time.Second, /* MinimumGCAge */
......
...@@ -17,7 +17,9 @@ limitations under the License. ...@@ -17,7 +17,9 @@ limitations under the License.
package container package container
import ( import (
"io/ioutil"
"os" "os"
"time"
) )
// OSInterface collects system level operations that need to be mocked out // OSInterface collects system level operations that need to be mocked out
...@@ -26,6 +28,12 @@ type OSInterface interface { ...@@ -26,6 +28,12 @@ type OSInterface interface {
Mkdir(path string, perm os.FileMode) error Mkdir(path string, perm os.FileMode) error
Symlink(oldname string, newname string) error Symlink(oldname string, newname string) error
Stat(path string) (os.FileInfo, error) Stat(path string) (os.FileInfo, error)
Remove(path string) error
Create(path string) (*os.File, error)
Hostname() (name string, err error)
Chtimes(path string, atime time.Time, mtime time.Time) error
Pipe() (r *os.File, w *os.File, err error)
ReadDir(dirname string) ([]os.FileInfo, error)
} }
// RealOS is used to dispatch the real system level operaitons. // RealOS is used to dispatch the real system level operaitons.
...@@ -45,3 +53,34 @@ func (RealOS) Symlink(oldname string, newname string) error { ...@@ -45,3 +53,34 @@ func (RealOS) Symlink(oldname string, newname string) error {
func (RealOS) Stat(path string) (os.FileInfo, error) { func (RealOS) Stat(path string) (os.FileInfo, error) {
return os.Stat(path) return os.Stat(path)
} }
// Remove will call os.Remove to remove the path.
func (RealOS) Remove(path string) error {
return os.Remove(path)
}
// Create will call os.Create to create and return a file
// at path.
func (RealOS) Create(path string) (*os.File, error) {
return os.Create(path)
}
// Hostname will call os.Hostname to return the hostname.
func (RealOS) Hostname() (name string, err error) {
return os.Hostname()
}
// Chtimes will call os.Chtimes to change the atime and mtime of the path
func (RealOS) Chtimes(path string, atime time.Time, mtime time.Time) error {
return os.Chtimes(path, atime, mtime)
}
// Pipe will call os.Pipe to return a connected pair of pipe.
func (RealOS) Pipe() (r *os.File, w *os.File, err error) {
return os.Pipe()
}
// ReadDir will call ioutil.ReadDir to return the files under the directory.
func (RealOS) ReadDir(dirname string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dirname)
}
...@@ -19,14 +19,25 @@ package testing ...@@ -19,14 +19,25 @@ package testing
import ( import (
"errors" "errors"
"os" "os"
"time"
) )
// FakeOS mocks out certain OS calls to avoid perturbing the filesystem // FakeOS mocks out certain OS calls to avoid perturbing the filesystem
// on the test machine.
// If a member of the form `*Fn` is set, that function will be called in place // If a member of the form `*Fn` is set, that function will be called in place
// of the real call. // of the real call.
type FakeOS struct { type FakeOS struct {
StatFn func(string) (os.FileInfo, error) StatFn func(string) (os.FileInfo, error)
ReadDirFn func(string) ([]os.FileInfo, error)
HostName string
Removes []string
Files map[string][]*os.FileInfo
}
func NewFakeOS() *FakeOS {
return &FakeOS{
Removes: []string{},
Files: make(map[string][]*os.FileInfo),
}
} }
// Mkdir is a fake call that just returns nil. // Mkdir is a fake call that just returns nil.
...@@ -46,3 +57,37 @@ func (f FakeOS) Stat(path string) (os.FileInfo, error) { ...@@ -46,3 +57,37 @@ func (f FakeOS) Stat(path string) (os.FileInfo, error) {
} }
return nil, errors.New("unimplemented testing mock") return nil, errors.New("unimplemented testing mock")
} }
// Remove is a fake call that returns nil.
func (f *FakeOS) Remove(path string) error {
f.Removes = append(f.Removes, path)
return nil
}
// Create is a fake call that returns nil.
func (FakeOS) Create(path string) (*os.File, error) {
return nil, nil
}
// Hostname is a fake call that returns nil.
func (f *FakeOS) Hostname() (name string, err error) {
return f.HostName, nil
}
// Chtimes is a fake call that returns nil.
func (FakeOS) Chtimes(path string, atime time.Time, mtime time.Time) error {
return nil
}
// Pipe is a fake call that returns nil.
func (FakeOS) Pipe() (r *os.File, w *os.File, err error) {
return nil, nil, nil
}
// ReadDir is a fake call that returns the files under the directory.
func (f *FakeOS) ReadDir(dirname string) ([]os.FileInfo, error) {
if f.ReadDirFn != nil {
return f.ReadDirFn(dirname)
}
return nil, errors.New("unimplemented testing mock")
}
...@@ -653,7 +653,7 @@ func TestFindContainersByPod(t *testing.T) { ...@@ -653,7 +653,7 @@ func TestFindContainersByPod(t *testing.T) {
fakeClient := NewFakeDockerClient() fakeClient := NewFakeDockerClient()
np, _ := network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone) np, _ := network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), componentconfig.HairpinNone)
// image back-off is set to nil, this test should not pull images // image back-off is set to nil, this test should not pull images
containerManager := NewFakeDockerManager(fakeClient, &record.FakeRecorder{}, nil, nil, &cadvisorapi.MachineInfo{}, options.GetDefaultPodInfraContainerImage(), 0, 0, "", containertest.FakeOS{}, np, nil, nil, nil) containerManager := NewFakeDockerManager(fakeClient, &record.FakeRecorder{}, nil, nil, &cadvisorapi.MachineInfo{}, options.GetDefaultPodInfraContainerImage(), 0, 0, "", &containertest.FakeOS{}, np, nil, nil, nil)
for i, test := range tests { for i, test := range tests {
fakeClient.RunningContainerList = test.runningContainerList fakeClient.RunningContainerList = test.runningContainerList
fakeClient.ExitedContainerList = test.exitedContainerList fakeClient.ExitedContainerList = test.exitedContainerList
......
...@@ -115,7 +115,7 @@ func createTestDockerManager(fakeHTTPClient *fakeHTTP, fakeDocker *FakeDockerCli ...@@ -115,7 +115,7 @@ func createTestDockerManager(fakeHTTPClient *fakeHTTP, fakeDocker *FakeDockerCli
&cadvisorapi.MachineInfo{}, &cadvisorapi.MachineInfo{},
options.GetDefaultPodInfraContainerImage(), options.GetDefaultPodInfraContainerImage(),
0, 0, "", 0, 0, "",
containertest.FakeOS{}, &containertest.FakeOS{},
networkPlugin, networkPlugin,
&fakeRuntimeHelper{}, &fakeRuntimeHelper{},
fakeHTTPClient, fakeHTTPClient,
......
...@@ -430,6 +430,7 @@ func NewMainKubelet( ...@@ -430,6 +430,7 @@ func NewMainKubelet(
klet, klet,
recorder, recorder,
containerRefManager, containerRefManager,
klet.podManager,
klet.livenessManager, klet.livenessManager,
klet.volumeManager, klet.volumeManager,
klet.httpClient, klet.httpClient,
......
...@@ -123,7 +123,7 @@ func newTestKubelet(t *testing.T) *TestKubelet { ...@@ -123,7 +123,7 @@ func newTestKubelet(t *testing.T) *TestKubelet {
fakeKubeClient := &fake.Clientset{} fakeKubeClient := &fake.Clientset{}
kubelet := &Kubelet{} kubelet := &Kubelet{}
kubelet.kubeClient = fakeKubeClient kubelet.kubeClient = fakeKubeClient
kubelet.os = containertest.FakeOS{} kubelet.os = &containertest.FakeOS{}
kubelet.hostname = testKubeletHostname kubelet.hostname = testKubeletHostname
kubelet.nodeName = testKubeletHostname kubelet.nodeName = testKubeletHostname
......
...@@ -154,7 +154,7 @@ func newTestDockerManager() (*dockertools.DockerManager, *dockertools.FakeDocker ...@@ -154,7 +154,7 @@ func newTestDockerManager() (*dockertools.DockerManager, *dockertools.FakeDocker
&cadvisorapi.MachineInfo{}, &cadvisorapi.MachineInfo{},
options.GetDefaultPodInfraContainerImage(), options.GetDefaultPodInfraContainerImage(),
0, 0, "", 0, 0, "",
containertest.FakeOS{}, &containertest.FakeOS{},
networkPlugin, networkPlugin,
nil, nil,
nil, nil,
......
...@@ -19,16 +19,16 @@ package rkt ...@@ -19,16 +19,16 @@ package rkt
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"sync" "sync"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
"github.com/coreos/go-systemd/dbus" "github.com/coreos/go-systemd/dbus"
rktapi "github.com/coreos/rkt/api/v1alpha" rktapi "github.com/coreos/rkt/api/v1alpha"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"k8s.io/kubernetes/pkg/api"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/types"
) )
// fakeRktInterface mocks the rktapi.PublicAPIClient interface for testing purpose. // fakeRktInterface mocks the rktapi.PublicAPIClient interface for testing purpose.
...@@ -147,6 +147,14 @@ func (f *fakeSystemd) Reload() error { ...@@ -147,6 +147,14 @@ func (f *fakeSystemd) Reload() error {
return fmt.Errorf("Not implemented") return fmt.Errorf("Not implemented")
} }
func (f *fakeSystemd) ResetFailed() error {
f.Lock()
defer f.Unlock()
f.called = append(f.called, "ResetFailed")
return f.err
}
// fakeRuntimeHelper implementes kubecontainer.RuntimeHelper interfaces for testing purpose. // fakeRuntimeHelper implementes kubecontainer.RuntimeHelper interfaces for testing purpose.
type fakeRuntimeHelper struct { type fakeRuntimeHelper struct {
dnsServers []string dnsServers []string
...@@ -171,3 +179,44 @@ func (f *fakeRuntimeHelper) GeneratePodHostNameAndDomain(pod *api.Pod) (string, ...@@ -171,3 +179,44 @@ func (f *fakeRuntimeHelper) GeneratePodHostNameAndDomain(pod *api.Pod) (string,
func (f *fakeRuntimeHelper) GetPodDir(podUID types.UID) string { func (f *fakeRuntimeHelper) GetPodDir(podUID types.UID) string {
return "/poddir/" + string(podUID) return "/poddir/" + string(podUID)
} }
type fakeRktCli struct {
sync.Mutex
cmds []string
result []string
err error
}
func newFakeRktCli() *fakeRktCli {
return &fakeRktCli{
cmds: []string{},
result: []string{},
}
}
func (f *fakeRktCli) RunCommand(args ...string) (result []string, err error) {
f.Lock()
defer f.Unlock()
cmd := append([]string{"rkt"}, args...)
f.cmds = append(f.cmds, strings.Join(cmd, " "))
return f.result, f.err
}
func (f *fakeRktCli) Reset() {
f.cmds = []string{}
f.result = []string{}
f.err = nil
}
type fakePodGetter struct {
pods map[types.UID]*api.Pod
}
func newFakePodGetter() *fakePodGetter {
return &fakePodGetter{pods: make(map[types.UID]*api.Pod)}
}
func (f fakePodGetter) GetPodByUID(uid types.UID) (*api.Pod, bool) {
p, found := f.pods[uid]
return p, found
}
...@@ -68,7 +68,7 @@ func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Sec ...@@ -68,7 +68,7 @@ func (r *Runtime) PullImage(image kubecontainer.ImageSpec, pullSecrets []api.Sec
return err return err
} }
if _, err := r.runCommand("fetch", dockerPrefix+img); err != nil { if _, err := r.cli.RunCommand("fetch", dockerPrefix+img); err != nil {
glog.Errorf("Failed to fetch: %v", err) glog.Errorf("Failed to fetch: %v", err)
return err return err
} }
...@@ -104,7 +104,7 @@ func (r *Runtime) RemoveImage(image kubecontainer.ImageSpec) error { ...@@ -104,7 +104,7 @@ func (r *Runtime) RemoveImage(image kubecontainer.ImageSpec) error {
if err != nil { if err != nil {
return err return err
} }
if _, err := r.runCommand("image", "rm", imageID); err != nil { if _, err := r.cli.RunCommand("image", "rm", imageID); err != nil {
return err return err
} }
return nil return nil
......
...@@ -33,8 +33,10 @@ import ( ...@@ -33,8 +33,10 @@ import (
"k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/resource"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
containertesting "k8s.io/kubernetes/pkg/kubelet/container/testing" containertesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
kubetesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
"k8s.io/kubernetes/pkg/kubelet/lifecycle" "k8s.io/kubernetes/pkg/kubelet/lifecycle"
"k8s.io/kubernetes/pkg/kubelet/rkt/mock_os" "k8s.io/kubernetes/pkg/kubelet/rkt/mock_os"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/errors"
utiltesting "k8s.io/kubernetes/pkg/util/testing" utiltesting "k8s.io/kubernetes/pkg/util/testing"
) )
...@@ -1072,11 +1074,7 @@ func TestSetApp(t *testing.T) { ...@@ -1072,11 +1074,7 @@ func TestSetApp(t *testing.T) {
} }
func TestGenerateRunCommand(t *testing.T) { func TestGenerateRunCommand(t *testing.T) {
hostName, err := os.Hostname() hostName := "test-hostname"
if err != nil {
t.Fatalf("Cannot get the hostname: %v", err)
}
tests := []struct { tests := []struct {
pod *api.Pod pod *api.Pod
uuid string uuid string
...@@ -1177,6 +1175,7 @@ func TestGenerateRunCommand(t *testing.T) { ...@@ -1177,6 +1175,7 @@ func TestGenerateRunCommand(t *testing.T) {
} }
rkt := &Runtime{ rkt := &Runtime{
os: &kubetesting.FakeOS{HostName: hostName},
config: &Config{ config: &Config{
Path: "/bin/rkt/rkt", Path: "/bin/rkt/rkt",
Stage1Image: "/bin/rkt/stage1-coreos.aci", Stage1Image: "/bin/rkt/stage1-coreos.aci",
...@@ -1397,3 +1396,218 @@ func TestImageStats(t *testing.T) { ...@@ -1397,3 +1396,218 @@ func TestImageStats(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, result, &kubecontainer.ImageStats{TotalStorageBytes: 600}) assert.Equal(t, result, &kubecontainer.ImageStats{TotalStorageBytes: 600})
} }
func TestGarbageCollect(t *testing.T) {
fr := newFakeRktInterface()
fs := newFakeSystemd()
cli := newFakeRktCli()
fakeOS := kubetesting.NewFakeOS()
getter := newFakePodGetter()
rkt := &Runtime{
os: fakeOS,
cli: cli,
apisvc: fr,
podGetter: getter,
systemd: fs,
containerRefManager: kubecontainer.NewRefManager(),
}
fakeApp := &rktapi.App{Name: "app-foo"}
tests := []struct {
gcPolicy kubecontainer.ContainerGCPolicy
apiPods []*api.Pod
pods []*rktapi.Pod
serviceFilesOnDisk []string
expectedCommands []string
expectedServiceFiles []string
}{
// All running pods, should not be gc'd.
// Dead, new pods should not be gc'd.
// Dead, old pods should be gc'd.
// Deleted pods should be gc'd.
// Service files without corresponded pods should be removed.
{
kubecontainer.ContainerGCPolicy{
MinAge: 0,
MaxContainers: 0,
},
[]*api.Pod{
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-1"}},
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-2"}},
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-3"}},
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-4"}},
},
[]*rktapi.Pod{
{
Id: "deleted-foo",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: time.Now().Add(time.Hour).UnixNano(),
StartedAt: time.Now().Add(time.Hour).UnixNano(),
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-0",
},
},
},
{
Id: "running-foo",
State: rktapi.PodState_POD_STATE_RUNNING,
CreatedAt: 0,
StartedAt: 0,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-1",
},
},
},
{
Id: "running-bar",
State: rktapi.PodState_POD_STATE_RUNNING,
CreatedAt: 0,
StartedAt: 0,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-2",
},
},
},
{
Id: "dead-old",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: 0,
StartedAt: 0,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-3",
},
},
},
{
Id: "dead-new",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: time.Now().Add(time.Hour).UnixNano(),
StartedAt: time.Now().Add(time.Hour).UnixNano(),
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-4",
},
},
},
},
[]string{"k8s_dead-old.service", "k8s_deleted-foo.service", "k8s_non-existing-bar.service"},
[]string{"rkt rm dead-old", "rkt rm deleted-foo"},
[]string{"/run/systemd/system/k8s_dead-old.service", "/run/systemd/system/k8s_deleted-foo.service", "/run/systemd/system/k8s_non-existing-bar.service"},
},
// gcPolicy.MaxContainers should be enforced.
// Oldest ones are removed first.
{
kubecontainer.ContainerGCPolicy{
MinAge: 0,
MaxContainers: 1,
},
[]*api.Pod{
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-0"}},
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-1"}},
{ObjectMeta: api.ObjectMeta{UID: "pod-uid-2"}},
},
[]*rktapi.Pod{
{
Id: "dead-2",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: 2,
StartedAt: 2,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-2",
},
},
},
{
Id: "dead-1",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: 1,
StartedAt: 1,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-1",
},
},
},
{
Id: "dead-0",
State: rktapi.PodState_POD_STATE_EXITED,
CreatedAt: 0,
StartedAt: 0,
Apps: []*rktapi.App{fakeApp},
Annotations: []*rktapi.KeyValue{
{
Key: k8sRktUIDAnno,
Value: "pod-uid-0",
},
},
},
},
[]string{"k8s_dead-0.service", "k8s_dead-1.service", "k8s_dead-2.service"},
[]string{"rkt rm dead-0", "rkt rm dead-1"},
[]string{"/run/systemd/system/k8s_dead-0.service", "/run/systemd/system/k8s_dead-1.service"},
},
}
for i, tt := range tests {
testCaseHint := fmt.Sprintf("test case #%d", i)
ctrl := gomock.NewController(t)
fakeOS.ReadDirFn = func(dirname string) ([]os.FileInfo, error) {
serviceFileNames := tt.serviceFilesOnDisk
var fileInfos []os.FileInfo
for _, name := range serviceFileNames {
mockFI := mock_os.NewMockFileInfo(ctrl)
mockFI.EXPECT().Name().Return(name)
fileInfos = append(fileInfos, mockFI)
}
return fileInfos, nil
}
fr.pods = tt.pods
for _, p := range tt.apiPods {
getter.pods[p.UID] = p
}
err := rkt.GarbageCollect(tt.gcPolicy)
assert.NoError(t, err, testCaseHint)
sort.Sort(sortedStringList(tt.expectedCommands))
sort.Sort(sortedStringList(cli.cmds))
assert.Equal(t, tt.expectedCommands, cli.cmds, testCaseHint)
sort.Sort(sortedStringList(tt.expectedServiceFiles))
sort.Sort(sortedStringList(fakeOS.Removes))
assert.Equal(t, tt.expectedServiceFiles, fakeOS.Removes, testCaseHint)
// Cleanup after each test.
cli.Reset()
ctrl.Finish()
fakeOS.Removes = []string{}
getter.pods = make(map[types.UID]*api.Pod)
}
}
...@@ -62,6 +62,8 @@ type systemdInterface interface { ...@@ -62,6 +62,8 @@ type systemdInterface interface {
RestartUnit(name string, mode string, ch chan<- string) (int, error) RestartUnit(name string, mode string, ch chan<- string) (int, error)
// Reload is equivalent to 'systemctl daemon-reload'. // Reload is equivalent to 'systemctl daemon-reload'.
Reload() error Reload() error
// ResetFailed is equivalent to 'systemctl reset-failed'.
ResetFailed() error
} }
// systemd implements the systemdInterface using dbus and systemctl. // systemd implements the systemdInterface using dbus and systemctl.
...@@ -101,3 +103,8 @@ func (s *systemd) Version() (systemdVersion, error) { ...@@ -101,3 +103,8 @@ func (s *systemd) Version() (systemdVersion, error) {
} }
return systemdVersion(result), nil return systemdVersion(result), nil
} }
// ResetFailed calls 'systemctl reset failed'
func (s *systemd) ResetFailed() error {
return exec.Command("systemctl", "reset-failed").Run()
}
...@@ -69,7 +69,7 @@ func TestRunOnce(t *testing.T) { ...@@ -69,7 +69,7 @@ func TestRunOnce(t *testing.T) {
statusManager: status.NewManager(nil, podManager), statusManager: status.NewManager(nil, podManager),
containerRefManager: kubecontainer.NewRefManager(), containerRefManager: kubecontainer.NewRefManager(),
podManager: podManager, podManager: podManager,
os: containertest.FakeOS{}, os: &containertest.FakeOS{},
volumeManager: newVolumeManager(), volumeManager: newVolumeManager(),
diskSpaceManager: diskSpaceManager, diskSpaceManager: diskSpaceManager,
containerRuntime: fakeRuntime, containerRuntime: fakeRuntime,
......
...@@ -65,7 +65,7 @@ func NewHollowKubelet( ...@@ -65,7 +65,7 @@ func NewHollowKubelet(
cadvisorInterface, cadvisorInterface,
manifestFilePath, manifestFilePath,
nil, /* cloud-provider */ nil, /* cloud-provider */
containertest.FakeOS{}, /* os-interface */ &containertest.FakeOS{}, /* os-interface */
20*time.Second, /* FileCheckFrequency */ 20*time.Second, /* FileCheckFrequency */
20*time.Second, /* HTTPCheckFrequency */ 20*time.Second, /* HTTPCheckFrequency */
1*time.Minute, /* MinimumGCAge */ 1*time.Minute, /* MinimumGCAge */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment