Documentation
¶
Overview ¶
Package clockdrift collects NTP/chrony time-synchronization data from the host for emission as PCI-DSS 10.6 audit evidence.
Index ¶
Constants ¶
const (
BackendChrony = "chrony"
)
BackendName identifies a TimeSource implementation. It is exposed as a label value on the clock_drift_reference_info gauge so dashboards can distinguish backends if more than one ever ships (e.g., w32tm).
const DefaultCacheTTL = 0
DefaultCacheTTL controls result reuse between collection ticks. Zero disables caching: chronyc is invoked on every tick (typically every 15s), keeping clock drift metrics at the same cadence as all other system metrics and ensuring a lost-sync event surfaces within one tick rather than up to five minutes later.
Variables ¶
var LeapStatusValues = []LeapStatus{ LeapNormal, LeapInsertSecond, LeapDeleteSecond, LeapNotSynchronised, LeapUnknown, }
LeapStatusValues lists every well-known LeapStatus, in dashboard display order. It is exported so callers (e.g., the Prometheus sender) can iterate without hardcoding the set in two places.
Functions ¶
This section is empty.
Types ¶
type ChronyBackend ¶
type ChronyBackend struct {
// contains filtered or unexported fields
}
ChronyBackend implements TimeSource by executing `chronyc tracking`.
Behavior:
- exec is wrapped in the caller-supplied context (5s timeout in production) so a wedged chronyd cannot stall the metrics agent.
- Locale and timezone are pinned via LANG=C, LC_ALL=C, TZ=UTC so a reconfigured stemcell cannot accidentally rename chrony's field keys out from under the parser.
- Successful results are cached for cacheTTL to avoid forking chronyc more often than the audit cadence demands.
func NewChronyBackend ¶
func NewChronyBackend(opts ...ChronyOption) *ChronyBackend
NewChronyBackend constructs a ChronyBackend with sensible defaults that can be selectively overridden via options.
func (*ChronyBackend) Collect ¶
func (c *ChronyBackend) Collect(ctx context.Context) (*TimeSyncData, error)
Collect returns the latest TimeSyncData, honoring the supplied context's deadline. The result may come from an in-memory cache if the previous call succeeded within cacheTTL.
func (*ChronyBackend) Name ¶
func (c *ChronyBackend) Name() string
Name returns the backend identifier ("chrony").
type ChronyOption ¶
type ChronyOption func(*ChronyBackend)
ChronyOption configures a ChronyBackend at construction time.
func WithCacheTTL ¶
func WithCacheTTL(d time.Duration) ChronyOption
WithCacheTTL overrides the duration for which a successful snapshot is reused. A non-positive value disables caching and forces every Collect call to invoke chronyc.
func WithCmdRunner ¶
func WithCmdRunner(r func(ctx context.Context) (string, error)) ChronyOption
WithCmdRunner overrides the function used to execute `chronyc tracking`. Tests inject a fake to return canned output without forking a process.
func WithLogger ¶
func WithLogger(l *log.Logger) ChronyOption
WithLogger sets the logger used for non-fatal parse warnings. If unset, warnings are silently discarded so the package never spams the agent log.
type LeapStatus ¶
type LeapStatus string
LeapStatus is the chrony-reported leap-second state.
const ( LeapNormal LeapStatus = "Normal" LeapInsertSecond LeapStatus = "Insert second" LeapDeleteSecond LeapStatus = "Delete second" LeapNotSynchronised LeapStatus = "Not synchronised" LeapUnknown LeapStatus = "unknown" )
func ParseLeapStatus ¶
func ParseLeapStatus(s string) LeapStatus
ParseLeapStatus maps a chronyc leap-status string to a typed LeapStatus. Matching is case-insensitive and accepts both British ("Not synchronised") and American ("Not synchronized") spellings so a future chrony release that normalizes the spelling cannot silently drop the unsync signal.
type TimeDirection ¶
type TimeDirection string
TimeDirection describes whether the local clock is running slow or fast relative to the upstream NTP source.
const ( DirectionSlow TimeDirection = "slow" DirectionFast TimeDirection = "fast" DirectionUnknown TimeDirection = "unknown" )
func ParseTimeDirection ¶
func ParseTimeDirection(s string) TimeDirection
ParseTimeDirection maps a chronyc direction word to a typed TimeDirection. Unknown inputs (including empty string and uppercase variants) return DirectionUnknown rather than panicking.
type TimeSource ¶
type TimeSource interface {
// Name returns the backend identifier (see BackendChrony).
Name() string
// Collect returns the latest time-synchronization snapshot or an error.
// A nil error and non-nil *TimeSyncData indicates a successful read; the
// caller may still observe NaN values on individual fields when the
// underlying tool emitted unparseable output for that field.
Collect(ctx context.Context) (*TimeSyncData, error)
}
TimeSource collects time-synchronization data from the system.
Implementations are NOT required to be safe for concurrent use; the Collector calls Collect sequentially from a single goroutine. Collect MUST honor the supplied context's deadline/cancellation so a wedged time daemon cannot stall the broader metrics collection cycle.
type TimeSyncData ¶
type TimeSyncData struct {
ReferenceID string `json:"reference_id"`
ReferenceHost string `json:"reference_host"`
Stratum *int `json:"stratum,omitempty"`
RefTimeUTC string `json:"ref_time_utc"`
SystemTimeOffsetSec float64 `json:"system_time_offset_sec"`
SystemTimeDirection TimeDirection `json:"system_time_direction"`
LastOffsetSec float64 `json:"last_offset_sec"`
FrequencyPPM float64 `json:"frequency_ppm"`
FrequencyDirection TimeDirection `json:"frequency_direction"`
RootDelaySec float64 `json:"root_delay_sec"`
LeapStatus LeapStatus `json:"leap_status"`
}
TimeSyncData is one snapshot of the local clock's NTP-sync state.
Float fields use math.NaN() as a sentinel for "the underlying tool emitted a value we could not parse", which the sender translates to a missing gauge data point rather than a misleading zero. Stratum uses *int with nil signaling missing/invalid (NTP stratum 0 is a valid sentinel meaning "unspecified" and we must not collide with it). RefTimeUnixSec uses *float64 because float zero would render as Jan 1 1970 UTC on dashboards.
Sign convention for offset and PPM fields: when the chrony direction word is "slow" the parser stores the value as negative; "fast" stays positive. The companion Direction enum preserves the original word so consumers can disambiguate without relying on the sign.