Skip to content

Add optional durable runtime NTP server configuration - #225

Open
elielgordondensity wants to merge 1 commit into
nerves-time:mainfrom
DensityCo:durable-ntp-servers
Open

elielgordondensity wants to merge 1 commit into
nerves-time:mainfrom
DensityCo:durable-ntp-servers

Conversation

@elielgordondensity

Copy link
Copy Markdown
Contributor

Summary

Adds an optional :servers_file config key for durably persisting the runtime NTP server list.

Today, NervesTime.set_ntp_servers/1 updates the running ntpd configuration, but the new server list only lives in GenServer state. After a reboot, NervesTime falls back to config :nerves_time, :servers. This makes it hard to support devices that receive their NTP servers dynamically (provisioning, legacy network migration, DHCP/ConnMan metadata, or user configuration).

When :servers_file is configured, the list set at runtime is written to that file and reloaded on the next boot, taking precedence over :servers:

config :nerves_time,
  servers: [],
  servers_file: "/data/nerves_time/ntp_servers"

Behavior

  • On startup, load :servers_file when configured and present.
  • Fall back to :servers when no persisted file exists.
  • set_ntp_servers/1 persists the server list when :servers_file is configured.
  • An empty list is persisted as an explicit "NTP disabled" marker so that choice survives a reboot. The presence of the file is the signal to use the persisted list, so a present-but-empty file yields [] (NTP off) rather than falling back to :servers.
  • Persistence failures are both returned (set_ntp_servers/1 now returns :ok | {:error, term()}) and logged. The running configuration is updated regardless.

Behavior is unchanged unless :servers_file is configured.

Implementation

  • New NervesTime.ServerFile module encapsulates load/save. File format is one server per line; blank lines and # comments are ignored. Intermediate directories are created on save.
  • NervesTime.Ntpd.init/1 loads from ServerFile first, falling back to :servers (then the built-in pool default).
  • set_ntp_servers/1 persists before restarting ntpd and returns the persistence result.

Tests

  • test/server_file_test.exs — unit tests for load/save round-trip, directory creation, empty-list ("NTP disabled") semantics, comment/blank-line parsing, no-op when unconfigured, and write-failure error + log.
  • test/nerves_time_test.exs — integration tests for startup precedence, fallback to :servers, persist-and-reload across an application restart, and disable-via-empty-list across a restart.

All tests pass; mix format, mix credo --strict, and mix dialyzer are clean.

@elielgordondensity
elielgordondensity marked this pull request as draft May 29, 2026 22:27
Introduce an optional `:servers_file` config key that durably persists the
runtime NTP server list. When configured, `NervesTime.set_ntp_servers/1`
writes the list to the file and it is reloaded on the next boot, taking
precedence over `:servers`. This helps devices that receive NTP servers
dynamically (provisioning, DHCP/ConnMan metadata, user configuration) keep
that choice across reboots.

* On startup, load `:servers_file` when configured and present, falling
  back to `:servers` otherwise.
* `set_ntp_servers/1` persists the server list when `:servers_file` is set
  and returns `{:error, reason}` if persistence fails.
* An empty list is persisted as an explicit "NTP disabled" marker so that
  choice also survives a reboot.

Behavior is unchanged unless `:servers_file` is configured.
@elielgordondensity
elielgordondensity marked this pull request as ready for review May 29, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants