Merge pull request #20137 from mqliang/fake-time

reuse fake clock in the kube repo for kubelet unit tests
This commit is contained in:
Alex Robinson 2016-01-30 16:37:44 -08:00
commit ae5a6e86df
6 changed files with 19 additions and 173 deletions

4
Godeps/Godeps.json generated
View File

@ -861,10 +861,6 @@
"ImportPath": "github.com/spf13/pflag",
"Rev": "08b1a584251b5b62f458943640fc8ebd4d50aaa5"
},
{
"ImportPath": "github.com/ssoroka/ttime",
"Rev": "881f221816e0300201ac24f6c31e54e3bb958de7"
},
{
"ImportPath": "github.com/stretchr/objx",
"Rev": "d40df0cc104c06eae2dfe03d7dddb83802d52f9a"

1
Godeps/LICENSES.md generated
View File

@ -84,7 +84,6 @@ github.com/Sirupsen/logrus | MITname
github.com/skynetservices/skydns | MITname
github.com/spf13/cobra | Apache-2
github.com/spf13/pflag | spdxBSD3
github.com/ssoroka/ttime | UNKNOWN
github.com/stretchr/objx | MIT?
github.com/stretchr/testify | MIT?
github.com/syndtr/gocapability | spdxBSD2

View File

@ -1,42 +0,0 @@
This is an experiment in making time easier to mock in Go tests.
You should be able to alias the ttime library to time to avoid having to change all your time.Now() methods to ttime.Now() throughout your code.
All methods return actual time.Time structs (if they were supposed to).
example code:
import (
time "github.com/ssoroka/ttime"
)
fmt.Printf("The starting time is %v", time.Now().UTC())
// in test this will not sleep at all, but it will advance the clock 5 seconds.
// in production, it's identical to time.Sleep
time.Sleep(5 * time.Second)
fmt.Printf("The time after sleeping for 5 seconds is %v", time.Now().UTC())
time.After(10 * time.Second, func() {
// This will execute after 10 seconds in production and immediately in tests.
fmt.Printf("It is now %v", time.Now().UTC())
})
example test:
func TestFreezingTime(t *testing.T) {
time.Freeze(time.Now()) // freeze the system clock, at least as far as ttime is concerned.
// or freeze time at a specific date/time (eg, test leap-year support!):
now, err := time.Parse(time.RFC3339, "2012-02-29T00:00:00Z")
if err != nil { panic("date time parse failed") }
time.Freeze(now)
defer time.Unfreeze()
// test leap-year-specific code
if !isLeapYear(time.Now()) {
t.Error("Oh no! isLeapYear is broken!")
}
t.Logf("It is now %v", time.Now().UTC())
}

View File

@ -1,108 +0,0 @@
package ttime
import "time"
var (
currentTime time.Time
timeFrozen bool
)
type Duration time.Duration
type Location time.Location
type Month time.Month
type ParseError time.ParseError
type Ticker time.Ticker
type Time time.Time
type Timer time.Timer
type Weekday time.Weekday
var (
// import a ton of constants so we can act like the time library.
Parse = time.Parse
ParseDuration = time.ParseDuration
Date = time.Date
ParseInLocation = time.ParseInLocation
FixedZone = time.FixedZone
LoadLocation = time.LoadLocation
Sunday = time.Sunday
Monday = time.Monday
Tuesday = time.Tuesday
Wednesday = time.Wednesday
Thursday = time.Thursday
Friday = time.Friday
Saturday = time.Saturday
ANSIC = time.ANSIC
UnixDate = time.UnixDate
RubyDate = time.RubyDate
RFC822 = time.RFC822
RFC822Z = time.RFC822Z
RFC850 = time.RFC850
RFC1123 = time.RFC1123
RFC1123Z = time.RFC1123Z
RFC3339 = time.RFC3339
RFC3339Nano = time.RFC3339Nano
Kitchen = time.Kitchen
Stamp = time.Stamp
StampMilli = time.StampMilli
StampMicro = time.StampMicro
StampNano = time.StampNano
// constants that I really should redefine:
NewTimer = time.NewTimer
NewTicker = time.NewTicker
Unix = time.Unix
)
func Freeze(t time.Time) {
currentTime = t
timeFrozen = true
}
func Unfreeze() {
timeFrozen = false
}
func IsFrozen() bool {
return timeFrozen
}
func Now() time.Time {
if timeFrozen {
return currentTime
} else {
return time.Now()
}
}
func After(d time.Duration) <-chan time.Time {
if timeFrozen {
currentTime = currentTime.Add(d)
c := make(chan time.Time, 1)
c <- currentTime
return c
} else {
return time.After(d)
}
}
func Tick(d time.Duration) <-chan time.Time {
if timeFrozen {
c := make(chan time.Time, 1)
go func() {
for {
currentTime = currentTime.Add(d)
c <- currentTime
}
}()
return c
} else {
return time.Tick(d)
}
}
func Sleep(d time.Duration) {
if timeFrozen && d > 0 {
currentTime = currentTime.Add(d)
} else {
time.Sleep(d)
}
}

View File

@ -156,7 +156,7 @@ func (im *realImageManager) GetImageList() ([]kubecontainer.Image, error) {
return images, nil
}
func (im *realImageManager) detectImages(detected time.Time) error {
func (im *realImageManager) detectImages(detectTime time.Time) error {
images, err := im.runtime.ListImages()
if err != nil {
return err
@ -185,7 +185,7 @@ func (im *realImageManager) detectImages(detected time.Time) error {
// New image, set it as detected now.
if _, ok := im.imageRecords[image.ID]; !ok {
im.imageRecords[image.ID] = &imageRecord{
firstDetected: detected,
firstDetected: detectTime,
}
}
@ -228,7 +228,7 @@ func (im *realImageManager) GarbageCollect() error {
if usagePercent >= im.policy.HighThresholdPercent {
amountToFree := usage - (int64(im.policy.LowThresholdPercent) * capacity / 100)
glog.Infof("[ImageManager]: Disk usage on %q (%s) is at %d%% which is over the high threshold (%d%%). Trying to free %d bytes", fsInfo.Device, fsInfo.Mountpoint, usagePercent, im.policy.HighThresholdPercent, amountToFree)
freed, err := im.freeSpace(amountToFree)
freed, err := im.freeSpace(amountToFree, time.Now())
if err != nil {
return err
}
@ -249,9 +249,8 @@ func (im *realImageManager) GarbageCollect() error {
// bytes freed is always returned.
// Note that error may be nil and the number of bytes free may be less
// than bytesToFree.
func (im *realImageManager) freeSpace(bytesToFree int64) (int64, error) {
startTime := time.Now()
err := im.detectImages(startTime)
func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (int64, error) {
err := im.detectImages(freeTime)
if err != nil {
return 0, err
}
@ -274,13 +273,13 @@ func (im *realImageManager) freeSpace(bytesToFree int64) (int64, error) {
spaceFreed := int64(0)
for _, image := range images {
// Images that are currently in used were given a newer lastUsed.
if image.lastUsed.After(startTime) {
if image.lastUsed.After(freeTime) {
break
}
// Avoid garbage collect the image if the image is not old enough.
// In such a case, the image may have just been pulled down, and will be used by a container right away.
if startTime.Sub(image.firstDetected) < im.minAge {
if freeTime.Sub(image.firstDetected) < im.minAge {
continue
}

View File

@ -22,12 +22,12 @@ import (
"time"
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
"github.com/ssoroka/ttime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/kubernetes/pkg/client/record"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/util"
)
var zero time.Time
@ -231,7 +231,7 @@ func TestFreeSpaceImagesInUseContainersAreIgnored(t *testing.T) {
},
}
spaceFreed, err := manager.freeSpace(2048)
spaceFreed, err := manager.freeSpace(2048, time.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(1024, spaceFreed)
@ -271,7 +271,7 @@ func TestFreeSpaceRemoveByLeastRecentlyUsed(t *testing.T) {
require.NoError(t, manager.detectImages(time.Now()))
require.Equal(t, manager.imageRecordsLen(), 2)
spaceFreed, err := manager.freeSpace(1024)
spaceFreed, err := manager.freeSpace(1024, time.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(1024, spaceFreed)
@ -302,7 +302,7 @@ func TestFreeSpaceTiesBrokenByDetectedTime(t *testing.T) {
require.NoError(t, manager.detectImages(time.Now()))
require.Equal(t, manager.imageRecordsLen(), 2)
spaceFreed, err := manager.freeSpace(1024)
spaceFreed, err := manager.freeSpace(1024, time.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(2048, spaceFreed)
@ -330,7 +330,7 @@ func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) {
},
}
spaceFreed, err := manager.freeSpace(1024)
spaceFreed, err := manager.freeSpace(1024, time.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(1024, spaceFreed)
@ -431,18 +431,20 @@ func TestGarbageCollectImageNotOldEnough(t *testing.T) {
},
}
require.NoError(t, manager.detectImages(ttime.Now()))
fakeClock := util.FakeClock{Time: time.Now()}
fmt.Println(fakeClock.Now())
require.NoError(t, manager.detectImages(fakeClock.Now()))
require.Equal(t, manager.imageRecordsLen(), 2)
// no space freed since one image is in used, and another one is not old enough
spaceFreed, err := manager.freeSpace(1024)
spaceFreed, err := manager.freeSpace(1024, fakeClock.Now())
assert := assert.New(t)
require.NoError(t, err)
assert.EqualValues(0, spaceFreed)
assert.Len(fakeRuntime.ImageList, 2)
// sleep 1 minute, then 1 image will be garbage collected
ttime.Sleep(defaultGCAge)
spaceFreed, err = manager.freeSpace(1024)
// move clock by minAge duration, then 1 image will be garbage collected
fakeClock.Step(manager.minAge)
spaceFreed, err = manager.freeSpace(1024, fakeClock.Now())
require.NoError(t, err)
assert.EqualValues(1024, spaceFreed)
assert.Len(fakeRuntime.ImageList, 1)