Skip to content

Commit a2b19b3

Browse files
authored
Merge pull request kubernetes#127910 from leonzz/leonzz-tmp
fix node start time inconsistency in kubelet
2 parents 9fe41b6 + d506720 commit a2b19b3

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

pkg/kubelet/util/boottime_util_linux.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,54 @@ package util
2121

2222
import (
2323
"fmt"
24+
"os"
25+
"strconv"
26+
"strings"
2427
"time"
2528

2629
"golang.org/x/sys/unix"
30+
"k8s.io/klog/v2"
2731
)
2832

29-
// GetBootTime returns the time at which the machine was started, truncated to the nearest second
33+
// GetBootTime returns the time at which the machine was started, truncated to the nearest second.
34+
// It uses /proc/stat first, which is more accurate, and falls back to the less accurate
35+
// unix.Sysinfo if /proc/stat failed.
3036
func GetBootTime() (time.Time, error) {
37+
bootTime, err := getBootTimeWithProcStat()
38+
if err != nil {
39+
klog.InfoS("Failed to get boot time from /proc/uptime. Will retry with unix.Sysinfo.", "error", err)
40+
return getBootTimeWithSysinfo()
41+
}
42+
return bootTime, nil
43+
}
44+
45+
func getBootTimeWithProcStat() (time.Time, error) {
46+
raw, err := os.ReadFile("/proc/stat")
47+
if err != nil {
48+
return time.Time{}, fmt.Errorf("error getting boot time: %w", err)
49+
}
50+
rawFields := strings.Fields(string(raw))
51+
for i, v := range rawFields {
52+
if v == "btime" {
53+
if len(rawFields) > i+1 {
54+
sec, err := strconv.ParseInt(rawFields[i+1], 10, 64)
55+
if err != nil {
56+
return time.Time{}, fmt.Errorf("error parsing boot time %s: %w", rawFields[i+1], err)
57+
}
58+
return time.Unix(sec, 0), nil
59+
}
60+
break
61+
}
62+
}
63+
64+
return time.Time{}, fmt.Errorf("can not find btime from /proc/stat: %s", raw)
65+
}
66+
67+
func getBootTimeWithSysinfo() (time.Time, error) {
3168
currentTime := time.Now()
3269
var info unix.Sysinfo_t
3370
if err := unix.Sysinfo(&info); err != nil {
34-
return time.Time{}, fmt.Errorf("error getting system uptime: %s", err)
71+
return time.Time{}, fmt.Errorf("error getting system uptime: %w", err)
3572
}
3673
return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second), nil
3774
}

pkg/kubelet/util/boottime_util_linux_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,18 @@ func TestGetBootTime(t *testing.T) {
3535
t.Errorf("Invalid system uptime")
3636
}
3737
}
38+
39+
func TestVariationBetweenGetBootTimeMethods(t *testing.T) {
40+
boottime1, err := getBootTimeWithProcStat()
41+
if err != nil {
42+
t.Errorf("Unable to get boot time from /proc/uptime")
43+
}
44+
boottime2, err := getBootTimeWithSysinfo()
45+
if err != nil {
46+
t.Errorf("Unable to get boot time from unix.Sysinfo")
47+
}
48+
diff := boottime1.Sub(boottime2)
49+
if diff > time.Second || diff < -time.Second {
50+
t.Errorf("boot time produced by 2 methods should not vary more than a second")
51+
}
52+
}

0 commit comments

Comments
 (0)