diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fb545b..f0c1c31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,3 @@ ---- name: ci "on": @@ -24,15 +23,18 @@ jobs: strategy: matrix: os: - - "centos-7" - - "centos-8" - - "debian-9" - - "debian-10" - - "ubuntu-1804" - - "ubuntu-2004" + - "almalinux-9" + - "amazonlinux-2023" + - "centos-stream-9" + - "debian-12" + - "fedora-latest" + - "oraclelinux-9" + - "rockylinux-9" + - "ubuntu-22.04" + - "ubuntu-24.04" suite: - - "client" - - "master" + - "default" + - "server" fail-fast: false steps: @@ -41,10 +43,8 @@ jobs: - name: Install Chef uses: actionshub/chef-install@main - name: Dokken - uses: actionshub/test-kitchen@main env: CHEF_LICENSE: accept-no-persist KITCHEN_LOCAL_YAML: kitchen.dokken.yml - with: - suite: ${{ matrix.suite }} - os: ${{ matrix.os }} + INSTANCE_NAME: ${{ matrix.suite }}-${{ matrix.os }} + run: kitchen test "${INSTANCE_NAME//./}" diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 6fa8e77..e42be9b 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -3,3 +3,8 @@ config: line-length: false # MD013 no-duplicate-heading: false # MD024 reference-links-images: false # MD052 + no-multiple-blanks: + maximum: 2 +ignores: + - .github/copilot-instructions.md + - .windsurf/** diff --git a/.mdlrc b/.mdlrc index 9cc2c63..ebeef65 100644 --- a/.mdlrc +++ b/.mdlrc @@ -1 +1 @@ -rules "~MD013", "~MD024", "~MD025" +rules "~MD013", "~MD024" diff --git a/LIMITATIONS.md b/LIMITATIONS.md new file mode 100644 index 0000000..a9aba0c --- /dev/null +++ b/LIMITATIONS.md @@ -0,0 +1,28 @@ +# Limitations + +## Package Availability + +### APT (Debian/Ubuntu) + +- Debian 12: `chrony` is available from the standard Debian 12 repositories for amd64, arm64, armel, armhf, i386, mips64el, ppc64el, riscv64, and s390x. +- Ubuntu 22.04 and 24.04: `chrony` is available from the standard Ubuntu repositories for amd64, arm64, armhf, ppc64el, riscv64, and s390x. + +### DNF/YUM (RHEL family) + +- RHEL 9, AlmaLinux 9, Rocky Linux 9, Oracle Linux 9, and CentOS Stream 9 provide `chrony` from their standard repositories. +- Amazon Linux 2023 provides `chrony` from the standard `dnf` repositories. +- Fedora latest provides `chrony` from the standard Fedora repositories. + +## Architecture Limitations + +- No cookbook-specific architecture restriction is currently known for supported platforms. +- The common deployment targets across the tested platforms are x86_64 and aarch64. + +## Source/Compiled Installation + +Chrony is installed from OS packages on all supported platforms in this cookbook. No source build path is implemented or required. + +## Known Issues + +- This cookbook does not manage vendor-specific repository setup because chrony is installed from the base OS repositories on supported platforms. +- Platform support in `metadata.rb` should stay aligned with `kitchen.yml`, `kitchen.dokken.yml`, and `kitchen.global.yml`. diff --git a/README.md b/README.md index 451b4ab..215f016 100644 --- a/README.md +++ b/README.md @@ -6,45 +6,50 @@ [![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors) [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) -Configures the time synchronization application `chrony` as a client or master timeserver, maintaining the accuracy of the system clock (similar to NTP). Isolated networks are supported as well. +Configures the time synchronization application `chrony` as a client or server timeserver, maintaining the accuracy of the system clock (similar to NTP). ## Maintainers -This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you’d like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF). +This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you'd like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF). ## Requirements ### Platforms -- Debian / Ubuntu -- CentOS / Redhat +- Debian 12+ +- Ubuntu 22.04+ +- RHEL / AlmaLinux / Rocky / Oracle Linux 9+ +- CentOS Stream 9+ +- Amazon Linux 2023 +- Fedora (latest) ### Chef -- Chef 13+ +- Chef >= 15.3 -## Recipes +## Resources -### client - -Configures the node to use the `chrony` application to keep the node's clock synced. If there is a node using the `chrony::master` recipe, the client will attempt to sync with it, unless disabled via `['chrony']['search_masters']`. If there is not an available master, the attribute list `['chrony'][:servers]` is used (defaults are `[0-3].debian.pool.ntp.org`). If there is a master node, the `['chrony'][:allowed]` will be set to allow for syncing with the master. - -### default - -The default recipe passes through to the client recipe. - -### master - -The node will use the `chrony` application to provide time to nodes using the `chrony::client` recipe. The master sets its own time against the attribute list `['chrony'][:servers]` (defaults are `[0-3].debian.pool.ntp.org`). Access to this master is restricted by the `['chrony'][:allowed]` attribute set in the recipe (default is to the `x.y.*` subnet). +This cookbook provides the `chrony_config` custom resource. See [documentation/chrony_config.md](documentation/chrony_config.md) for full details. ## Usage -Nodes using the `chrony::client` recipe will attempt to sync time with nodes using the `chrony::master` recipe. If there are no `chrony::master` nodes found, the contents of the attribute list `['chrony'][:servers]` are used (defaults are `[0-3].debian.pool.ntp.org`). - -The current configurations are supported: - -1) Clients with direct NTP server access -2) A master with direct NTP server access with clients pointing to it +```ruby +chrony_config 'default' do + servers({ 'pool.ntp.org' => 'iburst' }) +end +``` + +### Server configuration + +```ruby +chrony_config 'server' do + servers({ + 'ntp1.example.com' => 'iburst', + 'ntp2.example.com' => 'iburst', + }) + allow ['192.168.1.0/24'] +end +``` ## Contributors diff --git a/attributes/default.rb b/attributes/default.rb deleted file mode 100644 index 36c163e..0000000 --- a/attributes/default.rb +++ /dev/null @@ -1,45 +0,0 @@ -# -# Author:: Matt Ray -# Contributor:: Dang H. Nguyen -# Contributor:: Lance Albertson -# Cookbook:: chrony -# Attributes:: default -# Copyright:: 2011-2020, Chef Software, Inc. -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# hash of default servers in the chrony.conf from Ubuntu -default['chrony']['servers'] = { - 'pool.ntp.org' => 'iburst', -} - -default['chrony']['server_options'] = 'offline minpoll 8' - -# Use servers including recipe chrony::master registered in Chef server -# to populate our servers list. -default['chrony']['search_masters'] = true - -# set in the client & master recipes -# for better security, clients that do not need to serve ntp requests to peers or other clients -# should not have the `allow` directive in chrony.conf -# In this case, an empty array is the correct setting -# See https://chrony.tuxfamily.org/faq.html#_how_can_i_make_code_chronyd_code_more_secure -default['chrony']['allow'] = [] - -default['chrony']['log_dir'] = '/var/log/chrony' -default['chrony']['driftfile'] = '/var/lib/chrony/drift' - -# Extra configuration values to be added directly to chrony.conf -default['chrony']['extra_config'] = [] diff --git a/documentation/chrony_config.md b/documentation/chrony_config.md new file mode 100644 index 0000000..5c4141d --- /dev/null +++ b/documentation/chrony_config.md @@ -0,0 +1,45 @@ +# chrony_config + +The `chrony_config` resource manages the chrony package, service, and configuration file. + +## Actions + +| Action | Description | +|---|---| +| `:create` | Installs the chrony package, manages the configuration file, and enables and starts the service. Default action. | +| `:delete` | Stops and disables the service, removes the package, and deletes the configuration file. | + +## Properties + +| Property | Type | Default | Description | +|---|---|---|---| +| `servers` | Hash | `{ 'pool.ntp.org' => 'iburst' }` | NTP servers to configure, keyed by hostname with option strings as values. | +| `pools` | Hash | `{}` | NTP pools to configure, keyed by hostname with option strings as values. | +| `allow` | Array | `[]` | Networks or hosts allowed to query the local chrony server. | +| `deny` | Array | `[]` | Networks or hosts denied access to the local chrony server. | +| `driftfile` | String | platform-specific | Drift file path. Defaults to `/var/lib/chrony/drift` on RHEL family and `/var/lib/chrony/chrony.drift` on Debian family. | +| `log_dir` | String | `'/var/log/chrony'` | Directory used for chrony log output. | +| `extra_config` | Array | `[]` | Raw configuration lines appended to the generated chrony configuration file. | + +## Examples + +### Basic usage + +```ruby +chrony_config 'default' do + action :create +end +``` + +### Server configuration + +```ruby +chrony_config 'server' do + servers( + 'ntp1.example.com' => 'iburst', + 'ntp2.example.com' => 'iburst' + ) + allow ['192.168.1.0/24'] + action :create +end +``` diff --git a/kitchen.dokken.yml b/kitchen.dokken.yml index 47eff95..61ec21c 100644 --- a/kitchen.dokken.yml +++ b/kitchen.dokken.yml @@ -7,11 +7,6 @@ transport: { name: dokken } provisioner: { name: dokken } platforms: - - name: almalinux-8 - driver: - image: dokken/almalinux-8 - pid_one_command: /usr/lib/systemd/systemd - - name: almalinux-9 driver: image: dokken/almalinux-9 @@ -22,36 +17,11 @@ platforms: image: dokken/amazonlinux-2023 pid_one_command: /usr/lib/systemd/systemd - - name: centos-7 - driver: - image: dokken/centos-7 - pid_one_command: /usr/lib/systemd/systemd - - - name: centos-stream-8 - driver: - image: dokken/centos-stream-8 - pid_one_command: /usr/lib/systemd/systemd - - name: centos-stream-9 driver: image: dokken/centos-stream-9 pid_one_command: /usr/lib/systemd/systemd - - name: debian-9 - driver: - image: dokken/debian-9 - pid_one_command: /bin/systemd - - - name: debian-10 - driver: - image: dokken/debian-10 - pid_one_command: /bin/systemd - - - name: debian-11 - driver: - image: dokken/debian-11 - pid_one_command: /bin/systemd - - name: debian-12 driver: image: dokken/debian-12 @@ -62,52 +32,36 @@ platforms: image: dokken/fedora-latest pid_one_command: /usr/lib/systemd/systemd - - name: opensuse-leap-15 - driver: - image: dokken/opensuse-leap-15 - pid_one_command: /usr/lib/systemd/systemd - - - name: oraclelinux-7 - driver: - image: dokken/oraclelinux-7 - pid_one_command: /usr/lib/systemd/systemd - - - name: oraclelinux-8 - driver: - image: dokken/oraclelinux-8 - pid_one_command: /usr/lib/systemd/systemd - - name: oraclelinux-9 driver: image: dokken/oraclelinux-9 pid_one_command: /usr/lib/systemd/systemd - - name: rockylinux-8 - driver: - image: dokken/rockylinux-8 - pid_one_command: /usr/lib/systemd/systemd - - name: rockylinux-9 driver: image: dokken/rockylinux-9 pid_one_command: /usr/lib/systemd/systemd - - name: ubuntu-18.04 - driver: - image: dokken/ubuntu-18.04 - pid_one_command: /bin/systemd - - - name: ubuntu-20.04 - driver: - image: dokken/ubuntu-20.04 - pid_one_command: /bin/systemd - - name: ubuntu-22.04 driver: image: dokken/ubuntu-22.04 pid_one_command: /bin/systemd - - name: ubuntu-23.04 + - name: ubuntu-24.04 driver: - image: dokken/ubuntu-23.04 + image: dokken/ubuntu-24.04 pid_one_command: /bin/systemd + +suites: + - name: default + run_list: + - recipe[test::default] + verifier: + inspec_tests: + - path: test/integration/default + - name: server + run_list: + - recipe[test::server] + verifier: + inspec_tests: + - path: test/integration/server diff --git a/kitchen.global.yml b/kitchen.global.yml index a382fcd..976f5ed 100644 --- a/kitchen.global.yml +++ b/kitchen.global.yml @@ -15,24 +15,12 @@ verifier: name: inspec platforms: - - name: almalinux-8 - name: almalinux-9 - name: amazonlinux-2023 - - name: centos-7 - - name: centos-stream-8 - name: centos-stream-9 - - name: debian-9 - - name: debian-10 - - name: debian-11 - name: debian-12 - name: fedora-latest - - name: opensuse-leap-15 - - name: oraclelinux-7 - - name: oraclelinux-8 - name: oraclelinux-9 - - name: rockylinux-8 - name: rockylinux-9 - - name: ubuntu-18.04 - - name: ubuntu-20.04 - name: ubuntu-22.04 - - name: ubuntu-23.04 + - name: ubuntu-24.04 diff --git a/kitchen.yml b/kitchen.yml index bd41b0a..113678b 100644 --- a/kitchen.yml +++ b/kitchen.yml @@ -10,31 +10,28 @@ provisioner: verifier: name: inspec - inspec_tests: - - path: test/integration/inspec platforms: - - name: centos-7 - - name: centos-8 - - name: debian-9 - - name: debian-10 - - name: ubuntu-18.04 - - name: ubuntu-20.04 + - name: almalinux-9 + - name: amazonlinux-2023 + - name: centos-stream-9 + - name: debian-12 + - name: fedora-latest + - name: oraclelinux-9 + - name: rockylinux-9 + - name: ubuntu-22.04 + - name: ubuntu-24.04 suites: - - name: client + - name: default run_list: - - recipe[test] - - recipe[chrony::client] - attributes: - chrony: - extra_config: - - "log measurements statistics tracking" + - recipe[test::default] verifier: - - name: master + inspec_tests: + - path: test/integration/default + - name: server run_list: - - recipe[test] - - recipe[chrony::master] + - recipe[test::server] verifier: - inputs: - type: "master" + inspec_tests: + - path: test/integration/server diff --git a/libraries/helpers.rb b/libraries/helpers.rb index 28b74c8..44a5a38 100644 --- a/libraries/helpers.rb +++ b/libraries/helpers.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # # Cookbook:: chrony # Libraries:: helpers @@ -40,5 +41,3 @@ def chrony_conf_file end end end -Chef::DSL::Recipe.include Chrony::Cookbook::Helpers -Chef::Resource.include Chrony::Cookbook::Helpers diff --git a/metadata.rb b/metadata.rb index fe53177..f0fc204 100644 --- a/metadata.rb +++ b/metadata.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true name 'chrony' maintainer 'Sous Chefs' maintainer_email 'help@sous-chefs.org' @@ -6,9 +7,14 @@ version '1.2.7' issues_url 'https://github.com/sous-chefs/chrony/issues' source_url 'https://github.com/sous-chefs/chrony' -chef_version '>= 13.0' +chef_version '>= 15.3' -supports 'debian' -supports 'ubuntu' -supports 'redhat', '>= 7.0' -supports 'centos', '>= 7.0' +supports 'debian', '>= 12.0' +supports 'ubuntu', '>= 22.04' +supports 'centos_stream', '>= 9.0' +supports 'fedora' +supports 'oracle', '>= 9.0' +supports 'redhat', '>= 9.0' +supports 'rocky', '>= 9.0' +supports 'almalinux', '>= 9.0' +supports 'amazon', '>= 2023.0' diff --git a/recipes/client.rb b/recipes/client.rb deleted file mode 100644 index 4f4e2a0..0000000 --- a/recipes/client.rb +++ /dev/null @@ -1,56 +0,0 @@ -# -# Author:: Matt Ray -# Contributor:: Dang H. Nguyen -# Contributor:: Lance Albertson -# Cookbook:: chrony -# Recipe:: client -# Copyright:: 2011-2020, Chef Software, Inc. -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -package 'chrony' - -# search for the chrony master(s), if found populate the template accordingly -# typical deployment will only have 1 master, but still allow for multiple -masters = search(:node, 'recipes:chrony\:\:master') if node['chrony']['search_masters'] -masters ||= [] -if masters.empty? - Chef::Log.info("No chrony master(s) found, using node['chrony']['servers'] attribute.") -else - node.default['chrony']['servers'] = {} - masters.each do |master| - node.default['chrony']['servers'][master['ipaddress']] = master['chrony']['server_options'] - node.default['chrony']['allow'].push master['ipaddress'] - end -end - -template 'chrony.conf' do - path chrony_conf_file - source 'chrony_client.conf.erb' - owner 'root' - group 'root' - mode '0644' - variables allow: node['chrony']['allow'], - driftfile: node['chrony']['driftfile'], - log_dir: node['chrony']['log_dir'], - servers: node['chrony']['servers'] - notifies :restart, 'service[chrony]', :delayed -end - -service 'chrony' do - service_name chrony_service_name - supports restart: true, status: true, reload: true - action %i(start enable) -end diff --git a/recipes/default.rb b/recipes/default.rb deleted file mode 100644 index 940e6d4..0000000 --- a/recipes/default.rb +++ /dev/null @@ -1,21 +0,0 @@ -# -# Author:: Matt Ray -# Cookbook:: chrony -# Recipe:: default -# Copyright:: 2011-2019, Chef Software, Inc. -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -include_recipe 'chrony::client' diff --git a/recipes/master.rb b/recipes/master.rb deleted file mode 100644 index db18cbb..0000000 --- a/recipes/master.rb +++ /dev/null @@ -1,46 +0,0 @@ -# -# Author:: Matt Ray -# Contributor:: Lance Albertson -# Cookbook:: chrony -# Recipe:: master -# Copyright:: 2011-2020, Chef Software, Inc. -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -package 'chrony' - -service 'chrony' do - service_name chrony_service_name - supports restart: true, status: true, reload: true - action %i(start enable) -end - -# set the allowed hosts to the subnet -# ip = node.ipaddress.split('.') -# set the allowed hosts to the class B -# node['chrony'][:allow] = ["allow #{ip[0]}.#{ip[1]}"] - -template 'chrony.conf' do - path chrony_conf_file - source 'chrony_master.conf.erb' - owner 'root' - group 'root' - mode '0644' - variables allow: node['chrony']['allow'], - driftfile: node['chrony']['driftfile'], - log_dir: node['chrony']['log_dir'], - servers: node['chrony']['servers'] - notifies :restart, 'service[chrony]' -end diff --git a/resources/config.rb b/resources/config.rb new file mode 100644 index 0000000..b5c22c2 --- /dev/null +++ b/resources/config.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +# +# Cookbook:: chrony +# Resource:: config +# +# Copyright:: 2020, Sous Chefs +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +unified_mode true +provides :chrony_config + +property :servers, Hash, default: { 'pool.ntp.org' => 'iburst' } +property :pools, Hash, default: {} +property :allow, Array, default: [] +property :deny, Array, default: [] +property :driftfile, String, default: lazy { + platform_family?('rhel', 'fedora', 'amazon') ? '/var/lib/chrony/drift' : '/var/lib/chrony/chrony.drift' +} +property :log_dir, String, default: '/var/log/chrony' +property :extra_config, Array, default: [] + +action :create do + package 'chrony' + + template chrony_conf_file do + source 'chrony.conf.erb' + cookbook 'chrony' + owner 'root' + group 'root' + mode '0644' + variables( + servers: new_resource.servers, + pools: new_resource.pools, + allow: new_resource.allow, + deny: new_resource.deny, + driftfile: new_resource.driftfile, + log_dir: new_resource.log_dir, + extra_config: new_resource.extra_config + ) + notifies :restart, "service[#{chrony_service_name}]", :delayed + end + + service chrony_service_name do + supports restart: true, status: true, reload: true + action [:enable, :start] + end +end + +action :delete do + service chrony_service_name do + action [:stop, :disable] + end + + package 'chrony' do + action :remove + end + + file chrony_conf_file do + action :delete + end +end + +action_class do + include Chrony::Cookbook::Helpers +end diff --git a/spec/shared_examples.rb b/spec/shared_examples.rb index e598436..c520fc8 100644 --- a/spec/shared_examples.rb +++ b/spec/shared_examples.rb @@ -1,14 +1,19 @@ -shared_examples 'chrony client' do - it 'installed chrony' do +# frozen_string_literal: true + +shared_examples 'chrony_config :create' do + it 'installs the chrony package' do expect(chef_run).to install_package('chrony') end - it 'started and enabled the chrony(d) service' do - expect(chef_run).to start_service('chrony') - expect(chef_run).to enable_service('chrony') + it 'creates the chrony configuration file' do + expect(chef_run).to create_template(conf_file) + end + + it 'enables the chrony service' do + expect(chef_run).to enable_service(service_name) end - it 'created chrony.conf' do - expect(chef_run).to create_template('chrony.conf') + it 'starts the chrony service' do + expect(chef_run).to start_service(service_name) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 43b918a..a5db647 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'chefspec' require 'chefspec/berkshelf' require_relative 'shared_examples' RSpec.configure do |config| - config.color = true # Use color in STDOUT - config.formatter = :documentation # Use the specified formatter - config.log_level = :error # Avoid deprecation notice SPAM + config.color = true + config.formatter = :documentation + config.log_level = :error end diff --git a/spec/unit/recipes/client_spec.rb b/spec/unit/recipes/client_spec.rb deleted file mode 100644 index 936ad03..0000000 --- a/spec/unit/recipes/client_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# -# Cookbook:: chrony -# Spec:: client -# -# Author: Dang H. Nguyen -# Contributor:: Lance Albertson -# -# Copyright:: 2020, The Walt Disney Company, All Rights Reserved -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -require 'spec_helper' - -describe 'chrony::client' do - context 'on Ubuntu 18.04' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '18.04').converge(described_recipe) - end - - it_behaves_like 'chrony client' - - it 'rendered /etc/chrony.conf' do - expect(chef_run).to render_file('/etc/chrony/chrony.conf').with_content(/pool pool.ntp.org iburst/) - expect(chef_run).to_not render_file('/etc/chrony/chrony.conf').with_content('allow') - end - end - - context 'on CentOS 7' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'centos', version: '7').converge(described_recipe) - end - - it_behaves_like 'chrony client' - - it 'rendered /etc/chrony.conf' do - expect(chef_run).to render_file('/etc/chrony.conf').with_content(/pool pool.ntp.org iburst/) - expect(chef_run).to_not render_file('/etc/chrony/chrony.conf').with_content('allow') - end - end -end diff --git a/spec/unit/recipes/default_spec.rb b/spec/unit/recipes/default_spec.rb deleted file mode 100644 index 907478c..0000000 --- a/spec/unit/recipes/default_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# -# Cookbook:: chrony -# Spec:: default -# -# Author: Dang H. Nguyen -# Contributor:: Lance Albertson -# -# Copyright:: 2020, The Walt Disney Company, All Rights Reserved -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'spec_helper' - -describe 'chrony::default' do - context 'When all attributes are default' do - context 'on Ubuntu 18.04' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '18.04').converge(described_recipe) - end - - it 'included the client recipe' do - expect(chef_run).to include_recipe('chrony::client') - end - end - - context 'on CentOS 7' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'centos', version: '7').converge(described_recipe) - end - - it 'included the client recipe' do - expect(chef_run).to include_recipe('chrony::client') - end - end - end -end diff --git a/spec/unit/recipes/master_spec.rb b/spec/unit/recipes/master_spec.rb deleted file mode 100644 index ce7d813..0000000 --- a/spec/unit/recipes/master_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# -# Cookbook:: chrony -# Spec:: client -# -# Author: Dang H. Nguyen -# Contributor:: Lance Albertson -# -# Copyright:: 2020, The Walt Disney Company, All Rights Reserved -# Copyright:: 2020, Sous Chefs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'spec_helper' - -describe 'chrony::master' do - context 'on Ubuntu 18.04' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '18.04').converge(described_recipe) - end - - it_behaves_like 'chrony client' - - it 'rendered /etc/chrony/chrony.conf' do - expect(chef_run).to render_file('/etc/chrony/chrony.conf').with_content(/server pool.ntp.org iburst/) - expect(chef_run).to render_file('/etc/chrony/chrony.conf').with_content('allow') - end - end - - context 'on CentOS 7' do - cached(:chef_run) do - ChefSpec::ServerRunner.new(platform: 'centos', version: '7').converge(described_recipe) - end - - it_behaves_like 'chrony client' - - it 'rendered /etc/chrony.conf' do - expect(chef_run).to render_file('/etc/chrony.conf').with_content(/server pool.ntp.org iburst/) - expect(chef_run).to render_file('/etc/chrony.conf').with_content('allow') - end - end -end diff --git a/spec/unit/resources/config_spec.rb b/spec/unit/resources/config_spec.rb new file mode 100644 index 0000000..90477c3 --- /dev/null +++ b/spec/unit/resources/config_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'chrony_config' do + context ':create action' do + context 'on AlmaLinux 9 (RHEL family)' do + cached(:chef_run) do + ChefSpec::SoloRunner.new( + platform: 'almalinux', version: '9', + step_into: ['chrony_config'] + ) do |_node| + end.converge('test::default') + end + + let(:conf_file) { '/etc/chrony.conf' } + let(:service_name) { 'chronyd' } + + include_examples 'chrony_config :create' + + it 'renders the config with default servers' do + expect(chef_run).to render_file('/etc/chrony.conf') + .with_content(/server pool\.ntp\.org iburst/) + end + + it 'uses RHEL driftfile path' do + expect(chef_run).to render_file('/etc/chrony.conf') + .with_content(%r{driftfile /var/lib/chrony/drift$}) + end + + it 'does not include allow directives for client config' do + expect(chef_run).not_to render_file('/etc/chrony.conf') + .with_content(/^allow/) + end + + it 'includes extra_config lines' do + expect(chef_run).to render_file('/etc/chrony.conf') + .with_content(/^log measurements statistics tracking$/) + end + end + + context 'on Ubuntu 24.04 (Debian family)' do + cached(:chef_run) do + ChefSpec::SoloRunner.new( + platform: 'ubuntu', version: '24.04', + step_into: ['chrony_config'] + ).converge('test::default') + end + + let(:conf_file) { '/etc/chrony/chrony.conf' } + let(:service_name) { 'chrony' } + + include_examples 'chrony_config :create' + + it 'uses Debian driftfile path' do + expect(chef_run).to render_file('/etc/chrony/chrony.conf') + .with_content(%r{driftfile /var/lib/chrony/chrony\.drift}) + end + end + + context 'with server configuration (allow directives)' do + cached(:chef_run) do + ChefSpec::SoloRunner.new( + platform: 'almalinux', version: '9', + step_into: ['chrony_config'] + ).converge('test::server') + end + + it 'renders allow directives' do + expect(chef_run).to render_file('/etc/chrony.conf') + .with_content(%r{^allow 10\.0\.0\.0/8}) + end + end + end + + context ':delete action' do + cached(:chef_run) do + ChefSpec::SoloRunner.new( + platform: 'almalinux', version: '9', + step_into: ['chrony_config'] + ).converge('test::delete') + end + + it 'stops the chrony service' do + expect(chef_run).to stop_service('chronyd') + end + + it 'disables the chrony service' do + expect(chef_run).to disable_service('chronyd') + end + + it 'removes the chrony package' do + expect(chef_run).to remove_package('chrony') + end + + it 'deletes the config file' do + expect(chef_run).to delete_file('/etc/chrony.conf') + end + end +end diff --git a/templates/chrony.conf.erb b/templates/chrony.conf.erb new file mode 100644 index 0000000..449cd29 --- /dev/null +++ b/templates/chrony.conf.erb @@ -0,0 +1,32 @@ +# Generated by Chef for <%= node['fqdn'] %> +# Local modifications will be overwritten. + +<% @servers.each do |server, options| -%> +server <%= server %> <%= options %> +<% end -%> + +<% @pools.each do |pool, options| -%> +pool <%= pool %> <%= options %> +<% end -%> + +driftfile <%= @driftfile %> +logdir <%= @log_dir %> + +<% @allow.each do |allow| -%> +allow <%= allow %> +<% end -%> + +<% @deny.each do |deny| -%> +deny <%= deny %> +<% end -%> + +<% @extra_config.each do |config| -%> +<%= config %> +<% end -%> + +# 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 diff --git a/templates/chrony_client.conf.erb b/templates/chrony_client.conf.erb deleted file mode 100644 index 27cbc52..0000000 --- a/templates/chrony_client.conf.erb +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Chef Infra Client. Do not hand edit - -# Use public servers from the pool.ntp.org project. -# Please consider joining the pool (http://www.pool.ntp.org/join.html). -# See https://chrony.tuxfamily.org/faq.html#_what_is_the_minimum_recommended_configuration_for_an_ntp_client -# for explanations for these configuration settings -<% @servers.each do |server, options| %> -<%= "pool #{server} #{options}" %> -<% end %> - -# Record the rate at which the system clock gains/losses time. -driftfile <%= @driftfile %> - -# Allow the system clock to be stepped in the first three updates -# if its offset is larger than 1 second. -makestep 1 3 - -# Enable kernel synchronization of the real-time clock (RTC). -rtcsync - -# Specify directory for log files. -logdir <%= @log_dir %> - -# The allow directive is used to designate a particular subnet from which clients -# are allowed to access chrony. As a client option this can be used for accessing the the -# NTP interface to query status information. -<% if @allow %> -<% @allow.each do |allowed| %> -allow <%= allowed %> -<% end %> -<% end %> - -# Extra configuration -<% node['chrony']['extra_config'].each do |conf| -%> -<%= conf %> -<% end -%> diff --git a/templates/chrony_master.conf.erb b/templates/chrony_master.conf.erb deleted file mode 100644 index 0728a42..0000000 --- a/templates/chrony_master.conf.erb +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Chef Infra Client. Do not hand edit - -# Use public servers from the pool.ntp.org project. -# Please consider joining the pool (http://www.pool.ntp.org/join.html). -<% @servers.each do |server, options| %> -<%= "server #{server} #{options}" %> -<% end %> - -# Record the rate at which the system clock gains/losses time. -driftfile <%= @driftfile %> - -# Allow the system clock to be stepped in the first three updates -# if its offset is larger than 1 second. -makestep 1 3 - -# Enable kernel synchronization of the real-time clock (RTC). -rtcsync - -# Specify directory for log files. -logdir <%= @log_dir %> - -# The allow directive is used to designate a particular subnet from which NTP clients -# are allowed to access the computer as an NTP server. The default is that no clients -# are allowed access, i.e. chronyd operates purely as an NTP client. If the allow -# directive is used, chronyd will be both a client of its servers, and a server to other clients. -<% if @allow.empty? %> -allow -<% else %> -<% @allow.each do |allowed| %> -allow <%= allowed %> -<% end %> -<% end %> - -# Extra configuration -<% node['chrony']['extra_config'].each do |conf| -%> -<%= conf %> -<% end -%> diff --git a/test/cookbooks/test/metadata.rb b/test/cookbooks/test/metadata.rb index ba9b31b..92b8f61 100644 --- a/test/cookbooks/test/metadata.rb +++ b/test/cookbooks/test/metadata.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + name 'test' maintainer 'Sous Chefs' maintainer_email 'help@sous-chefs.org' license 'Apache-2.0' description 'Testing cookbook for chrony' version '0.0.1' +depends 'chrony' diff --git a/test/cookbooks/test/recipes/default.rb b/test/cookbooks/test/recipes/default.rb index f3013c9..a6c83c8 100644 --- a/test/cookbooks/test/recipes/default.rb +++ b/test/cookbooks/test/recipes/default.rb @@ -1,3 +1,9 @@ -# Workaround issue running chronyd in docker related to: -# https://access.redhat.com/solutions/4410831 -directory '/var/run/chrony' if platform_family?('rhel') +# frozen_string_literal: true + +apt_update + +directory '/var/run/chrony' if platform_family?('rhel', 'fedora', 'amazon') + +chrony_config 'default' do + extra_config ['log measurements statistics tracking'] +end diff --git a/test/cookbooks/test/recipes/delete.rb b/test/cookbooks/test/recipes/delete.rb new file mode 100644 index 0000000..943c6f7 --- /dev/null +++ b/test/cookbooks/test/recipes/delete.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +chrony_config 'default' do + action :delete +end diff --git a/test/cookbooks/test/recipes/server.rb b/test/cookbooks/test/recipes/server.rb new file mode 100644 index 0000000..88e9086 --- /dev/null +++ b/test/cookbooks/test/recipes/server.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +apt_update + +directory '/var/run/chrony' if platform_family?('rhel', 'fedora', 'amazon') + +chrony_config 'server' do + allow ['10.0.0.0/8'] +end diff --git a/test/integration/default/controls/default.rb b/test/integration/default/controls/default.rb new file mode 100644 index 0000000..9a907f3 --- /dev/null +++ b/test/integration/default/controls/default.rb @@ -0,0 +1,49 @@ +chrony_conf_file = if os.redhat? || os.name == 'fedora' + '/etc/chrony.conf' + else + '/etc/chrony/chrony.conf' + end + +chrony_service = if os.redhat? || os.name == 'fedora' + 'chronyd' + else + 'chrony' + end + +control 'chrony-client-01' do + impact 1.0 + title 'chrony package is installed' + desc 'The chrony package should be installed' + + describe package('chrony') do + it { should be_installed } + end +end + +control 'chrony-client-02' do + impact 1.0 + title 'chrony service is enabled and running' + desc 'The chrony service should be enabled and running' + + describe service(chrony_service) do + it { should be_enabled } + it { should be_running } + end +end + +control 'chrony-client-03' do + impact 1.0 + title 'chrony configuration file' + desc 'The chrony configuration file should exist with correct ownership and content' + + describe file(chrony_conf_file) do + it { should be_file } + its('owner') { should eq 'root' } + its('group') { should eq 'root' } + its('mode') { should cmp '0644' } + its('content') { should match(/^server pool\.ntp\.org iburst/) } + its('content') { should match(/^driftfile /) } + its('content') { should match(/^log measurements statistics tracking$/) } + its('content') { should_not match(/^allow/) } + end +end diff --git a/test/integration/default/inspec.yml b/test/integration/default/inspec.yml new file mode 100644 index 0000000..c17104a --- /dev/null +++ b/test/integration/default/inspec.yml @@ -0,0 +1,7 @@ +--- +name: default +title: Chrony client configuration +maintainer: Sous Chefs +license: Apache-2.0 +summary: Verifies chrony is installed and configured as a client +version: 1.0.0 diff --git a/test/integration/inspec/controls/default_spec.rb b/test/integration/inspec/controls/default_spec.rb deleted file mode 100644 index 6ae9180..0000000 --- a/test/integration/inspec/controls/default_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -chrony_conf_file = os.redhat? ? '/etc/chrony.conf' : '/etc/chrony/chrony.conf' -chrony_type = input('type') - -control 'chrony' do - describe service 'chronyd' do - it { should be_installed } - it { should be_enabled } - it { should be_running } - end - - describe file chrony_conf_file do - it { should be_file } - its('owner') { should eq 'root' } - its('group') { should eq 'root' } - its('mode') { should cmp '0644' } - if chrony_type == 'client' - its('content') { should_not match(/allow.*/) } - its('content') { should match /^log measurements statistics tracking$/ } - else - its('content') { should match(/allow.*/) } - its('content') { should_not match /^log measurements statistics tracking$/ } - end - end -end diff --git a/test/integration/inspec/inspec.yml b/test/integration/inspec/inspec.yml deleted file mode 100644 index 0ee44c0..0000000 --- a/test/integration/inspec/inspec.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -name: chrony -inputs: - - name: type - value: "client" diff --git a/test/integration/server/controls/default.rb b/test/integration/server/controls/default.rb new file mode 100644 index 0000000..4fd5b1d --- /dev/null +++ b/test/integration/server/controls/default.rb @@ -0,0 +1,47 @@ +chrony_conf_file = if os.redhat? || os.name == 'fedora' + '/etc/chrony.conf' + else + '/etc/chrony/chrony.conf' + end + +chrony_service = if os.redhat? || os.name == 'fedora' + 'chronyd' + else + 'chrony' + end + +control 'chrony-server-01' do + impact 1.0 + title 'chrony package is installed' + desc 'The chrony package should be installed' + + describe package('chrony') do + it { should be_installed } + end +end + +control 'chrony-server-02' do + impact 1.0 + title 'chrony service is enabled and running' + desc 'The chrony service should be enabled and running' + + describe service(chrony_service) do + it { should be_enabled } + it { should be_running } + end +end + +control 'chrony-server-03' do + impact 1.0 + title 'chrony server configuration file' + desc 'The chrony configuration file should contain allow directives for server mode' + + describe file(chrony_conf_file) do + it { should be_file } + its('owner') { should eq 'root' } + its('group') { should eq 'root' } + its('mode') { should cmp '0644' } + its('content') { should match(/^driftfile /) } + its('content') { should match(%r{^allow 10\.0\.0\.0/8}) } + end +end diff --git a/test/integration/server/inspec.yml b/test/integration/server/inspec.yml new file mode 100644 index 0000000..2c6a2e5 --- /dev/null +++ b/test/integration/server/inspec.yml @@ -0,0 +1,7 @@ +--- +name: server +title: Chrony server configuration +maintainer: Sous Chefs +license: Apache-2.0 +summary: Verifies chrony is installed and configured as a server +version: 1.0.0