Skip to content

Commit

Permalink
add simple chrony object
Browse files Browse the repository at this point in the history
  • Loading branch information
jreidinger committed Nov 2, 2017
1 parent dbb257f commit d0cfa16
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
76 changes: 76 additions & 0 deletions src/lib/cfa/chrony_conf.rb
@@ -0,0 +1,76 @@
require "cfa/base_model"
require "cfa/augeas_parser"
require "cfa/matcher"
require "cfa/placer"

module CFA
# class representings /etc/chrony.conf file model. It provides helper to manipulate
# with file. It uses CFA framework and Augeas parser.
# @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/BaseModel
# @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/AugeasParser
#
class ChronyConf < ::CFA::BaseModel
PATH = "/etc/chrony.conf".freeze

def initialize(file_handler: nil)
super(CFA::AugeasParser.new("chrony.lns"), PATH, file_handler: file_handler)
end

def load
super
fix_collection_names(data)
end

# options hash with key value, where value can be nil for keyword options
def add_pool(address, options)
# if there is already pool entry, place it after, if not, try use comment
existing_pools = pure_pools
if existing_pools.empty?
# for now first chrony have pools under comment mentioning pool.ntp.org
# so try to place it below
matcher = Matcher.new { |k,v| k.start_with?("#comment") && v =~ /www\.pool\.ntp\.org/ }
else
# place after the last pool available
matcher = Matcher.new(key: existing_pools.last[:key],
value_matcher: existing_pools.last[:value])
end
placer = AfterPlacer.new(matcher)

key = "pool[]"
value = AugeasTreeValue.new(AugeasTree.new, address)
options.each_pair do |k, v|
value.tree[k] = v
end

data.add(key, value, placer)
end

# delete all pools defined
def clear_pools
data.delete(POOLS_MATCHER)
end

private

COLLECTION_KEYS = [
"pool"
]
# if there is only one element of collection, augeas does not add [],
# so fix it here for known keys which we modify ( and can be hitted with it )
def fix_collection_names(tree)
tree.data.each do |entry|
entry[:key] << "[]" if COLLECTION_KEYS.include?(entry[:key])
fix_collection_names(entry[:value].tree) if entry[:value].is_a?(AugeasTreeValue)
fix_collection_names(entry[:value]) if entry[:value].is_a?(AugeasTree)
end
end

POOLS_MATCHER = Matcher.new(collection: "pool")

# list of pools in internal data structure
def pure_pools
data.select(POOLS_MATCHER)
end

end
end
101 changes: 101 additions & 0 deletions test/cfa/chrony_conf_test.rb
@@ -0,0 +1,101 @@
require_relative "../test_helper"
require "cfa/memory_file"
require "cfa/chrony_conf"

def ntp_disk_content
path = File.expand_path("../../fixtures/cfa/chrony.conf.original", __FILE__)
File.read(path)
end

def ntp_file(content)
CFA::MemoryFile.new(content)
end

def ntp_conf(file)
CFA::ChronyConf.new(file_handler: file)
end

describe CFA::ChronyConf do
subject(:chrony) { ntp_conf(file) }

let(:file) { ntp_file(content) }

before do
subject.load
end

describe "#clear_pools" do
let(:content) do
"pool 2.opensuse.pool.ntp.org iburst\npool pool.ntp.org offline\n"
end

it "removes all pools from file" do
subject.clear_pools
subject.save
expect(file.content).to_not match(/pool/)
end
end

describe "#add_pool" do
context "there is already pool" do
let(:content) do
"# Use public servers from the pool.ntp.org project.\n"\
"# Please consider joining the pool (http://www.pool.ntp.org/join.html).\n" \
"pool 2.opensuse.pool.ntp.org iburst\n"\
"rtcsync\n"
end

let(:expected_output) do
"# Use public servers from the pool.ntp.org project.\n"\
"# Please consider joining the pool (http://www.pool.ntp.org/join.html).\n" \
"pool 2.opensuse.pool.ntp.org iburst\n"\
"pool ntp.suse.cz offline\n"\
"rtcsync\n"
end

it "adds new pool after the latest pool entry" do
subject.add_pool("ntp.suse.cz", "offline" => nil)
subject.save
expect(file.content).to eq expected_output
end
end

context "there is pool comments" do
let(:content) do
"# Use public servers from the pool.ntp.org project.\n"\
"# Please consider joining the pool (http://www.pool.ntp.org/join.html).\n" \
"rtcsync\n"
end

let(:expected_output) do
"# Use public servers from the pool.ntp.org project.\n"\
"# Please consider joining the pool (http://www.pool.ntp.org/join.html).\n" \
"pool ntp.suse.cz offline\n"\
"rtcsync\n"
end

it "adds new pool after the comment" do
subject.add_pool("ntp.suse.cz", "offline" => nil)
subject.save
expect(file.content).to eq expected_output
end
end

context "otherwise" do
let(:content) do
"rtcsync\n"
end

let(:expected_output) do
"rtcsync\n" \
"pool ntp.suse.cz offline\n"
end

it "adds new pool at the end" do
subject.add_pool("ntp.suse.cz", "offline" => nil)
subject.save
expect(file.content).to eq expected_output
end
end
end
end
38 changes: 38 additions & 0 deletions test/data/scr_root/etc/chrony.conf.original
@@ -0,0 +1,38 @@
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
pool 2.opensuse.pool.ntp.org iburst

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3

# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

# Enable hardware timestamping on all interfaces that support it.
#hwtimestamp *

# Increase the minimum number of selectable sources required to adjust
# the system clock.
#minsources 2

# Allow NTP client access from local network.
#allow 192.168.0.0/16

# Serve time even if not synchronized to a time source.
#local stratum 10

# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys

# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC

# Specify directory for log files.
logdir /var/log/chrony

# Select which information is logged.
#log measurements statistics tracking

0 comments on commit d0cfa16

Please sign in to comment.