Commit e87fa5e0 authored by Random Liu's avatar Random Liu Committed by Random-Liu

* Refactor kuberuntime unit test

* Add gc unit test * Fix init container unit test
parent 37dc74fa
...@@ -123,10 +123,13 @@ func (r *FakeRuntimeService) RunPodSandbox(config *runtimeApi.PodSandboxConfig) ...@@ -123,10 +123,13 @@ func (r *FakeRuntimeService) RunPodSandbox(config *runtimeApi.PodSandboxConfig)
readyState := runtimeApi.PodSandBoxState_READY readyState := runtimeApi.PodSandBoxState_READY
r.Sandboxes[podSandboxID] = &FakePodSandbox{ r.Sandboxes[podSandboxID] = &FakePodSandbox{
PodSandboxStatus: runtimeApi.PodSandboxStatus{ PodSandboxStatus: runtimeApi.PodSandboxStatus{
Id: &podSandboxID, Id: &podSandboxID,
Metadata: config.Metadata, Metadata: config.Metadata,
State: &readyState, State: &readyState,
CreatedAt: &createdAt, CreatedAt: &createdAt,
Network: &runtimeApi.PodSandboxNetworkStatus{
Ip: &FakePodSandboxIP,
},
Labels: config.Labels, Labels: config.Labels,
Annotations: config.Annotations, Annotations: config.Annotations,
}, },
...@@ -174,17 +177,8 @@ func (r *FakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeApi. ...@@ -174,17 +177,8 @@ func (r *FakeRuntimeService) PodSandboxStatus(podSandboxID string) (*runtimeApi.
return nil, fmt.Errorf("pod sandbox %q not found", podSandboxID) return nil, fmt.Errorf("pod sandbox %q not found", podSandboxID)
} }
return &runtimeApi.PodSandboxStatus{ status := s.PodSandboxStatus
Id: &podSandboxID, return &status, nil
Metadata: s.Metadata,
CreatedAt: s.CreatedAt,
State: s.State,
Network: &runtimeApi.PodSandboxNetworkStatus{
Ip: &FakePodSandboxIP,
},
Labels: s.Labels,
Annotations: s.Annotations,
}, nil
} }
func (r *FakeRuntimeService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]*runtimeApi.PodSandbox, error) { func (r *FakeRuntimeService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]*runtimeApi.PodSandbox, error) {
...@@ -228,7 +222,7 @@ func (r *FakeRuntimeService) CreateContainer(podSandboxID string, config *runtim ...@@ -228,7 +222,7 @@ func (r *FakeRuntimeService) CreateContainer(podSandboxID string, config *runtim
// ContainerID should be randomized for real container runtime, but here just use // ContainerID should be randomized for real container runtime, but here just use
// fixed BuildContainerName() for easily making fake containers. // fixed BuildContainerName() for easily making fake containers.
containerID := BuildContainerName(config.Metadata) containerID := BuildContainerName(config.Metadata, podSandboxID)
createdAt := time.Now().Unix() createdAt := time.Now().Unix()
createdState := runtimeApi.ContainerState_CREATED createdState := runtimeApi.ContainerState_CREATED
imageRef := config.Image.GetImage() imageRef := config.Image.GetImage()
...@@ -351,21 +345,8 @@ func (r *FakeRuntimeService) ContainerStatus(containerID string) (*runtimeApi.Co ...@@ -351,21 +345,8 @@ func (r *FakeRuntimeService) ContainerStatus(containerID string) (*runtimeApi.Co
return nil, fmt.Errorf("container %q not found", containerID) return nil, fmt.Errorf("container %q not found", containerID)
} }
return &runtimeApi.ContainerStatus{ status := c.ContainerStatus
Id: c.Id, return &status, nil
Metadata: c.Metadata,
State: c.State,
CreatedAt: c.CreatedAt,
Image: c.Image,
ImageRef: c.ImageRef,
Labels: c.Labels,
Annotations: c.Annotations,
ExitCode: c.ExitCode,
StartedAt: c.StartedAt,
FinishedAt: c.FinishedAt,
Reason: c.Reason,
Mounts: c.Mounts,
}, nil
} }
func (r *FakeRuntimeService) Exec(containerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error { func (r *FakeRuntimeService) Exec(containerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error {
......
...@@ -22,8 +22,9 @@ import ( ...@@ -22,8 +22,9 @@ import (
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
) )
func BuildContainerName(metadata *runtimeApi.ContainerMetadata) string { func BuildContainerName(metadata *runtimeApi.ContainerMetadata, sandboxID string) string {
return fmt.Sprintf("%s_%d", metadata.GetName(), metadata.GetAttempt()) // include the sandbox ID to make the container ID unique.
return fmt.Sprintf("%s_%s_%d", sandboxID, metadata.GetName(), metadata.GetAttempt())
} }
func BuildSandboxName(metadata *runtimeApi.PodSandboxMetadata) string { func BuildSandboxName(metadata *runtimeApi.PodSandboxMetadata) string {
......
...@@ -148,6 +148,7 @@ func TestGarbageCollectNoMaxLimit(t *testing.T) { ...@@ -148,6 +148,7 @@ func TestGarbageCollectNoMaxLimit(t *testing.T) {
}) })
addPods(gc.podGetter, "foo", "foo1", "foo2", "foo3", "foo4") addPods(gc.podGetter, "foo", "foo1", "foo2", "foo3", "foo4")
assert.Nil(t, gc.GarbageCollect(kubecontainer.ContainerGCPolicy{MinAge: time.Minute, MaxPerPodContainer: -1, MaxContainers: -1}, true))
assert.Len(t, fakeDocker.Removed, 0) assert.Len(t, fakeDocker.Removed, 0)
} }
......
...@@ -47,8 +47,7 @@ func TestRemoveContainer(t *testing.T) { ...@@ -47,8 +47,7 @@ func TestRemoveContainer(t *testing.T) {
} }
// Create fake sandbox and container // Create fake sandbox and container
_, fakeContainers, err := makeAndSetFakePod(m, fakeRuntime, pod) _, fakeContainers := makeAndSetFakePod(t, m, fakeRuntime, pod)
assert.NoError(t, err)
assert.Equal(t, len(fakeContainers), 1) assert.Equal(t, len(fakeContainers), 1)
containerId := fakeContainers[0].GetId() containerId := fakeContainers[0].GetId()
......
...@@ -136,6 +136,12 @@ func (cgc *containerGC) removeSandbox(sandboxID string) { ...@@ -136,6 +136,12 @@ func (cgc *containerGC) removeSandbox(sandboxID string) {
} }
} }
// isPodDeleted returns true if the pod is already deleted.
func (cgc *containerGC) isPodDeleted(podUID types.UID) bool {
_, found := cgc.podGetter.GetPodByUID(podUID)
return !found
}
// evictableContainers gets all containers that are evictable. Evictable containers are: not running // evictableContainers gets all containers that are evictable. Evictable containers are: not running
// and created more than MinAge ago. // and created more than MinAge ago.
func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByEvictUnit, error) { func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByEvictUnit, error) {
...@@ -179,17 +185,64 @@ func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByE ...@@ -179,17 +185,64 @@ func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByE
return evictUnits, nil return evictUnits, nil
} }
// evictableSandboxes gets all sandboxes that are evictable. Evictable sandboxes are: not running // evict all containers that are evictable
func (cgc *containerGC) evictContainers(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool) error {
// Separate containers by evict units.
evictUnits, err := cgc.evictableContainers(gcPolicy.MinAge)
if err != nil {
return err
}
// Remove deleted pod containers if all sources are ready.
if allSourcesReady {
for key, unit := range evictUnits {
if cgc.isPodDeleted(key.uid) {
cgc.removeOldestN(unit, len(unit)) // Remove all.
delete(evictUnits, key)
}
}
}
// Enforce max containers per evict unit.
if gcPolicy.MaxPerPodContainer >= 0 {
cgc.enforceMaxContainersPerEvictUnit(evictUnits, gcPolicy.MaxPerPodContainer)
}
// Enforce max total number of containers.
if gcPolicy.MaxContainers >= 0 && evictUnits.NumContainers() > gcPolicy.MaxContainers {
// Leave an equal number of containers per evict unit (min: 1).
numContainersPerEvictUnit := gcPolicy.MaxContainers / evictUnits.NumEvictUnits()
if numContainersPerEvictUnit < 1 {
numContainersPerEvictUnit = 1
}
cgc.enforceMaxContainersPerEvictUnit(evictUnits, numContainersPerEvictUnit)
// If we still need to evict, evict oldest first.
numContainers := evictUnits.NumContainers()
if numContainers > gcPolicy.MaxContainers {
flattened := make([]containerGCInfo, 0, numContainers)
for key := range evictUnits {
flattened = append(flattened, evictUnits[key]...)
}
sort.Sort(byCreated(flattened))
cgc.removeOldestN(flattened, numContainers-gcPolicy.MaxContainers)
}
}
return nil
}
// evictSandboxes evicts all sandboxes that are evictable. Evictable sandboxes are: not running
// and contains no containers at all. // and contains no containers at all.
func (cgc *containerGC) evictableSandboxes(minAge time.Duration) ([]string, error) { func (cgc *containerGC) evictSandboxes(minAge time.Duration) error {
containers, err := cgc.manager.getKubeletContainers(true) containers, err := cgc.manager.getKubeletContainers(true)
if err != nil { if err != nil {
return nil, err return err
} }
sandboxes, err := cgc.manager.getKubeletSandboxes(true) sandboxes, err := cgc.manager.getKubeletSandboxes(true)
if err != nil { if err != nil {
return nil, err return err
} }
evictSandboxes := make([]string, 0) evictSandboxes := make([]string, 0)
...@@ -222,13 +275,10 @@ func (cgc *containerGC) evictableSandboxes(minAge time.Duration) ([]string, erro ...@@ -222,13 +275,10 @@ func (cgc *containerGC) evictableSandboxes(minAge time.Duration) ([]string, erro
evictSandboxes = append(evictSandboxes, sandboxID) evictSandboxes = append(evictSandboxes, sandboxID)
} }
return evictSandboxes, nil for _, sandbox := range evictSandboxes {
} cgc.removeSandbox(sandbox)
}
// isPodDeleted returns true if the pod is already deleted. return nil
func (cgc *containerGC) isPodDeleted(podUID types.UID) bool {
_, found := cgc.podGetter.GetPodByUID(podUID)
return !found
} }
// evictPodLogsDirectories evicts all evictable pod logs directories. Pod logs directories // evictPodLogsDirectories evicts all evictable pod logs directories. Pod logs directories
...@@ -278,59 +328,16 @@ func (cgc *containerGC) evictPodLogsDirectories(allSourcesReady bool) error { ...@@ -278,59 +328,16 @@ func (cgc *containerGC) evictPodLogsDirectories(allSourcesReady bool) error {
// * gets evictable sandboxes which are not ready and contains no containers. // * gets evictable sandboxes which are not ready and contains no containers.
// * removes evictable sandboxes. // * removes evictable sandboxes.
func (cgc *containerGC) GarbageCollect(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool) error { func (cgc *containerGC) GarbageCollect(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool) error {
// Separate containers by evict units. // Remove evictable containers
evictUnits, err := cgc.evictableContainers(gcPolicy.MinAge) if err := cgc.evictContainers(gcPolicy, allSourcesReady); err != nil {
if err != nil {
return err return err
} }
// Remove deleted pod containers if all sources are ready.
if allSourcesReady {
for key, unit := range evictUnits {
if cgc.isPodDeleted(key.uid) {
cgc.removeOldestN(unit, len(unit)) // Remove all.
delete(evictUnits, key)
}
}
}
// Enforce max containers per evict unit.
if gcPolicy.MaxPerPodContainer >= 0 {
cgc.enforceMaxContainersPerEvictUnit(evictUnits, gcPolicy.MaxPerPodContainer)
}
// Enforce max total number of containers.
if gcPolicy.MaxContainers >= 0 && evictUnits.NumContainers() > gcPolicy.MaxContainers {
// Leave an equal number of containers per evict unit (min: 1).
numContainersPerEvictUnit := gcPolicy.MaxContainers / evictUnits.NumEvictUnits()
if numContainersPerEvictUnit < 1 {
numContainersPerEvictUnit = 1
}
cgc.enforceMaxContainersPerEvictUnit(evictUnits, numContainersPerEvictUnit)
// If we still need to evict, evict oldest first.
numContainers := evictUnits.NumContainers()
if numContainers > gcPolicy.MaxContainers {
flattened := make([]containerGCInfo, 0, numContainers)
for key := range evictUnits {
flattened = append(flattened, evictUnits[key]...)
}
sort.Sort(byCreated(flattened))
cgc.removeOldestN(flattened, numContainers-gcPolicy.MaxContainers)
}
}
// Remove sandboxes with zero containers // Remove sandboxes with zero containers
evictSandboxes, err := cgc.evictableSandboxes(sandboxMinGCAge) if err := cgc.evictSandboxes(sandboxMinGCAge); err != nil {
if err != nil {
return err return err
} }
for _, sandbox := range evictSandboxes {
cgc.removeSandbox(sandbox)
}
// Remove pod sandbox log directory // Remove pod sandbox log directory
// TODO(random-liu): Add legacy container log localtion cleanup.
return cgc.evictPodLogsDirectories(allSourcesReady) return cgc.evictPodLogsDirectories(allSourcesReady)
} }
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