diff --git a/.fixtures.yml b/.fixtures.yml index 8bb1d3c..d54324f 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -7,6 +7,8 @@ fixtures: repo: "https://github.com/puppetlabs/puppetlabs-yumrepo_core.git" puppet_version: ">= 6.0.0" apt: "https://github.com/puppetlabs/puppetlabs-apt.git" + archive: "https://github.com/voxpupuli/puppet-archive.git" + powershell: "https://github.com/puppetlabs/puppetlabs-powershell.git" stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib.git" zypprepo: "https://github.com/voxpupuli/puppet-zypprepo.git" symlinks: diff --git a/manifests/config.pp b/manifests/config.pp index 87d430a..59f4540 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -19,11 +19,6 @@ }, 'output' => $metricbeat::outputs, }) - - $validate_cmd = $metricbeat::disable_configtest ? { - true => undef, - default => '/usr/share/metricbeat/bin/metricbeat -configtest -c %', - } } elsif $metricbeat::major_version == '6' { $metricbeat_config_temp = delete_undef_values({ @@ -40,11 +35,6 @@ 'output' => $metricbeat::outputs, }) - $validate_cmd = $metricbeat::disable_configtest ? { - true => undef, - default => '/usr/share/metricbeat/bin/metricbeat test config', - } - # Add the 'xpack' section if supported (version >= 6.2.0) if versioncmp($metricbeat::package_ensure, '6.2.0') >= 0 { $metricbeat_config = deep_merge($metricbeat_config_temp, {'xpack' => $metricbeat::xpack}) @@ -55,13 +45,46 @@ } - file{'metricbeat.yml': - ensure => $metricbeat::ensure, - path => '/etc/metricbeat/metricbeat.yml', - owner => 'root', - group => 'root', - mode => $metricbeat::config_mode, - content => inline_template('<%= @metricbeat_config.to_yaml() %>'), - validate_cmd => $validate_cmd, + case $::kernel { + 'Linux': { + $validate_cmd = $metricbeat::disable_configtest ? { + true => undef, + default => $metricbeat::major_version ? { + '5' => '/usr/share/metricbeat/bin/metricbeat -configtest -c %', + default => '/usr/share/metricbeat/bin/metricbeat test config', + } + } + + file{'metricbeat.yml': + ensure => $metricbeat::ensure, + path => $metricbeat::config_file, + owner => 'root', + group => 'root', + mode => $metricbeat::config_mode, + content => inline_template('<%= @metricbeat_config.to_yaml() %>'), + validate_cmd => $validate_cmd, + } + } + 'Windows': { + $cmd_install_dir = regsubst($metricbeat::install_dir, '/', '\\', 'G') + $metricbeat_path = join([$cmd_install_dir, 'Metricbeat', 'metricbeat.exe'], '\\') + $validate_cmd = $metricbeat::disable_configtest ? { + true => undef, + default => $metricbeat::major_version ? { + '5' => "\"${metricbeat_path}\" -N configtest -c \"%\"", + default => "\"${metricbeat_path}\" test config", + } + } + + file{'metricbeat.yml': + ensure => $metricbeat::ensure, + path => $metricbeat::config_file, + content => inline_template('<%= @metricbeat_config.to_yaml() %>'), + validate_cmd => $validate_cmd, + } + } + default: { + fail("${::kernel} is not supported by metricbeat.") + } } } diff --git a/manifests/init.pp b/manifests/init.pp index 21505b2..3ae3b16 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -36,6 +36,11 @@ # [String] The name of the beat which is published as the `beat.name` # field of each transaction. (default: $::hostname) # +# * `config_file` +# [String] The absolute path to the configuration file location. (default: +# /etc/metricbeat/metricbeat.yaml on Linux, C:/Program Files/Metricbeat/metricbeat.yml +# on Windows) +# # * `config_mode` # [String] The file permission mode of the config file. Must be in Linux # octal format. Default: '0600' @@ -44,6 +49,10 @@ # [Boolean] If true disable configuration file testing. It is generally # recommended to leave this parameter at its default value. (default: false) # +# * `download_url` +# Optional[Variant[Stdlib::HTTPUrl, Stdlib::HTTPSUrl]] The URL of the ZIP +# file to download. Only valid on Windows nodes. (default: undef) +# # * `ensure` # [String] Ensures that all required resources are managed or removed # from the target node. This is good for bulk uninstallation across a @@ -58,6 +67,10 @@ # sub-dictionary. When this is true custom fields are added to the top # level dictionary of each transaction. (default: false) # +# * `install_dir` +# Optional[String] The absolute path to the location where metricbeat will +# be installed. Only applicable on Windows. (default: C:/Program Files) +# # * `logging` # [Hash] The configuration section of File['metricbeat.yml'] for the # logging output. @@ -72,13 +85,18 @@ # # * `package_ensure` # [String] The desired state of Package['metricbeat']. Only valid when -# $ensure is present. (default: 'present') +# $ensure is present. On Windows this is the version number of the package. +# (default: 'present') # # * `processors` # Optional[Array[Hash]] An optional list of dictionaries to configure # processors, provided by libbeat, to process events before they are # sent to the output. (default: undef) # +# * `proxy_address* +# Optional[Variant[Stdlib::HTTPUrl, Stdlib::HTTPSUrl]] The Proxy server used +# for downloading files. (default: undef) +# # * `queue` # [Hash] Configure the internal queue before being consumed by the output(s) # in bulk transactions. As of 6.0 only a memory queue is available, all @@ -99,58 +117,61 @@ # When false the init script's stop and start functions will be used. # (default: true) # +# * `service_provider` +# Optional[String] The optional service provider of the node. (default: +# 'redhat' on RedHat nodes, undef otherwise) +# # * `tags` # Optional[Array[String]] An optional list of values to include in the # `tag` field of each published transaction. This is useful for # identifying groups of servers by logical property. (default: undef) # +# * `tmp_dir` +# String The absolute path to the temporary directory. On Windows, this +# is the target directory for the ZIP file download. (default: /tmp on +# Linux, C:\Windows\Temp on Windows) +# +# * `url_arch +# Optional[String] An optional string describing the architecture of +# the target node. Only applicable on Windows nodes. (default: x86 or x64) +# # * `xpack` # Optional[Hash] Configuration items to export internal stats to a # monitoring Elasticsearch cluster class metricbeat( - Array[Hash] $modules = [{}], - Hash $outputs = {}, - String $beat_name = $::hostname, - Pattern[/^0[0-7]{3}$/] $config_mode = '0600', - Boolean $disable_configtest = false, - Enum['present', 'absent'] $ensure = 'present', - Optional[Hash] $fields = undef, - Boolean $fields_under_root = false, - Hash $logging = { - 'level' => 'info', - 'files' => { - 'keepfiles' => 7, - 'name' => 'metricbeat', - 'path' => '/var/log/metricbeat', - 'rotateeverybytes' => '10485760', - }, - 'metrics' => { - 'enabled' => false, - 'period' => '30s', - }, - 'selectors' => undef, - 'to_files' => true, - 'to_syslog' => false, - }, - Enum['5', '6'] $major_version = '5', - Boolean $manage_repo = true, - String $package_ensure = 'present', - Optional[Array[Hash]] $processors = undef, - Hash $queue = { - 'mem' => { - 'events' => 4096, - 'flush' => { - 'min_events' => 0, - 'timeout' => '0s', - }, - }, - }, - Integer $queue_size = 1000, - Enum['enabled', 'disabled', 'running', 'unmanaged'] $service_ensure = 'enabled', - Boolean $service_has_restart = true, - Optional[Array[String]] $tags = undef, - Optional[Hash] $xpack = undef, -) { + Array[Hash] $modules = $metricbeat::params::modules, + Hash $outputs = $metricbeat::params::outputs, + String $beat_name = $metricbeat::params::beat_name, + String $config_file = $metricbeat::params::config_file, + Pattern[/^0[0-7]{3}$/] $config_mode = $metricbeat::params::config_mode, + Boolean $disable_configtest = $metricbeat::params::disable_configtest, + Optional[Variant[Stdlib::HTTPUrl, Stdlib::HTTPSUrl]] $download_url = $metricbeat::params::download_url, + Enum['present', 'absent'] $ensure = $metricbeat::params::ensure, + Optional[Hash] $fields = $metricbeat::params::fields, + Boolean $fields_under_root = $metricbeat::params::fields_under_root, + Optional[String] $install_dir = $metricbeat::params::install_dir, + Hash $logging = $metricbeat::params::logging, + Enum['5', '6'] $major_version = $metricbeat::params::major_version, + Boolean $manage_repo = $metricbeat::params::manage_repo, + String $package_ensure = $metricbeat::params::package_ensure, + Optional[Array[Hash]] $processors = $metricbeat::params::processors, + Optional[Variant[Stdlib::HTTPUrl, Stdlib::HTTPSUrl]] $proxy_address = $metricbeat::params::proxy_address, + Hash $queue = $metricbeat::params::queue, + Integer $queue_size = $metricbeat::params::queue_size, + Enum['enabled', 'disabled', 'running', 'unmanaged'] $service_ensure = $metricbeat::params::service_ensure, + Boolean $service_has_restart = $metricbeat::params::service_has_restart, + Optional[String] $service_provider = $metricbeat::params::service_provider, + Optional[Array[String]] $tags = $metricbeat::params::tags, + String $tmp_dir = $metricbeat::params::tmp_dir, + Optional[String] $url_arch = $metricbeat::params::url_arch, + Optional[Hash] $xpack = $metricbeat::params::xpack, +) inherits metricbeat::params { + + $real_download_url = $download_url ? { + undef => "https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-${package_ensure}-windows-${metricbeat::params::url_arch}.zip", + default => $download_url, + } + if $manage_repo { class{'metricbeat::repo':} diff --git a/manifests/install.pp b/manifests/install.pp index 61dc1fe..59de535 100644 --- a/manifests/install.pp +++ b/manifests/install.pp @@ -5,14 +5,79 @@ # # @summary Manages the state of Package['metricbeat'] class metricbeat::install inherits metricbeat { - if $metricbeat::ensure == 'present' { - $package_ensure = $metricbeat::package_ensure + if $::kernel == 'Windows' { + $filename = regsubst($metricbeat::real_download_url, '^https?.*\/([^\/]+)\.[^.].*', '\1') + $foldername = 'Metricbeat' + $zip_file = join([$metricbeat::tmp_dir, "${filename}.zip"], '/') + $install_folder = join([$metricbeat::install_dir, $foldername], '/') + $version_file = join([$install_folder, $filename], '/') + + Exec { + provider => powershell, + } + + if !defined(File[$metricbeat::install_dir]) { + file{$metricbeat::install_dir: + ensure => directory, + } + } + + archive{ $zip_file: + source => $metricbeat::real_download_url, + cleanup => false, + creates => $version_file, + proxy_server => $metricbeat::proxy_address, + } + exec{"unzip ${filename}": + command => "\$sh=New-Object -COM Shell.Application;\$sh.namespace((Convert-Path '${metricbeat::install_dir}')).Copyhere(\$sh.namespace((Convert-Path '${zip_file}')).items(), 16)", # lint:ignore:140chars + creates => $version_file, + require => [ + File[$metricbeat::install_dir], + Archive[$zip_file], + ], + } + + # Clean up after ourselves + file{$zip_file: + ensure => absent, + backup => false, + require => Exec["unzip ${filename}"], + } + + # You can't remove the old dir while the service has files locked... + exec{"stop service ${filename}": + command => 'Set-Service -Name metricbeat -Status Stopped', + creates => $version_file, + onlyif => 'if(Get-WmiObject -Class Win32_Service -Filter "Name=\'metricbeat\'") {exit 0} else {exit 1}', + require => Exec["unzip ${filename}"], + } + exec{"rename ${filename}": + command => "Remove-Item '${install_folder}' -Recurse -Force -ErrorAction SilentlyContinue;Rename-Item '${metricbeat::install_dir}/${filename}' '${install_folder}'", # lint:ignore:140chars + creates => $version_file, + require => Exec["stop service ${filename}"], + } + exec{"mark ${filename}": + command => "New-Item '${version_file}' -ItemType file", + creates => $version_file, + require => Exec["rename ${filename}"], + } + exec{"install ${filename}": + cwd => $install_folder, + command => './install-service-metricbeat.ps1', + refreshonly => true, + subscribe => Exec["mark ${filename}"], + } } else { - $package_ensure = $metricbeat::ensure - } + if $metricbeat::ensure == 'present' { + $package_ensure = $metricbeat::package_ensure + } + else { + $package_ensure = $metricbeat::ensure + } - package{'metricbeat': - ensure => $package_ensure, + package{'metricbeat': + ensure => $package_ensure, + } } } diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000..cf8f8ad --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,96 @@ +# A description of what this class does +# +# @summary A short summary of the purpose of this class +# +# @example +# include metricbeat::params +class metricbeat::params { + $ensure = 'present' + $beat_name = $::hostname + $config_mode = '0600' + $disable_configtest = false + $download_url = undef + $fields = undef + $fields_under_root = false + $manage_repo = true + $major_version = '5' + $modules = [{}] + $outputs = {} + $processors = undef + $proxy_address = undef + $queue = { + 'mem' => { + 'events' => 4096, + 'flush' => { + 'min_events' => 0, + 'timeout' => '0s', + }, + }, + } + $queue_size = 1000 + $service_ensure = 'enabled' + $service_has_restart = true + $tags = undef + $xpack = undef + + case $::kernel { + 'Linux': { + $config_file = '/etc/metricbeat/metricbeat.yml' + $install_dir = undef + $logging = { + 'level' => 'info', + 'files' => { + 'keepfiles' => 7, + 'name' => 'metricbeat', + 'path' => '/var/log/metricbeat', + 'rotateeverybytes' => '10485760', + }, + 'metrics' => { + 'enabled' => false, + 'period' => '30s', + }, + 'selectors' => undef, + 'to_files' => true, + 'to_syslog' => false, + } + $package_ensure = 'present' + $service_provider = $::osfamily ? { + 'RedHat' => 'redhat', + default => undef, + } + $tmp_dir = '/tmp' + $url_arch = undef + } + 'Windows': { + $config_file = 'C:/Program Files/Metricbeat/metricbeat.yml' + $install_dir = 'C:/Program Files' + $logging = { + 'level' => 'info', + 'files' => { + 'keepfiles' => 7, + 'name' => 'metricbeat', + 'path' => 'C:/Program Files/Metricbeat/logs', + 'rotateeverybytes' => '10485760', + }, + 'metrics' => { + 'enabled' => false, + 'period' => '30s', + }, + 'selectors' => undef, + 'to_eventlog' => false, + 'to_files' => true, + } + $package_ensure = '5.6.2' + $service_provider = undef + $tmp_dir = 'C:/Windows/Temp' + $url_arch = $::architecture ? { + 'x86' => 'x86', + 'x64' => 'x86_64', + default => fail("${::architecture} is not supported by metricbeat."), + } + } + default: { + fail("${::kernel} is not supported by metricbeat.") + } + } +} diff --git a/metadata.json b/metadata.json index 7174ec0..8d4e54d 100644 --- a/metadata.json +++ b/metadata.json @@ -16,6 +16,14 @@ "name": "puppetlabs-apt", "version_requirement": ">= 4.0.0 < 7.0.0" }, + { + "name": "puppet/archive", + "version_requirement": ">= 0.5.0 < 4.0.0" + }, + { + "name": "puppetlabs-powershell", + "version_requirement": ">= 1.0.1 < 3.0.0" + }, { "name": "puppet-zypprepo", "version_requirement": ">= 2.0.0 < 3.0.0" @@ -69,6 +77,13 @@ "operatingsystemrelease": [ "12" ] + }, + { + "operatingsystem": "windows", + "operatingsystemrelease": [ + "2012", + "2012 R2" + ] } ], "requirements": [ diff --git a/spec/classes/metricbeat_spec.rb b/spec/classes/metricbeat_spec.rb index 4407ff2..7d92a54 100644 --- a/spec/classes/metricbeat_spec.rb +++ b/spec/classes/metricbeat_spec.rb @@ -8,32 +8,15 @@ it { is_expected.to compile } describe 'metricbeat::config' do - it do - is_expected.to contain_file('metricbeat.yml').with( - ensure: 'present', - owner: 'root', - group: 'root', - mode: '0600', - path: '/etc/metricbeat/metricbeat.yml', - validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', - ) - end - - describe 'with ensure = absent' do - let(:params) { { 'ensure' => 'absent' } } - + if os_facts[:kernel] == 'windows' it do is_expected.to contain_file('metricbeat.yml').with( - ensure: 'absent', - path: '/etc/metricbeat/metricbeat.yml', - validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', + ensure: 'present', + path: 'C:/Program Files/Metricbeat/metricbeat.yml', + validate_cmd: "\"C:\\Program Files\\Metricbeat\\metricbeat.exe\" -N configtest -c \"%\"", # rubocop:disable StringLiterals ) end - end - - describe 'with disable_configtest = true' do - let(:params) { { 'disable_configtest' => true } } - + else it do is_expected.to contain_file('metricbeat.yml').with( ensure: 'present', @@ -41,23 +24,72 @@ group: 'root', mode: '0600', path: '/etc/metricbeat/metricbeat.yml', - validate_cmd: nil, + validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', ) end end + describe 'with ensure = absent' do + let(:params) { { 'ensure' => 'absent' } } + + if os_facts[:kernel] == 'windows' + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'absent', + path: 'C:/Program Files/Metricbeat/metricbeat.yml', + validate_cmd: "\"C:\\Program Files\\Metricbeat\\metricbeat.exe\" -N configtest -c \"%\"", # rubocop:disable StringLiterals + ) + end + else + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'absent', + path: '/etc/metricbeat/metricbeat.yml', + validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', + ) + end + end + end + + describe 'with disable_configtest = true' do + let(:params) { { 'disable_configtest' => true } } + + if os_facts[:kernel] == 'windows' + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'present', + path: 'C:/Program Files/Metricbeat/metricbeat.yml', + validate_cmd: nil, + ) + end + else + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'present', + owner: 'root', + group: 'root', + mode: '0600', + path: '/etc/metricbeat/metricbeat.yml', + validate_cmd: nil, + ) + end + end + end + describe 'with config_mode = 0644' do let(:params) { { 'config_mode' => '0644' } } - it do - is_expected.to contain_file('metricbeat.yml').with( - ensure: 'present', - owner: 'root', - group: 'root', - mode: '0644', - path: '/etc/metricbeat/metricbeat.yml', - validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', - ) + if os_facts[:kernel] != 'windows' + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'present', + owner: 'root', + group: 'root', + mode: '0644', + path: '/etc/metricbeat/metricbeat.yml', + validate_cmd: '/usr/share/metricbeat/bin/metricbeat -configtest -c %', + ) + end end end @@ -70,38 +102,86 @@ describe 'with major_version = 6 for new config test flag' do let(:params) { { 'major_version' => '6' } } - it do - is_expected.to contain_file('metricbeat.yml').with( - ensure: 'present', - owner: 'root', - group: 'root', - mode: '0600', - path: '/etc/metricbeat/metricbeat.yml', - validate_cmd: '/usr/share/metricbeat/bin/metricbeat test config', - ) + if os_facts[:kernel] == 'windows' + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'present', + path: 'C:/Program Files/Metricbeat/metricbeat.yml', + validate_cmd: "\"C:\\Program Files\\Metricbeat\\metricbeat.exe\" test config", # rubocop:disable StringLiterals + ) + end + else + it do + is_expected.to contain_file('metricbeat.yml').with( + ensure: 'present', + owner: 'root', + group: 'root', + mode: '0600', + path: '/etc/metricbeat/metricbeat.yml', + validate_cmd: '/usr/share/metricbeat/bin/metricbeat test config', + ) + end end end end describe 'metricbeat::install' do - it { is_expected.to contain_package('metricbeat').with(ensure: 'present') } + if os_facts[:kernel] == 'windows' + it do + is_expected.to contain_file('C:/Program Files').with(ensure: 'directory') + is_expected.to contain_archive('C:/Windows/Temp/metricbeat-5.6.2-windows-x86_64.zip').with( + creates: 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64', + source: 'https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-5.6.2-windows-x86_64.zip', + ) + is_expected.to contain_exec('unzip metricbeat-5.6.2-windows-x86_64').with( + command: "\$sh=New-Object -COM Shell.Application;\$sh.namespace((Convert-Path 'C:/Program Files')).Copyhere(\$sh.namespace((Convert-Path 'C:/Windows/Temp/metricbeat-5.6.2-windows-x86_64.zip')).items(), 16)", # rubocop:disable LineLength + creates: 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64', + ) + is_expected.to contain_exec('stop service metricbeat-5.6.2-windows-x86_64').with( + creates: 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64', + command: 'Set-Service -Name metricbeat -Status Stopped', + onlyif: 'if(Get-WmiObject -Class Win32_Service -Filter "Name=\'metricbeat\'") {exit 0} else {exit 1}', + ) + is_expected.to contain_exec('rename metricbeat-5.6.2-windows-x86_64').with( + creates: 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64', + command: "Remove-Item 'C:/Program Files/Metricbeat' -Recurse -Force -ErrorAction SilentlyContinue;Rename-Item 'C:/Program Files/metricbeat-5.6.2-windows-x86_64' 'C:/Program Files/Metricbeat'", # rubocop:disable LineLength + ) + is_expected.to contain_exec('mark metricbeat-5.6.2-windows-x86_64').with( + creates: 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64', + command: "New-Item 'C:/Program Files/Metricbeat/metricbeat-5.6.2-windows-x86_64' -ItemType file", + ) + is_expected.to contain_exec('install metricbeat-5.6.2-windows-x86_64').with( + command: './install-service-metricbeat.ps1', + cwd: 'C:/Program Files/Metricbeat', + refreshonly: true, + ) + end + else + it { is_expected.to contain_package('metricbeat').with(ensure: 'present') } + end describe 'with ensure = absent' do let(:params) { { 'ensure' => 'absent' } } - it { is_expected.to contain_package('metricbeat').with(ensure: 'absent') } + if os_facts[:kernel] != 'windows' + it { is_expected.to contain_package('metricbeat').with(ensure: 'absent') } + end end describe 'with package_ensure to a specific version' do let(:params) { { 'package_ensure' => '5.6.2-1' } } - it { is_expected.to contain_package('metricbeat').with(ensure: '5.6.2-1') } + if os_facts[:kernel] != 'windows' + it { is_expected.to contain_package('metricbeat').with(ensure: '5.6.2-1') } + end end describe 'with package_ensure = latest' do let(:params) { { 'package_ensure' => 'latest' } } - it { is_expected.to contain_package('metricbeat').with(ensure: 'latest') } + if os_facts[:kernel] != 'windows' + it { is_expected.to contain_package('metricbeat').with(ensure: 'latest') } + end end end diff --git a/spec/classes/params_spec.rb b/spec/classes/params_spec.rb new file mode 100644 index 0000000..97794e6 --- /dev/null +++ b/spec/classes/params_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe 'metricbeat::params' do + on_supported_os(facterversion: '2.4').each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + it { is_expected.to compile } + end + end +end