From ca3e53bab0a7d1996cdca428409895fc92343d6d Mon Sep 17 00:00:00 2001 From: Shaun Crampton Date: Fri, 7 Aug 2020 10:21:29 +0100 Subject: [PATCH] Fix up conntrack UTs; move to timeshim. --- bpf/bpf.go | 10 ---------- bpf/bpf_syscall.go | 10 ++++++++++ bpf/bpf_syscall_stub.go | 4 ++++ bpf/conntrack/cleanup.go | 27 ++++++++++++++++++++------- bpf/conntrack/conntrack_suite_test.go | 2 +- bpf/conntrack/conntrack_test.go | 15 +++++++-------- timeshim/mocktime/mocktime.go | 15 ++++++++++++--- timeshim/time.go | 7 +++++++ 8 files changed, 61 insertions(+), 29 deletions(-) diff --git a/bpf/bpf.go b/bpf/bpf.go index 527ceb6a5e..dab33b4f1a 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -2220,13 +2220,3 @@ func SupportsBPFDataplane() error { return nil } - -// KTimeNanos returns a nanosecond timestamp that is comparable with the ones generated by BPF. -func KTimeNanos() int64 { - var ts unix.Timespec - err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts) - if err != nil { - log.WithError(err).Panic("Failed to read system clock") - } - return ts.Nano() -} diff --git a/bpf/bpf_syscall.go b/bpf/bpf_syscall.go index c359090f2b..83c37aae90 100644 --- a/bpf/bpf_syscall.go +++ b/bpf/bpf_syscall.go @@ -598,3 +598,13 @@ func (m *MapIterator) Close() error { return nil } + +// KTimeNanos returns a nanosecond timestamp that is comparable with the ones generated by BPF. +func KTimeNanos() int64 { + var ts unix.Timespec + err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts) + if err != nil { + log.WithError(err).Panic("Failed to read system clock") + } + return ts.Nano() +} diff --git a/bpf/bpf_syscall_stub.go b/bpf/bpf_syscall_stub.go index ed0f10e49a..347a9a5e5a 100644 --- a/bpf/bpf_syscall_stub.go +++ b/bpf/bpf_syscall_stub.go @@ -78,3 +78,7 @@ func (m *MapIterator) Next() (k, v []byte, err error) { func (m *MapIterator) Close() error { return nil } + +func KTimeNanos() int64 { + panic("BPF syscall stub") +} diff --git a/bpf/conntrack/cleanup.go b/bpf/conntrack/cleanup.go index 91e00dab1e..35d79d1589 100644 --- a/bpf/conntrack/cleanup.go +++ b/bpf/conntrack/cleanup.go @@ -21,6 +21,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/projectcalico/felix/bpf" + "github.com/projectcalico/felix/timeshim" ) type Timeouts struct { @@ -133,7 +134,7 @@ func (s *Scanner) get(k Key) (Value, error) { type LivenessScanner struct { timeouts Timeouts dsr bool - NowNanos func() int64 + time timeshim.Interface // goTimeOfLastKTimeLookup is the go timestamp of the last time we looked up the kernel time. // We cache the kernel time because it's expensive to look up (vs looking up a go timestamp which uses vdso). @@ -142,18 +143,30 @@ type LivenessScanner struct { cachedKTime int64 } -func NewLivenessScanner(timeouts Timeouts, dsr bool) *LivenessScanner { - return &LivenessScanner{ +func NewLivenessScanner(timeouts Timeouts, dsr bool, opts ...LivenessScannerOpt) *LivenessScanner { + ls := &LivenessScanner{ timeouts: timeouts, dsr: dsr, - NowNanos: bpf.KTimeNanos, + time: timeshim.RealTime(), + } + for _, opt := range opts { + opt(ls) + } + return ls +} + +type LivenessScannerOpt func(ls *LivenessScanner) + +func WithTimeShim(shim timeshim.Interface) LivenessScannerOpt { + return func(ls *LivenessScanner) { + ls.time = shim } } func (l *LivenessScanner) ScanEntry(ctKey Key, ctVal Value, get EntryGet) ScanVerdict { - if l.cachedKTime == 0 || time.Since(l.goTimeOfLastKTimeLookup) > time.Second { - l.cachedKTime = l.NowNanos() - l.goTimeOfLastKTimeLookup = time.Now() + if l.cachedKTime == 0 || l.time.Since(l.goTimeOfLastKTimeLookup) > time.Second { + l.cachedKTime = l.time.KTimeNanos() + l.goTimeOfLastKTimeLookup = l.time.Now() } now := l.cachedKTime diff --git a/bpf/conntrack/conntrack_suite_test.go b/bpf/conntrack/conntrack_suite_test.go index 5c661482b5..ba42783162 100644 --- a/bpf/conntrack/conntrack_suite_test.go +++ b/bpf/conntrack/conntrack_suite_test.go @@ -33,7 +33,7 @@ func init() { logrus.SetFormatter(&logutils.Formatter{}) } -func TestCalculationGraph(t *testing.T) { +func TestBPFConntrack(t *testing.T) { RegisterFailHandler(Fail) junitReporter := reporters.NewJUnitReporter("../../report/bpf_conntrack_suite.xml") RunSpecsWithDefaultAndCustomReporters(t, "BPF Conntrack Suite", []Reporter{junitReporter}) diff --git a/bpf/conntrack/conntrack_test.go b/bpf/conntrack/conntrack_test.go index a2580cb533..6e9b12fbb3 100644 --- a/bpf/conntrack/conntrack_test.go +++ b/bpf/conntrack/conntrack_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/projectcalico/felix/bpf" + "github.com/projectcalico/felix/timeshim/mocktime" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" @@ -30,7 +31,7 @@ import ( "github.com/projectcalico/felix/bpf/mock" ) -const now = 1000 * time.Hour +var now = mocktime.StartKTime var ( ip1 = net.ParseIP("10.0.0.1") @@ -73,13 +74,13 @@ var _ = Describe("BPF Conntrack LivenessCalculator", func() { var lc *conntrack.LivenessScanner var scanner *conntrack.Scanner var ctMap *mock.Map + var mockTime *mocktime.MockTime BeforeEach(func() { + mockTime = mocktime.New() + Expect(mockTime.KTimeNanos()).To(BeNumerically("==",now)) ctMap = mock.NewMockMap(conntrack.MapParams) - lc = conntrack.NewLivenessScanner(timeouts, false) - lc.NowNanos = func() int64 { - return int64(now) - } + lc = conntrack.NewLivenessScanner(timeouts, false, conntrack.WithTimeShim(mockTime)) scanner = conntrack.NewScanner(ctMap, lc.ScanEntry) }) @@ -119,9 +120,7 @@ var _ = Describe("BPF Conntrack LivenessCalculator", func() { By("always deleting the entry if we fast-forward time") err = ctMap.Update(key.AsBytes(), entry[:]) Expect(err).NotTo(HaveOccurred()) - lc.NowNanos = func() int64 { - return int64(now + 2*time.Hour) - } + mockTime.IncrementTime(2*time.Hour) scanner.Scan() _, err = ctMap.Get(key.AsBytes()) Expect(bpf.IsNotExists(err)).To(BeTrue(), "Scan() should have cleaned up entry") diff --git a/timeshim/mocktime/mocktime.go b/timeshim/mocktime/mocktime.go index 29204eebfc..9a4835a9fe 100644 --- a/timeshim/mocktime/mocktime.go +++ b/timeshim/mocktime/mocktime.go @@ -24,11 +24,13 @@ import ( "github.com/projectcalico/felix/timeshim" ) -var startTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") +var StartTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") +var StartKTime = 1000 * time.Hour +var KTimeEpoch = StartTime.Add(-StartKTime) func New() *MockTime { return &MockTime{ - currentTime: startTime, + currentTime: StartTime, } } @@ -125,6 +127,13 @@ func (m *MockTime) Now() time.Time { return t } +func (m *MockTime) KTimeNanos() int64 { + m.lock.Lock() + defer m.lock.Unlock() + // Kernel time isn't necessarily coupled to the same epoch so use a different one. + return int64(m.currentTime.Sub(KTimeEpoch)) +} + func (m *MockTime) Since(t time.Time) time.Duration { return m.Now().Sub(t) } @@ -153,7 +162,7 @@ func (m *MockTime) incrementTimeLockHeld(t time.Duration) { } m.currentTime = m.currentTime.Add(t) - logrus.WithField("increment", t).WithField("t", m.currentTime.Sub(startTime)).Info("Incrementing time") + logrus.WithField("increment", t).WithField("t", m.currentTime.Sub(StartTime)).Info("Incrementing time") if len(m.timers) == 0 { return diff --git a/timeshim/time.go b/timeshim/time.go index c3442b593f..2e61a9ee00 100644 --- a/timeshim/time.go +++ b/timeshim/time.go @@ -16,6 +16,8 @@ package timeshim import ( "time" + + "github.com/projectcalico/felix/bpf" ) // Time is our shim interface to the time package. @@ -25,6 +27,7 @@ type Interface interface { Until(t time.Time) time.Duration After(t time.Duration) <-chan time.Time NewTimer(d Duration) Timer + KTimeNanos() int64 } type Time = time.Time @@ -79,3 +82,7 @@ func (realTime) Now() time.Time { func (realTime) Since(t time.Time) time.Duration { return time.Since(t) } + +func (realTime) KTimeNanos() int64 { + return bpf.KTimeNanos() +}