mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-28 06:59:44 -08:00
Updated vendored ntp package (#681)
The github.com/beevik/ntp package was recently updated with some API changes that broke node_exporter. This commit fetches the latest version of the ntp package and brings node_exporter in line with the latest API.
This commit is contained in:
parent
859a825bb8
commit
b62c7bc0ad
|
@ -152,7 +152,7 @@ func (c *ntpCollector) Update(ch chan<- prometheus.Metric) error {
|
||||||
maxerr += time.Second
|
maxerr += time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Validate() && resp.RootDistance <= *ntpMaxDistance && resp.CausalityViolation <= maxerr {
|
if resp.Validate() == nil && resp.RootDistance <= *ntpMaxDistance && resp.MinError <= maxerr {
|
||||||
ch <- c.sanity.mustNewConstMetric(1)
|
ch <- c.sanity.mustNewConstMetric(1)
|
||||||
} else {
|
} else {
|
||||||
ch <- c.sanity.mustNewConstMetric(0)
|
ch <- c.sanity.mustNewConstMetric(0)
|
||||||
|
|
1
vendor/github.com/beevik/ntp/CONTRIBUTORS
generated
vendored
1
vendor/github.com/beevik/ntp/CONTRIBUTORS
generated
vendored
|
@ -4,3 +4,4 @@ Anton Tolchanov (knyar)
|
||||||
Christopher Batey (chbatey)
|
Christopher Batey (chbatey)
|
||||||
Meng Zhuo (mengzhuo)
|
Meng Zhuo (mengzhuo)
|
||||||
Leonid Evdokimov (darkk)
|
Leonid Evdokimov (darkk)
|
||||||
|
Ask Bjørn Hansen (abh)
|
19
vendor/github.com/beevik/ntp/README.md
generated
vendored
19
vendor/github.com/beevik/ntp/README.md
generated
vendored
|
@ -4,21 +4,22 @@
|
||||||
ntp
|
ntp
|
||||||
===
|
===
|
||||||
|
|
||||||
The ntp package is an implementation of a simple NTP client. It allows you
|
The ntp package is an implementation of a Simple NTP (SNTP) client based on
|
||||||
to connect to a remote NTP server and request the current time.
|
[RFC5905](https://tools.ietf.org/html/rfc5905). It allows you to connect to
|
||||||
|
a remote NTP server and request the current time.
|
||||||
|
|
||||||
To request the current time, simply do the following:
|
If all you care about is the current time according to a known remote NTP
|
||||||
|
server, simply use the `Time` function:
|
||||||
```go
|
```go
|
||||||
time, err := ntp.Time("0.beevik-ntp.pool.ntp.org")
|
time, err := ntp.Time("0.beevik-ntp.pool.ntp.org")
|
||||||
```
|
```
|
||||||
|
|
||||||
To request the current time along with additional metadata, use the Query
|
If you want the time as well as additional metadata about the time, use the
|
||||||
function:
|
`Query` function instead:
|
||||||
```go
|
```go
|
||||||
response, err := ntp.Query("0.beevik-ntp.pool.ntp.org")
|
response, err := ntp.Query("0.beevik-ntp.pool.ntp.org")
|
||||||
```
|
```
|
||||||
|
|
||||||
NB: if you want to use the NTP Pool in your software you should request your
|
To use the NTP pool in your application, please request your own
|
||||||
own [vendor zone](http://www.pool.ntp.org/en/vendors.html). You **must
|
[vendor zone](http://www.pool.ntp.org/en/vendors.html). Avoid using
|
||||||
absolutely not use the default pool.ntp.org zone names** as the default
|
the `[number].pool.ntp.org` zone names in your applications.
|
||||||
configuration in your application or appliance.
|
|
||||||
|
|
44
vendor/github.com/beevik/ntp/RELEASE_NOTES.md
generated
vendored
Normal file
44
vendor/github.com/beevik/ntp/RELEASE_NOTES.md
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
Release v0.1.1
|
||||||
|
==============
|
||||||
|
|
||||||
|
**Breaking changes**
|
||||||
|
|
||||||
|
* Removed the `MaxStratum` constant.
|
||||||
|
|
||||||
|
**Deprecations**
|
||||||
|
|
||||||
|
* Officially deprecated the `TimeV` function.
|
||||||
|
|
||||||
|
**Internal changes**
|
||||||
|
|
||||||
|
* Removed `minDispersion` from the `RootDistance` calculation, since the value
|
||||||
|
was arbitrary.
|
||||||
|
* Moved some validation into main code path so that invalid `TransmitTime` and
|
||||||
|
`mode` responses trigger an error even when `Response.Validate` is not
|
||||||
|
called.
|
||||||
|
|
||||||
|
|
||||||
|
Release v0.1.0
|
||||||
|
==============
|
||||||
|
|
||||||
|
This is the initial release of the `ntp` package. Currently it supports the following features:
|
||||||
|
* `Time()` to query the current time according to a remote NTP server.
|
||||||
|
* `Query()` to query multiple pieces of time-related information from a remote NTP server.
|
||||||
|
* `QueryWithOptions()`, which is like `Query()` but with the ability to override default query options.
|
||||||
|
|
||||||
|
Time-related information returned by the `Query` functions includes:
|
||||||
|
* `Time`: the time the server transmitted its response, according to the server's clock.
|
||||||
|
* `ClockOffset`: the estimated offset of the client's clock relative to the server's clock. You may apply this offset to any local system clock reading once the query is complete.
|
||||||
|
* `RTT`: an estimate of the round-trip-time delay between the client and the server.
|
||||||
|
* `Precision`: the precision of the server's clock reading.
|
||||||
|
* `Stratum`: the "stratum" level of the server, where 1 indicates a server directly connected to a reference clock, and values greater than 1 indicating the number of hops from the reference clock.
|
||||||
|
* `ReferenceID`: A unique identifier for the NTP server that was contacted.
|
||||||
|
* `ReferenceTime`: The time at which the server last updated its local clock setting.
|
||||||
|
* `RootDelay`: The server's round-trip delay to the reference clock.
|
||||||
|
* `RootDispersion`: The server's total dispersion to the referenced clock.
|
||||||
|
* `RootDistance`: An estimate of the root synchronization distance.
|
||||||
|
* `Leap`: The leap second indicator.
|
||||||
|
* `MinError`: A lower bound on the clock error between the client and the server.
|
||||||
|
* `Poll`: the maximum polling interval between successive messages on the server.
|
||||||
|
|
||||||
|
The `Response` structure returned by the `Query` functions also contains a `Response.Validate()` function that returns an error if any of the fields returned by the server are invalid.
|
579
vendor/github.com/beevik/ntp/ntp.go
generated
vendored
579
vendor/github.com/beevik/ntp/ntp.go
generated
vendored
|
@ -1,15 +1,17 @@
|
||||||
// Copyright 2015 Brett Vickers.
|
// Copyright 2015-2017 Brett Vickers.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package ntp provides a simple mechanism for querying the current time from
|
// Package ntp provides an implementation of a Simple NTP (SNTP) client
|
||||||
// a remote NTP server. See RFC 5905. Approach inspired by go-nuts post by
|
// capable of querying the current time from a remote NTP server. See
|
||||||
// Michael Hofmann:
|
// RFC5905 (https://tools.ietf.org/html/rfc5905) for more details.
|
||||||
//
|
//
|
||||||
|
// This approach grew out of a go-nuts post by Michael Hofmann:
|
||||||
// https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ
|
// https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ
|
||||||
package ntp
|
package ntp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
@ -18,8 +20,42 @@ import (
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The LeapIndicator is used to warn if a leap second should be inserted
|
||||||
|
// or deleted in the last minute of the current month.
|
||||||
|
type LeapIndicator uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LeapNoWarning indicates no impending leap second.
|
||||||
|
LeapNoWarning LeapIndicator = 0
|
||||||
|
|
||||||
|
// LeapAddSecond indicates the last minute of the day has 61 seconds.
|
||||||
|
LeapAddSecond = 1
|
||||||
|
|
||||||
|
// LeapDelSecond indicates the last minute of the day has 59 seconds.
|
||||||
|
LeapDelSecond = 2
|
||||||
|
|
||||||
|
// LeapNotInSync indicates an unsynchronized leap second.
|
||||||
|
LeapNotInSync = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Internal constants
|
||||||
|
const (
|
||||||
|
defaultNtpVersion = 4
|
||||||
|
nanoPerSec = 1000000000
|
||||||
|
maxStratum = 16
|
||||||
|
defaultTimeout = 5 * time.Second
|
||||||
|
maxPollInterval = (1 << 17) * time.Second
|
||||||
|
maxDispersion = 16 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// Internal variables
|
||||||
|
var (
|
||||||
|
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
)
|
||||||
|
|
||||||
type mode uint8
|
type mode uint8
|
||||||
|
|
||||||
|
// NTP modes. This package uses only client mode.
|
||||||
const (
|
const (
|
||||||
reserved mode = 0 + iota
|
reserved mode = 0 + iota
|
||||||
symmetricActive
|
symmetricActive
|
||||||
|
@ -31,44 +67,8 @@ const (
|
||||||
reservedPrivate
|
reservedPrivate
|
||||||
)
|
)
|
||||||
|
|
||||||
// The LeapIndicator is used to warn if a leap second should be inserted
|
|
||||||
// or deleted in the last minute of the current month.
|
|
||||||
type LeapIndicator uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// LeapNoWarning indicates no impending leap second
|
|
||||||
LeapNoWarning LeapIndicator = 0
|
|
||||||
|
|
||||||
// LeapAddSecond indicates the last minute of the day has 61 seconds
|
|
||||||
LeapAddSecond = 1
|
|
||||||
|
|
||||||
// LeapDelSecond indicates the last minute of the day has 59 seconds
|
|
||||||
LeapDelSecond = 2
|
|
||||||
|
|
||||||
// LeapNotInSync indicates an unsynchronized leap second.
|
|
||||||
LeapNotInSync = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxStratum is the largest allowable NTP stratum value
|
|
||||||
MaxStratum = 16
|
|
||||||
|
|
||||||
nanoPerSec = 1000000000
|
|
||||||
|
|
||||||
defaultNtpVersion = 4
|
|
||||||
|
|
||||||
maxPoll = 17 // log2 max poll interval (~36 h)
|
|
||||||
maxDispersion = 16 // aka MAXDISP
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
)
|
|
||||||
|
|
||||||
// An ntpTime is a 64-bit fixed-point (Q32.32) representation of the number of
|
// An ntpTime is a 64-bit fixed-point (Q32.32) representation of the number of
|
||||||
// seconds elapsed since the NTP epoch.
|
// seconds elapsed.
|
||||||
type ntpTime uint64
|
type ntpTime uint64
|
||||||
|
|
||||||
// Duration interprets the fixed-point ntpTime as a number of elapsed seconds
|
// Duration interprets the fixed-point ntpTime as a number of elapsed seconds
|
||||||
|
@ -79,7 +79,7 @@ func (t ntpTime) Duration() time.Duration {
|
||||||
return time.Duration(sec + frac)
|
return time.Duration(sec + frac)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time interprets the fixed-point ntpTime as a an absolute time and returns
|
// Time interprets the fixed-point ntpTime as an absolute time and returns
|
||||||
// the corresponding time.Time value.
|
// the corresponding time.Time value.
|
||||||
func (t ntpTime) Time() time.Time {
|
func (t ntpTime) Time() time.Time {
|
||||||
return ntpEpoch.Add(t.Duration())
|
return ntpEpoch.Add(t.Duration())
|
||||||
|
@ -90,12 +90,15 @@ func (t ntpTime) Time() time.Time {
|
||||||
func toNtpTime(t time.Time) ntpTime {
|
func toNtpTime(t time.Time) ntpTime {
|
||||||
nsec := uint64(t.Sub(ntpEpoch))
|
nsec := uint64(t.Sub(ntpEpoch))
|
||||||
sec := nsec / nanoPerSec
|
sec := nsec / nanoPerSec
|
||||||
frac := (nsec - sec*nanoPerSec) << 32 / nanoPerSec
|
// Round up the fractional component so that repeated conversions
|
||||||
|
// between time.Time and ntpTime do not yield continually decreasing
|
||||||
|
// results.
|
||||||
|
frac := (((nsec - sec*nanoPerSec) << 32) + nanoPerSec - 1) / nanoPerSec
|
||||||
return ntpTime(sec<<32 | frac)
|
return ntpTime(sec<<32 | frac)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ntpTimeShort is a 32-bit fixed-point (Q16.16) representation of the
|
// An ntpTimeShort is a 32-bit fixed-point (Q16.16) representation of the
|
||||||
// number of seconds elapsed since the NTP epoch.
|
// number of seconds elapsed.
|
||||||
type ntpTimeShort uint32
|
type ntpTimeShort uint32
|
||||||
|
|
||||||
// Duration interprets the fixed-point ntpTimeShort as a number of elapsed
|
// Duration interprets the fixed-point ntpTimeShort as a number of elapsed
|
||||||
|
@ -132,134 +135,151 @@ func (m *msg) setMode(md mode) {
|
||||||
m.LiVnMode = (m.LiVnMode & 0xf8) | uint8(md)
|
m.LiVnMode = (m.LiVnMode & 0xf8) | uint8(md)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setLeapIndicator modifies the leap indicator on the message.
|
// setLeap modifies the leap indicator on the message.
|
||||||
func (m *msg) setLeapIndicator(li LeapIndicator) {
|
func (m *msg) setLeap(li LeapIndicator) {
|
||||||
m.LiVnMode = (m.LiVnMode & 0x3f) | uint8(li)<<6
|
m.LiVnMode = (m.LiVnMode & 0x3f) | uint8(li)<<6
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLeapIndicator returns the leap indicator on the message.
|
// getVersion returns the version value in the message.
|
||||||
func (m *msg) getLeapIndicator() LeapIndicator {
|
func (m *msg) getVersion() int {
|
||||||
|
return int((m.LiVnMode >> 3) & 0x07)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMode returns the mode value in the message.
|
||||||
|
func (m *msg) getMode() mode {
|
||||||
|
return mode(m.LiVnMode & 0x07)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLeap returns the leap indicator on the message.
|
||||||
|
func (m *msg) getLeap() LeapIndicator {
|
||||||
return LeapIndicator((m.LiVnMode >> 6) & 0x03)
|
return LeapIndicator((m.LiVnMode >> 6) & 0x03)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryOptions contains the list of configurable options that may be used with
|
// QueryOptions contains the list of configurable options that may be used
|
||||||
// the QueryWithOptions function.
|
// with the QueryWithOptions function.
|
||||||
type QueryOptions struct {
|
type QueryOptions struct {
|
||||||
Timeout time.Duration // defaults to 5 seconds
|
Timeout time.Duration // defaults to 5 seconds
|
||||||
Version int // NTP protocol version, defaults to 4
|
Version int // NTP protocol version, defaults to 4
|
||||||
Port int // NTP Server port for UDPAddr.Port, defaults to 123
|
LocalAddress string // IP address to use for the client address
|
||||||
TTL int // IP TTL to use for outgoing UDP packets, defaults to system default
|
Port int // Server port, defaults to 123
|
||||||
|
TTL int // IP TTL to use, defaults to system default
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Response contains time data, some of which is returned by the NTP server
|
// A Response contains time data, some of which is returned by the NTP server
|
||||||
// and some of which is calculated by the client.
|
// and some of which is calculated by the client.
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Time time.Time // receive time reported by the server
|
// Time is the transmit time reported by the server just before it
|
||||||
RTT time.Duration // round-trip time between client and server
|
// responded to the client's NTP query.
|
||||||
ClockOffset time.Duration // local clock offset relative to server
|
Time time.Time
|
||||||
Poll time.Duration // maximum polling interval
|
|
||||||
Precision time.Duration // precision of server's system clock
|
|
||||||
Stratum uint8 // stratum level of NTP server's clock
|
|
||||||
ReferenceID uint32 // server's reference ID
|
|
||||||
ReferenceTime time.Time // server's time of last clock update
|
|
||||||
RootDelay time.Duration // server's RTT to the reference clock
|
|
||||||
RootDispersion time.Duration // server's dispersion to the reference clock
|
|
||||||
Leap LeapIndicator // server's leap second indicator; see RFC 5905
|
|
||||||
|
|
||||||
// RootDistance is the single-packet estimate of the root synchronization
|
// ClockOffset is the estimated offset of the client clock relative to
|
||||||
// distance. Some SNTP clients limit-check this value before using the
|
// the server. Add this to the client's system clock time to obtain a
|
||||||
// response. For example, systemd-timesyncd uses 5.0s as an upper bound. See
|
// more accurate time.
|
||||||
// https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2
|
ClockOffset time.Duration
|
||||||
|
|
||||||
|
// RTT is the measured round-trip-time delay estimate between the client
|
||||||
|
// and the server.
|
||||||
|
RTT time.Duration
|
||||||
|
|
||||||
|
// Precision is the reported precision of the server's clock.
|
||||||
|
Precision time.Duration
|
||||||
|
|
||||||
|
// Stratum is the "stratum level" of the server. The smaller the number,
|
||||||
|
// the closer the server is to the reference clock. Stratum 1 servers are
|
||||||
|
// attached directly to the reference clock. A stratum value of 0
|
||||||
|
// indicates the "kiss of death," which typically occurs when the client
|
||||||
|
// issues too many requests to the server in a short period of time.
|
||||||
|
Stratum uint8
|
||||||
|
|
||||||
|
// ReferenceID is a 32-bit identifier identifying the server or
|
||||||
|
// reference clock.
|
||||||
|
ReferenceID uint32
|
||||||
|
|
||||||
|
// ReferenceTime is the time when the server's system clock was last
|
||||||
|
// set or corrected.
|
||||||
|
ReferenceTime time.Time
|
||||||
|
|
||||||
|
// RootDelay is the server's estimated aggregate round-trip-time delay to
|
||||||
|
// the stratum 1 server.
|
||||||
|
RootDelay time.Duration
|
||||||
|
|
||||||
|
// RootDispersion is the server's estimated maximum measurement error
|
||||||
|
// relative to the stratum 1 server.
|
||||||
|
RootDispersion time.Duration
|
||||||
|
|
||||||
|
// RootDistance is an estimate of the total synchronization distance
|
||||||
|
// between the client and the stratum 1 server.
|
||||||
RootDistance time.Duration
|
RootDistance time.Duration
|
||||||
|
|
||||||
// CausalityViolation is a time duration representing the amount of
|
// Leap indicates whether a leap second should be added or removed from
|
||||||
// causality violation between two sets of timestamps. It may be used as a
|
// the current month's last minute.
|
||||||
// lower bound on current time synchronization error betwen local and NTP
|
Leap LeapIndicator
|
||||||
// clock. A leap second may contribute as much as 1 second of causality violation.
|
|
||||||
CausalityViolation time.Duration
|
// MinError is a lower bound on the error between the client and server
|
||||||
|
// clocks. When the client and server are not synchronized to the same
|
||||||
|
// clock, the reported timestamps may appear to violate the principle of
|
||||||
|
// causality. In other words, the NTP server's response may indicate
|
||||||
|
// that a message was received before it was sent. In such cases, the
|
||||||
|
// minimum error may be useful.
|
||||||
|
MinError time.Duration
|
||||||
|
|
||||||
|
// Poll is the maximum interval between successive NTP polling messages.
|
||||||
|
// It is not relevant for simple NTP clients like this one.
|
||||||
|
Poll time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if the response is valid for the purposes of time
|
// Validate checks if the response is valid for the purposes of time
|
||||||
// synchronization.
|
// synchronization.
|
||||||
func (r *Response) Validate() bool {
|
func (r *Response) Validate() error {
|
||||||
// Reference Timestamp: Time when the system clock was last set or
|
// Handle invalid stratum values.
|
||||||
// corrected. Semantics of this value seems to vary across NTP server
|
if r.Stratum == 0 {
|
||||||
// implementations: it may be both NTP-clock time and system wall-clock
|
return errors.New("kiss of death received")
|
||||||
// time of this event. :-( So (T3 - ReferenceTime) is not true
|
}
|
||||||
// "freshness" as it may be actually NEGATIVE sometimes.
|
if r.Stratum >= maxStratum {
|
||||||
freshness := r.Time.Sub(r.ReferenceTime)
|
return errors.New("invalid stratum in response")
|
||||||
|
}
|
||||||
|
|
||||||
// (Lambda := RootDelay/2 + RootDispersion) check against MAXDISP (16s)
|
// Handle invalid leap second indicator.
|
||||||
// is required as ntp.org ntpd may report sane other fields while
|
if r.Leap == LeapNotInSync {
|
||||||
// giving quite erratic clock. The check is declared in packet() at
|
return errors.New("invalid leap second")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate the "freshness" of the time. If it exceeds the maximum
|
||||||
|
// polling interval (~36 hours), then it cannot be considered "fresh".
|
||||||
|
freshness := r.Time.Sub(r.ReferenceTime)
|
||||||
|
if freshness > maxPollInterval {
|
||||||
|
return errors.New("server clock not fresh")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the peer synchronization distance, lambda:
|
||||||
|
// lambda := RootDelay/2 + RootDispersion
|
||||||
|
// If this value exceeds MAXDISP (16s), then the time is not suitable
|
||||||
|
// for synchronization purposes.
|
||||||
// https://tools.ietf.org/html/rfc5905#appendix-A.5.1.1.
|
// https://tools.ietf.org/html/rfc5905#appendix-A.5.1.1.
|
||||||
lambda := r.RootDelay/2 + r.RootDispersion
|
lambda := r.RootDelay/2 + r.RootDispersion
|
||||||
|
if lambda > maxDispersion {
|
||||||
// `r.RTT > 0` check is not included as it does not depend on the
|
return errors.New("invalid dispersion")
|
||||||
// packet itself, but also depends on clock _speed_. It's indicator
|
|
||||||
// that local clock run faster than remote one, so (T4-T1) < (T3-T2),
|
|
||||||
// but it may be local clock issue.
|
|
||||||
// E.g. T1/T2/T3/T4 = 0/10/20/1 leads to RTT = -9s.
|
|
||||||
|
|
||||||
return r.Leap != LeapNotInSync && // RFC5905, packet()
|
|
||||||
0 < r.Stratum && r.Stratum < MaxStratum && // RFC5905, packet()
|
|
||||||
lambda < maxDispersion*time.Second && // RFC5905, packet()
|
|
||||||
!r.Time.Before(r.ReferenceTime) && // RFC5905, packet(), reftime <= xmt ~~ !(xmt < reftime)
|
|
||||||
freshness <= (1<<maxPoll)*time.Second && // ntpdate uses 24h as a heuristics instead of ~36h derived from MAXPOLL
|
|
||||||
ntpEpoch.Before(r.Time) && // sanity
|
|
||||||
ntpEpoch.Before(r.ReferenceTime) // sanity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Response) rootDistance() time.Duration {
|
// If the server's transmit time is before its reference time, the
|
||||||
// RFC5905 suggests more strict check against _peer_ in fit(), that
|
// response is invalid.
|
||||||
// root_dist should be less than MAXDIST + PHI * LOG2D(s.poll).
|
if r.Time.Before(r.ReferenceTime) {
|
||||||
// MAXPOLL is 17, so it is approximately at most (1s + 15e-6 * 2**17) =
|
return errors.New("invalid time reported")
|
||||||
// 2.96608 s, but MAXDIST and MAXPOLL are confugurable values in the
|
|
||||||
// reference implementation, so only MAXDISP check has hardcoded value
|
|
||||||
// in Validate().
|
|
||||||
//
|
|
||||||
// root_dist should also have following summands
|
|
||||||
// + Dispersion towards the peer
|
|
||||||
// + jitter of the link to the peer
|
|
||||||
// + PHI * (current_uptime - peer->uptime_of_last_update)
|
|
||||||
// but all these values are 0 if only single NTP packet was sent.
|
|
||||||
rtt := r.RTT
|
|
||||||
if rtt < 0 {
|
|
||||||
rtt = 0
|
|
||||||
}
|
|
||||||
return (rtt+r.RootDelay)/2 + r.RootDispersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Response) causalityViolation() time.Duration {
|
// nil means the response is valid.
|
||||||
// SNTP query has four timestamps for consecutive events: T1, T2, T3
|
return nil
|
||||||
// and T4. T1 and T4 use local clock, T2 and T3 use NTP clock.
|
|
||||||
// RTT = (T4 - T1) - (T3 - T2) = T4 - T3 + T2 - T1
|
|
||||||
// Offset = (T2 + T3)/2 - (T4 + T1)/2 = (-T4 + T3 + T2 - T1) / 2
|
|
||||||
// => T2 - T1 = RTT/2 + Offset && T4 - T3 = RTT/2 - Offset
|
|
||||||
// If system wall-clock is synced to NTP-clock then T2 >= T1 && T4 >= T3.
|
|
||||||
// This check may be useful against chrony NTP daemon as it starts
|
|
||||||
// relaying sane NTP clock before system wall-clock is actually adjusted.
|
|
||||||
violation := r.RTT / 2
|
|
||||||
if r.ClockOffset > 0 {
|
|
||||||
violation -= r.ClockOffset
|
|
||||||
} else {
|
|
||||||
violation += r.ClockOffset
|
|
||||||
}
|
|
||||||
if violation < 0 {
|
|
||||||
return -violation
|
|
||||||
}
|
|
||||||
return time.Duration(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query returns the current time from the remote server host. It also returns
|
// Query returns a response from the remote NTP server host. It contains
|
||||||
// additional information about the exchanged time information.
|
// the time at which the server transmitted the response as well as other
|
||||||
|
// useful information about the time and the remote server.
|
||||||
func Query(host string) (*Response, error) {
|
func Query(host string) (*Response, error) {
|
||||||
return QueryWithOptions(host, QueryOptions{})
|
return QueryWithOptions(host, QueryOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryWithOptions returns the current time from the remote server host.
|
// QueryWithOptions performs the same function as Query but allows for the
|
||||||
// It also returns additional information about the exchanged time
|
// customization of several query options.
|
||||||
// information. It allows the specification of additional query options.
|
|
||||||
func QueryWithOptions(host string, opt QueryOptions) (*Response, error) {
|
func QueryWithOptions(host string, opt QueryOptions) (*Response, error) {
|
||||||
m, now, err := getTime(host, opt)
|
m, now, err := getTime(host, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -268,64 +288,71 @@ func QueryWithOptions(host string, opt QueryOptions) (*Response, error) {
|
||||||
return parseTime(m, now), nil
|
return parseTime(m, now), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTime parses SNTP packet paired with the packet arrival time (dst) and
|
// TimeV returns the current time using information from a remote NTP server.
|
||||||
// returns Response having SNTP packet data converted to go types.
|
// On error, it returns the local system time. The version may be 2, 3, or 4.
|
||||||
func parseTime(m *msg, dst ntpTime) *Response {
|
//
|
||||||
r := &Response{
|
// Deprecated: TimeV is deprecated. Use QueryWithOptions instead.
|
||||||
Time: m.TransmitTime.Time(),
|
func TimeV(host string, version int) (time.Time, error) {
|
||||||
RTT: rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, dst),
|
m, recvTime, err := getTime(host, QueryOptions{Version: version})
|
||||||
ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, dst),
|
if err != nil {
|
||||||
Poll: toInterval(m.Poll),
|
return time.Now(), err
|
||||||
Precision: toInterval(m.Precision),
|
|
||||||
Stratum: m.Stratum,
|
|
||||||
ReferenceID: m.ReferenceID,
|
|
||||||
ReferenceTime: m.ReferenceTime.Time(),
|
|
||||||
RootDelay: m.RootDelay.Duration(),
|
|
||||||
RootDispersion: m.RootDispersion.Duration(),
|
|
||||||
Leap: m.getLeapIndicator(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// these are exported as values to preserve API style consistency
|
r := parseTime(m, recvTime)
|
||||||
r.RootDistance = r.rootDistance()
|
err = r.Validate()
|
||||||
r.CausalityViolation = r.causalityViolation()
|
if err != nil {
|
||||||
|
return time.Now(), err
|
||||||
// https://tools.ietf.org/html/rfc5905#section-7.3
|
|
||||||
if r.Stratum == 0 {
|
|
||||||
r.Stratum = MaxStratum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
// Use the clock offset to calculate the time.
|
||||||
|
return time.Now().Add(r.ClockOffset), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTime returns SNTP packet & DestinationTime timestamp.
|
// Time returns the current time using information from a remote NTP server.
|
||||||
|
// It uses version 4 of the NTP protocol. On error, it returns the local
|
||||||
|
// system time.
|
||||||
|
func Time(host string) (time.Time, error) {
|
||||||
|
return TimeV(host, defaultNtpVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTime performs the NTP server query and returns the response message
|
||||||
|
// along with the local system time it was received.
|
||||||
func getTime(host string, opt QueryOptions) (*msg, ntpTime, error) {
|
func getTime(host string, opt QueryOptions) (*msg, ntpTime, error) {
|
||||||
if opt.Version == 0 {
|
if opt.Version == 0 {
|
||||||
opt.Version = defaultNtpVersion
|
opt.Version = defaultNtpVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.Version < 2 || opt.Version > 4 {
|
if opt.Version < 2 || opt.Version > 4 {
|
||||||
panic("ntp: invalid version number")
|
return nil, 0, errors.New("invalid protocol version requested")
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.Timeout == 0 {
|
// Resolve the remote NTP server address.
|
||||||
opt.Timeout = defaultTimeout
|
raddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(host, "123"))
|
||||||
}
|
|
||||||
|
|
||||||
raddr, err := net.ResolveUDPAddr("udp", host+":123")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the local address if specified as an option.
|
||||||
|
var laddr *net.UDPAddr
|
||||||
|
if opt.LocalAddress != "" {
|
||||||
|
laddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(opt.LocalAddress, "0"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override the port if requested.
|
||||||
if opt.Port != 0 {
|
if opt.Port != 0 {
|
||||||
raddr.Port = opt.Port
|
raddr.Port = opt.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
con, err := net.DialUDP("udp", nil, raddr)
|
// Prepare a "connection" to the remote server.
|
||||||
|
con, err := net.DialUDP("udp", laddr, raddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
defer con.Close()
|
defer con.Close()
|
||||||
|
|
||||||
|
// Set a TTL for the packet if requested.
|
||||||
if opt.TTL != 0 {
|
if opt.TTL != 0 {
|
||||||
ipcon := ipv4.NewConn(con)
|
ipcon := ipv4.NewConn(con)
|
||||||
err = ipcon.SetTTL(opt.TTL)
|
err = ipcon.SetTTL(opt.TTL)
|
||||||
|
@ -334,110 +361,168 @@ func getTime(host string, opt QueryOptions) (*msg, ntpTime, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set a timeout on the connection.
|
||||||
|
if opt.Timeout == 0 {
|
||||||
|
opt.Timeout = defaultTimeout
|
||||||
|
}
|
||||||
con.SetDeadline(time.Now().Add(opt.Timeout))
|
con.SetDeadline(time.Now().Add(opt.Timeout))
|
||||||
|
|
||||||
m := new(msg)
|
// Allocate a message to hold the response.
|
||||||
m.setMode(client)
|
recvMsg := new(msg)
|
||||||
m.setVersion(opt.Version)
|
|
||||||
m.setLeapIndicator(LeapNotInSync)
|
|
||||||
|
|
||||||
xmtTime := time.Now()
|
// Allocate a message to hold the query.
|
||||||
xmt := toNtpTime(xmtTime)
|
xmitMsg := new(msg)
|
||||||
m.TransmitTime = xmt
|
xmitMsg.setMode(client)
|
||||||
|
xmitMsg.setVersion(opt.Version)
|
||||||
|
xmitMsg.setLeap(LeapNotInSync)
|
||||||
|
|
||||||
err = binary.Write(con, binary.BigEndian, m)
|
// To ensure privacy and prevent spoofing, try to use a random 64-bit
|
||||||
|
// value for the TransmitTime. If crypto/rand couldn't generate a
|
||||||
|
// random value, fall back to using the system clock. Keep track of
|
||||||
|
// when the messsage was actually sent.
|
||||||
|
r := make([]byte, 8)
|
||||||
|
_, err = rand.Read(r)
|
||||||
|
var sendTime time.Time
|
||||||
|
if err == nil {
|
||||||
|
xmitMsg.TransmitTime = ntpTime(binary.BigEndian.Uint64(r))
|
||||||
|
sendTime = time.Now()
|
||||||
|
} else {
|
||||||
|
sendTime = time.Now()
|
||||||
|
xmitMsg.TransmitTime = toNtpTime(sendTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit the query.
|
||||||
|
err = binary.Write(con, binary.BigEndian, xmitMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = binary.Read(con, binary.BigEndian, m)
|
// Receive the response.
|
||||||
|
err = binary.Read(con, binary.BigEndian, recvMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
delta := time.Since(xmtTime) // uses monotonic clock @ Go 1.9+, NB: delta != RTT
|
// Keep track of the time the response was received.
|
||||||
dst := toNtpTime(xmtTime.Add(delta))
|
delta := time.Since(sendTime)
|
||||||
|
if delta < 0 {
|
||||||
|
// The system clock may have been set backwards since the packet was
|
||||||
|
// transmitted. In go 1.9 and later, time.Since ensures that a
|
||||||
|
// monotonic clock is used, and delta can never be less than zero.
|
||||||
|
// In versions before 1.9, we have to check.
|
||||||
|
return nil, 0, errors.New("client clock ticked backwards")
|
||||||
|
}
|
||||||
|
recvTime := toNtpTime(sendTime.Add(delta))
|
||||||
|
|
||||||
// It's possible to use random uint64 as client's `TransmitTime` field,
|
// Check for invalid fields.
|
||||||
// it has better privacy (clock of the node is not disclosed in
|
if recvMsg.getMode() != server {
|
||||||
// plain-text), better UDP packet spoofing resistance (blind attacker
|
return nil, 0, errors.New("invalid mode in response")
|
||||||
// has to guess both port and the uint64 value), and OpenNTPD behaves
|
}
|
||||||
// like that. But math/rand is not secure enough for the purpose,
|
if recvMsg.TransmitTime == ntpTime(0) {
|
||||||
// crypto/rand takes 64 bits of entropy for every outgoing packet and
|
return nil, 0, errors.New("invalid transmit time in response")
|
||||||
// CSPRNG from crypto/rand/rand_unix is not available: see
|
}
|
||||||
// https://github.com/golang/go/issues/13820
|
if recvMsg.OriginTime != xmitMsg.TransmitTime {
|
||||||
// A packet is bogus if the origin timestamp t1 in the packet does not
|
return nil, 0, errors.New("server response mismatch")
|
||||||
// match the xmt state variable T1.
|
}
|
||||||
// -- https://tools.ietf.org/html/rfc5905#section-8
|
if recvMsg.ReceiveTime > recvMsg.TransmitTime {
|
||||||
if m.OriginTime != xmt {
|
return nil, 0, errors.New("server clock ticked backwards")
|
||||||
return nil, 0, errors.New("response OriginTime != query TransmitTime") // spoofed packet?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.OriginTime > dst { // Go 1.9 has monotonic clock preventing that, but 1.8 has not, so it's not panic()
|
// Correct the received message's origin time using the actual send
|
||||||
return nil, 0, errors.New("client clock tick backwards")
|
// time.
|
||||||
|
recvMsg.OriginTime = toNtpTime(sendTime)
|
||||||
|
|
||||||
|
return recvMsg, recvTime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.ReceiveTime > m.TransmitTime {
|
// parseTime parses the NTP packet along with the packet receive time to
|
||||||
return nil, 0, errors.New("server clock tick backwards")
|
// generate a Response record.
|
||||||
|
func parseTime(m *msg, recvTime ntpTime) *Response {
|
||||||
|
r := &Response{
|
||||||
|
Time: m.TransmitTime.Time(),
|
||||||
|
ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||||
|
RTT: rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||||
|
Precision: toInterval(m.Precision),
|
||||||
|
Stratum: m.Stratum,
|
||||||
|
ReferenceID: m.ReferenceID,
|
||||||
|
ReferenceTime: m.ReferenceTime.Time(),
|
||||||
|
RootDelay: m.RootDelay.Duration(),
|
||||||
|
RootDispersion: m.RootDispersion.Duration(),
|
||||||
|
Leap: m.getLeap(),
|
||||||
|
MinError: minError(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||||
|
Poll: toInterval(m.Poll),
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, dst, nil
|
// Calculate values depending on other calculated values
|
||||||
|
r.RootDistance = rootDistance(r.RTT, r.RootDelay, r.RootDispersion)
|
||||||
|
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeV returns the current time from the remote server host using the
|
// The following helper functions calculate additional metadata about the
|
||||||
// requested version of the NTP protocol. On error, it returns the local time.
|
// timestamps received from an NTP server. The timestamps returned by
|
||||||
// The version may be 2, 3, or 4.
|
// the server are given the following variable names:
|
||||||
func TimeV(host string, version int) (time.Time, error) {
|
|
||||||
m, dst, err := getTime(host, QueryOptions{Version: version})
|
|
||||||
if err != nil {
|
|
||||||
return time.Now(), err
|
|
||||||
}
|
|
||||||
r := parseTime(m, dst)
|
|
||||||
if !r.Validate() {
|
|
||||||
return time.Now(), errors.New("invalid SNTP reply")
|
|
||||||
}
|
|
||||||
// An SNTP client implementing the on-wire protocol has a single server
|
|
||||||
// and no dependent clients. It can operate with any subset of the NTP
|
|
||||||
// on-wire protocol, the simplest approach using only the transmit
|
|
||||||
// timestamp of the server packet and ignoring all other fields.
|
|
||||||
// -- https://tools.ietf.org/html/rfc5905#section-14
|
|
||||||
return time.Now().Add(r.ClockOffset), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time returns the current time from the remote server host using version 4 of
|
|
||||||
// the NTP protocol. On error, it returns the local time.
|
|
||||||
func Time(host string) (time.Time, error) {
|
|
||||||
return TimeV(host, defaultNtpVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtt(t1, t2, t3, t4 ntpTime) time.Duration {
|
|
||||||
// round trip delay time (https://tools.ietf.org/html/rfc5905#section-8)
|
|
||||||
// T1 = client send time
|
|
||||||
// T2 = server receive time
|
|
||||||
// T3 = server reply time
|
|
||||||
// T4 = client receive time
|
|
||||||
//
|
//
|
||||||
// RTT d:
|
// org = Origin Timestamp (client send time)
|
||||||
// d = (T4-T1) - (T3-T2)
|
// rec = Receive Timestamp (server receive time)
|
||||||
a := t4.Time().Sub(t1.Time())
|
// xmt = Transmit Timestamp (server reply time)
|
||||||
b := t3.Time().Sub(t2.Time())
|
// dst = Destination Timestamp (client receive time)
|
||||||
return a - b
|
|
||||||
|
func rtt(org, rec, xmt, dst ntpTime) time.Duration {
|
||||||
|
// round trip delay time
|
||||||
|
// rtt = (dst-org) - (xmt-rec)
|
||||||
|
a := dst.Time().Sub(org.Time())
|
||||||
|
b := xmt.Time().Sub(rec.Time())
|
||||||
|
rtt := a - b
|
||||||
|
if rtt < 0 {
|
||||||
|
rtt = 0
|
||||||
|
}
|
||||||
|
return rtt
|
||||||
}
|
}
|
||||||
|
|
||||||
func offset(t1, t2, t3, t4 ntpTime) time.Duration {
|
func offset(org, rec, xmt, dst ntpTime) time.Duration {
|
||||||
// local offset equation (https://tools.ietf.org/html/rfc5905#section-8)
|
// local clock offset
|
||||||
// T1 = client send time
|
// offset = ((rec-org) + (xmt-dst)) / 2
|
||||||
// T2 = server receive time
|
a := rec.Time().Sub(org.Time())
|
||||||
// T3 = server reply time
|
b := xmt.Time().Sub(dst.Time())
|
||||||
// T4 = client receive time
|
|
||||||
//
|
|
||||||
// Local clock offset t:
|
|
||||||
// t = ((T2-T1) + (T3-T4)) / 2
|
|
||||||
a := t2.Time().Sub(t1.Time())
|
|
||||||
b := t3.Time().Sub(t4.Time())
|
|
||||||
return (a + b) / time.Duration(2)
|
return (a + b) / time.Duration(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func minError(org, rec, xmt, dst ntpTime) time.Duration {
|
||||||
|
// Each NTP response contains two pairs of send/receive timestamps.
|
||||||
|
// When either pair indicates a "causality violation", we calculate the
|
||||||
|
// error as the difference in time between them. The minimum error is
|
||||||
|
// the greater of the two causality violations.
|
||||||
|
var error0, error1 ntpTime
|
||||||
|
if org >= rec {
|
||||||
|
error0 = org - rec
|
||||||
|
}
|
||||||
|
if xmt >= dst {
|
||||||
|
error1 = xmt - dst
|
||||||
|
}
|
||||||
|
if error0 > error1 {
|
||||||
|
return error0.Duration()
|
||||||
|
}
|
||||||
|
return error1.Duration()
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootDistance(rtt, rootDelay, rootDisp time.Duration) time.Duration {
|
||||||
|
// The root distance is:
|
||||||
|
// the maximum error due to all causes of the local clock
|
||||||
|
// relative to the primary server. It is defined as half the
|
||||||
|
// total delay plus total dispersion plus peer jitter.
|
||||||
|
// (https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2)
|
||||||
|
//
|
||||||
|
// In the reference implementation, it is calculated as follows:
|
||||||
|
// rootDist = max(MINDISP, rootDelay + rtt)/2 + rootDisp
|
||||||
|
// + peerDisp + PHI * (uptime - peerUptime)
|
||||||
|
// + peerJitter
|
||||||
|
// For an SNTP client which sends only a single packet, most of these
|
||||||
|
// terms are irrelevant and become 0.
|
||||||
|
totalDelay := rtt + rootDelay
|
||||||
|
return totalDelay/2 + rootDisp
|
||||||
|
}
|
||||||
|
|
||||||
func toInterval(t int8) time.Duration {
|
func toInterval(t int8) time.Duration {
|
||||||
switch {
|
switch {
|
||||||
case t > 0:
|
case t > 0:
|
||||||
|
|
20
vendor/vendor.json
vendored
20
vendor/vendor.json
vendored
|
@ -2,12 +2,6 @@
|
||||||
"comment": "",
|
"comment": "",
|
||||||
"ignore": "test",
|
"ignore": "test",
|
||||||
"package": [
|
"package": [
|
||||||
{
|
|
||||||
"checksumSHA1": "0Tugz8gj9KqqVj6JLkXUA7BXas4=",
|
|
||||||
"path": "github.com/sirupsen/logrus",
|
|
||||||
"revision": "0208149b40d863d2c1a2f8fe5753096a9cf2cc8b",
|
|
||||||
"revisionTime": "2017-02-27T12:44:09Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=",
|
"checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=",
|
||||||
"path": "github.com/alecthomas/template",
|
"path": "github.com/alecthomas/template",
|
||||||
|
@ -27,10 +21,12 @@
|
||||||
"revisionTime": "2015-10-22T06:55:26Z"
|
"revisionTime": "2015-10-22T06:55:26Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "X73SC/5YR0afYD/tq802Shxb0UI=",
|
"checksumSHA1": "OXcULIfKv/Xcsa4o1By1lY0s+AI=",
|
||||||
"path": "github.com/beevik/ntp",
|
"path": "github.com/beevik/ntp",
|
||||||
"revision": "74e5133786235ede462c83c17fa3b34653901719",
|
"revision": "802074b1b037c59dbdbee7e196ad91b51d0ec844",
|
||||||
"revisionTime": "2017-08-25T09:16:23Z"
|
"revisionTime": "2017-10-03T23:10:51Z",
|
||||||
|
"version": "v0.1.1",
|
||||||
|
"versionExact": "v0.1.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "spyv5/YFBjYyZLZa1U2LBfDR8PM=",
|
"checksumSHA1": "spyv5/YFBjYyZLZa1U2LBfDR8PM=",
|
||||||
|
@ -182,6 +178,12 @@
|
||||||
"revision": "a66a2f8b6f3fe82a95a1bed0bb3705bac8031717",
|
"revision": "a66a2f8b6f3fe82a95a1bed0bb3705bac8031717",
|
||||||
"revisionTime": "2017-06-07T19:36:46Z"
|
"revisionTime": "2017-06-07T19:36:46Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "0Tugz8gj9KqqVj6JLkXUA7BXas4=",
|
||||||
|
"path": "github.com/sirupsen/logrus",
|
||||||
|
"revision": "0208149b40d863d2c1a2f8fe5753096a9cf2cc8b",
|
||||||
|
"revisionTime": "2017-02-27T12:44:09Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "uozMgPjB4AggpuuJkGq3FgAs4CA=",
|
"checksumSHA1": "uozMgPjB4AggpuuJkGq3FgAs4CA=",
|
||||||
"path": "github.com/soundcloud/go-runit/runit",
|
"path": "github.com/soundcloud/go-runit/runit",
|
||||||
|
|
Loading…
Reference in a new issue