@@ -3,27 +3,34 @@ package healthcheck
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "math"
78 "net/http"
89 "time"
910
1011 "github.com/cenkalti/backoff/v3"
1112 "github.com/pkg/errors"
1213 "github.com/rs/zerolog/log"
14+ "github.com/threefoldtech/zbus"
15+ "github.com/threefoldtech/zos/pkg/perf"
16+ "github.com/threefoldtech/zos/pkg/stubs"
1317 "github.com/threefoldtech/zos/pkg/zinit"
1418)
1519
1620const acceptableSkew = 10 * time .Minute
1721
1822func RunNTPCheck (ctx context.Context ) {
23+ operation := func () error {
24+ return ntpCheck (ctx )
25+ }
1926 go func () {
2027 for {
2128 exp := backoff .NewExponentialBackOff ()
2229 retryNotify := func (err error , d time.Duration ) {
2330 log .Error ().Err (err ).Msg ("failed to run ntp check" )
2431 }
2532
26- if err := backoff .RetryNotify (ntpCheck , backoff .WithContext (exp , ctx ), retryNotify ); err != nil {
33+ if err := backoff .RetryNotify (operation , backoff .WithContext (exp , ctx ), retryNotify ); err != nil {
2734 log .Error ().Err (err ).Send ()
2835 continue
2936 }
@@ -40,10 +47,13 @@ func RunNTPCheck(ctx context.Context) {
4047 }()
4148}
4249
43- func ntpCheck () error {
50+ func ntpCheck (ctx context. Context ) error {
4451 z := zinit .Default ()
45-
46- utcTime , err := getCurrentUTCTime ()
52+ zcl , err := perf .TryGetZbusClient (ctx )
53+ if err != nil {
54+ return fmt .Errorf ("ntpCheck expects zbus client in the context and found none %w" , err )
55+ }
56+ utcTime , err := getCurrentUTCTime (zcl )
4757 if err != nil {
4858 return err
4959 }
@@ -57,20 +67,101 @@ func ntpCheck() error {
5767 return nil
5868}
5969
60- func getCurrentUTCTime () (time.Time , error ) {
70+ func getCurrentUTCTime (zcl zbus.Client ) (time.Time , error ) {
71+
72+ // TimeServer represents a time server with its name and fetching function
73+ type TimeServer struct {
74+ Name string
75+ Func func () (time.Time , error )
76+ }
77+
78+ // List of time servers, and here not in the global vars, so we can inject zcl to pass to getTimeChainWithZCL
79+ var timeServers = []TimeServer {
80+ {
81+ Name : "tfchain" ,
82+ Func : func () (time.Time , error ) {
83+ return getTimeChainWithZCL (zcl )
84+ },
85+ },
86+ {
87+ Name : "worldtimeapi" ,
88+ Func : getWorldTimeAPI ,
89+ },
90+ {
91+ Name : "worldclockapi" ,
92+ Func : getWorldClockAPI ,
93+ },
94+ {
95+ Name : "timeapi.io" ,
96+ Func : getTimeAPI ,
97+ },
98+ }
99+ for _ , server := range timeServers {
100+ log .Info ().Msg (fmt .Sprint ("running NTP check against " , server .Name ))
101+ utcTime , err := server .Func ()
102+ if err == nil {
103+ log .Info ().Msg (fmt .Sprint ("utc time from " , server .Name , ": " , utcTime ))
104+ return utcTime , nil
105+ }
106+ log .Error ().Err (err ).Str ("server" , server .Name ).Msg ("failed to get time from server" )
107+ }
108+ return time.Time {}, errors .New ("failed to get time from all servers" )
109+ }
110+
111+ func getWorldTimeAPI () (time.Time , error ) {
61112 timeRes , err := http .Get ("https://worldtimeapi.org/api/timezone/UTC" )
62113 if err != nil {
63- return time.Time {}, errors .Wrapf (err , "failed to get date" )
114+ return time.Time {}, errors .Wrapf (err , "failed to get date from worldtimeapi " )
64115 }
116+ defer timeRes .Body .Close ()
65117
66118 var utcTime struct {
67119 DateTime time.Time `json:"datetime"`
68120 }
69- err = json .NewDecoder (timeRes .Body ).Decode (& utcTime )
70- timeRes .Body .Close ()
71- if err != nil {
72- return time.Time {}, errors .Wrapf (err , "failed to decode date response" )
121+ if err := json .NewDecoder (timeRes .Body ).Decode (& utcTime ); err != nil {
122+ return time.Time {}, errors .Wrapf (err , "failed to decode date response from worldtimeapi" )
73123 }
74124
75125 return utcTime .DateTime , nil
76126}
127+
128+ func getWorldClockAPI () (time.Time , error ) {
129+ timeRes , err := http .Get ("http://worldclockapi.com/api/json/utc/now" )
130+ if err != nil {
131+ return time.Time {}, errors .Wrapf (err , "failed to get date from worldclockapi" )
132+ }
133+ defer timeRes .Body .Close ()
134+
135+ var utcTime struct {
136+ CurrentDateTime string `json:"currentDateTime"` // Changed to string, needs manual parsing
137+ }
138+ if err := json .NewDecoder (timeRes .Body ).Decode (& utcTime ); err != nil {
139+ return time.Time {}, errors .Wrapf (err , "failed to decode date response from worldclockapi" )
140+ }
141+
142+ // Parse the time manually, handling the "Z"
143+ return time .Parse ("2006-01-02T15:04Z" , utcTime .CurrentDateTime )
144+ }
145+
146+ func getTimeAPI () (time.Time , error ) {
147+ timeRes , err := http .Get ("https://timeapi.io/api/Time/current/zone?timeZone=UTC" )
148+ if err != nil {
149+ return time.Time {}, errors .Wrapf (err , "failed to get date from timeapi.io" )
150+ }
151+ defer timeRes .Body .Close ()
152+
153+ var utcTime struct {
154+ DateTime string `json:"dateTime"` // Changed to string, needs manual parsing
155+ }
156+ if err := json .NewDecoder (timeRes .Body ).Decode (& utcTime ); err != nil {
157+ return time.Time {}, errors .Wrapf (err , "failed to decode date response from timeapi.io" )
158+ }
159+
160+ // Parse the time manually, handling the fractional seconds
161+ return time .Parse ("2006-01-02T15:04:05.999999" , utcTime .DateTime )
162+ }
163+
164+ func getTimeChainWithZCL (zcl zbus.Client ) (time.Time , error ) {
165+ gw := stubs .NewSubstrateGatewayStub (zcl )
166+ return gw .GetTime (context .Background ())
167+ }
0 commit comments