getTimeFromServer returns a function that will return timestamp as returned by the server increasing it client-side by certain interval until maximum is reached, at which point it will ask the server again for new timestamp. The server endpoint must be HTTP and be set using KOPIA_FAKE_CLOCK_ENDPOIN
(endpoint string)
| 34 | // The server endpoint must be HTTP and be set using KOPIA_FAKE_CLOCK_ENDPOINT environment |
| 35 | // variable. |
| 36 | func getTimeFromServer(endpoint string) func() time.Time { |
| 37 | var mu sync.Mutex |
| 38 | |
| 39 | var timeInfo struct { |
| 40 | Time time.Time `json:"time"` |
| 41 | ValidFor time.Duration `json:"validFor"` |
| 42 | } |
| 43 | |
| 44 | var ( |
| 45 | nextRefreshRealTime time.Time //nolint:forbidigo |
| 46 | localTimeOffset time.Duration // offset to be added to time.Now() to produce server time |
| 47 | ) |
| 48 | |
| 49 | return func() time.Time { |
| 50 | mu.Lock() |
| 51 | defer mu.Unlock() |
| 52 | |
| 53 | localTime := time.Now() //nolint:forbidigo |
| 54 | if localTime.After(nextRefreshRealTime) { |
| 55 | resp, err := http.Get(endpoint) //nolint:gosec,noctx |
| 56 | if err != nil { |
| 57 | log.Fatalf("unable to get fake time from server: %v", err) |
| 58 | } |
| 59 | defer resp.Body.Close() //nolint:errcheck |
| 60 | |
| 61 | if resp.StatusCode != http.StatusOK { |
| 62 | log.Fatalf("unable to get fake time from server: %v", resp.Status) |
| 63 | } |
| 64 | |
| 65 | if err := json.NewDecoder(resp.Body).Decode(&timeInfo); err != nil { |
| 66 | log.Fatalf("invalid time received from fake time server: %v", err) |
| 67 | } |
| 68 | |
| 69 | nextRefreshRealTime = localTime.Add(timeInfo.ValidFor) //nolint:forbidigo |
| 70 | |
| 71 | // compute offset such that localTime + localTimeOffset == serverTime |
| 72 | localTimeOffset = timeInfo.Time.Sub(localTime) |
| 73 | } |
| 74 | |
| 75 | return discardMonotonicTime(localTime.Add(localTimeOffset)) |
| 76 | } |
| 77 | } |