diff --git a/.fixtures-latest.yml b/.fixtures-latest.yml index c31ec97955..2cc999a6d6 100644 --- a/.fixtures-latest.yml +++ b/.fixtures-latest.yml @@ -2,13 +2,12 @@ fixtures: repositories: apt: repo: git://github.com/puppetlabs/puppetlabs-apt.git - ref: 6.3.0 stdlib: repo: git://github.com/puppetlabs/puppetlabs-stdlib.git - ref: 5.1.0 yumrepo_core: repo: git://github.com/puppetlabs/puppetlabs-yumrepo_core - ref: 1.0.1 puppet_version: ">= 6.0.0" + archive: + repo: git://github.com/voxpupuli/puppet-archive.git symlinks: sensu: "#{source_dir}" diff --git a/.fixtures.yml b/.fixtures.yml index b86e51f128..09c239d6d2 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -10,5 +10,8 @@ fixtures: repo: git://github.com/puppetlabs/puppetlabs-yumrepo_core ref: 1.0.1 puppet_version: ">= 6.0.0" + archive: + repo: git://github.com/voxpupuli/puppet-archive.git + ref: 'v3.0.0' symlinks: sensu: "#{source_dir}" diff --git a/.rspec b/.rspec new file mode 100644 index 0000000000..49d5710b3e --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--format documentation diff --git a/.travis.yml b/.travis.yml index 71cb1ae163..4c948798c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -149,6 +149,11 @@ matrix: env: BEAKER_set="amazonlinux-201803" BEAKER_PUPPET_COLLECTION=puppet6 bundler_args: script: bundle exec rake beaker + allow_failures: + - rvm: 2.4.4 + env: PUPPET_GEM_VERSION="~> 5" FIXTURES_YML=".fixtures-latest.yml" + - rvm: 2.5.3 + env: PUPPET_GEM_VERSION="~> 6" FIXTURES_YML=".fixtures-latest.yml" branches: only: - master diff --git a/README.md b/README.md index 9eaabcf910..309238a125 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ 3. [Usage - Configuration options and additional functionality](#usage) * [Basic Sensu backend](#basic-sensu-backend) * [Basic Sensu agent](#basic-sensu-agent) + * [Manage Windows Agent](#manage-windows-agent) * [Advanced agent](#advanced-agent) * [Advanced SSL](#advanced-ssl) * [Enterprise support](#enterprise-support) @@ -65,6 +66,8 @@ This module has a soft dependency on the [puppetlabs/apt](https://forge.puppet.c If using Puppet >= 6.0.0 there is a soft dependency on the [puppetlabs/yumrepo_core](https://forge.puppet.com/puppetlabs/yumrepo_core) module (`>= 1.0.1 < 2.0.0`) for systems using `yum`. +If managing Windows there is a soft dependency on the [puppet/archive](https://forge.puppet.com/puppet/archive) module (`>= 3.0.0 < 4.0.0`). + ### Beginning with sensu This module provides Vagrant definitions that can be used to get started with Sensu. @@ -119,6 +122,36 @@ associated to `linux` and `apache-servers` subscriptions. } ``` +### Manage Windows Agent + +This module supports Windows Sensu Go agent starting with version 5.7.0. + +The Windows package source must be specified as either a URL, a Puppet source or a filesystem path. + +Install sensu-go-agent on Windows from URL: + +```puppet +class { 'sensu::agent': + package_source => 'https://s3-us-west-2.amazonaws.com/sensu.io/sensu-go/5.7.0/sensu-go-agent_5.7.0.2380_en-US.x64.msi', +} +``` + +Install sensu-go-agent on Windows from Puppet source: + +```puppet +class { 'sensu::agent': + package_source => 'puppet:///modules/profile/sensu/sensu-go-agent.msi', +} +``` + +If a system already has the necessary MSI present it can be installed without downloading from an URL: + +```puppet +class { 'sensu::agent': + package_source => 'C:\Temp\sensu-go-agent.msi', +} +``` + ### Advanced agent If you wish to change the `agent` password you must provide the new and old password. diff --git a/REFERENCE.md b/REFERENCE.md index 022b5ed479..19112463f4 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -170,6 +170,25 @@ Windows MSI packaging and to avoid surprising upgrades. Default value: `undef` +##### `package_source` + +Data type: `Optional[String[1]]` + +Source of package for installing Windows. +Paths with http:// or https:// will be downloaded +Paths with puppet:// or absolute filesystem paths will also be installed. + +Default value: `undef` + +##### `package_download_path` + +Data type: `Optional[Stdlib::Absolutepath]` + +Where to download the MSI for Windows. Defaults to `C:\`. +This parameter only used when `package_source` is an URL or when it's a puppet source (`puppet://`). + +Default value: `undef` + ##### `package_name` Data type: `String` @@ -229,6 +248,15 @@ Sets show_diff parameter for agent.yml configuration file Default value: `true` +##### `log_file` + +Data type: `Optional[Stdlib::Absolutepath]` + +Path to agent log file, only for Windows. +Defaults to `C:\ProgramData\sensu\log\sensu-agent.log` + +Default value: `undef` + ### sensu::backend Class to manage the Sensu backend. diff --git a/Rakefile b/Rakefile index 0d1b8a566d..e2126ff838 100644 --- a/Rakefile +++ b/Rakefile @@ -55,3 +55,10 @@ desc 'Generate REFERENCE.md' task :reference do sh 'puppet strings generate --format markdown' end + +namespace :acceptance do + desc 'Run acceptance tests against current code for Windows' + RSpec::Core::RakeTask.new(:windows) do |t| + t.pattern = 'spec/acceptance/windows_spec.rb' + end +end diff --git a/Vagrantfile b/Vagrantfile index afffd6e82a..3b18a835ea 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -135,4 +135,47 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| agent.vm.provision :shell, :inline => "puppet apply /vagrant/tests/sensu-agent.pp" agent.vm.provision :shell, :inline => "facter --custom-dir=/vagrant/lib/facter sensu_agent" end + + config.vm.define "win2008r2-agent", autostart: false do |agent| + agent.vm.box = "opentable/win-2008r2-standard-amd64-nocm" + agent.vm.provider :virtualbox do |vb| + vb.customize ["modifyvm", :id, "--memory", "2048"] + vb.customize ["modifyvm", :id, "--cpus", "1"] + end + agent.vm.hostname = 'win2008r2-agent' + agent.vm.network :private_network, ip: "192.168.52.25" + agent.vm.network "forwarded_port", host: 3390, guest: 3389, auto_correct: true + agent.vm.provision :shell, :path => "tests/provision_basic_win.ps1" + agent.vm.provision :shell, :inline => '$env:PATH += ";C:\Program Files\Puppet Labs\Puppet\bin" ; iex "puppet apply -v C:/vagrant/tests/sensu-agent.pp"' + agent.vm.provision :shell, :inline => '$env:PATH += ";C:\Program Files\Puppet Labs\Puppet\bin" ; iex "facter --custom-dir=C:\vagrant\lib\facter sensu_agent"' + end + + config.vm.define "win2012r2-agent", autostart: false do |agent| + agent.vm.box = "opentable/win-2012r2-standard-amd64-nocm" + agent.vm.provider :virtualbox do |vb| + vb.customize ["modifyvm", :id, "--memory", "2048"] + vb.customize ["modifyvm", :id, "--cpus", "1"] + end + agent.vm.hostname = 'win2012r2-agent' + agent.vm.network :private_network, ip: "192.168.52.24" + agent.vm.network "forwarded_port", host: 3389, guest: 3389, auto_correct: true + agent.vm.provision :shell, :path => "tests/provision_basic_win.ps1" + agent.vm.provision :shell, :inline => 'iex "puppet apply -v C:/vagrant/tests/sensu-agent.pp"' + agent.vm.provision :shell, :inline => 'iex "facter --custom-dir=C:\vagrant\lib\facter sensu_agent"' + end + + config.vm.define "win2016-agent", autostart: false do |agent| + agent.vm.box = "mwrock/Windows2016" + agent.vm.provider :virtualbox do |vb| + vb.customize ["modifyvm", :id, "--memory", "2048"] + vb.customize ["modifyvm", :id, "--cpus", "1"] + vb.gui = false + end + agent.vm.hostname = 'win2016-agent' + agent.vm.network :private_network, ip: "192.168.52.26" + agent.vm.network "forwarded_port", host: 3391, guest: 3389, auto_correct: true + agent.vm.provision :shell, :path => "tests/provision_basic_win.ps1" + agent.vm.provision :shell, :inline => 'iex "puppet apply -v C:/vagrant/tests/sensu-agent.pp"' + agent.vm.provision :shell, :inline => 'iex "facter --custom-dir=C:\vagrant\lib\facter sensu_agent"' + end end diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..0d4423e13e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,58 @@ +version: '{build}-{branch}' +platform: + - x64 +build: off +max_jobs: 2 +branches: + only: + - master + - /^v\d/ +cache: +- C:\downloads +- C:\vendor\bundle +init: +- ps: $env:GEM_SOURCE = "http://rubygems.org" +install: +- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% +- ps: | + if (!(Test-Path C:\downloads)) { mkdir C:\downloads | Out-Null } + $log = "C:/puppet-agent.log" + $agent_url = "https://downloads.puppetlabs.com/windows/${ENV:PUPPET_REPO}/puppet-agent-x64-latest.msi" + Write-Output "Installing Puppet from $agent_url" + Write-Output "Log will be written to $log" + if ( Test-Path $log ) { Remove-Item $log } + cd C:\downloads + if (!(Test-Path "puppet-agent-x64-latest.msi")) { Start-FileDownload $agent_url } + Start-Process msiexec.exe -Wait -NoNewWindow -ArgumentList @("/i", "C:\downloads\puppet-agent-x64-latest.msi", "/qn", "/l*", "$log") + cd $ENV:APPVEYOR_BUILD_FOLDER +- ps: Copy-Item -Path $ENV:APPVEYOR_BUILD_FOLDER -Destination C:/ProgramData/PuppetLabs/code/environments/production/modules/sensu -Recurse +- ps: Copy-Item -Path "${ENV:APPVEYOR_BUILD_FOLDER}/tests/ssl" -Destination C:/ProgramData/PuppetLabs/puppet/etc/ssl -Recurse +- ps: Copy-Item -Path "${ENV:APPVEYOR_BUILD_FOLDER}/lib/facter" -Destination C:/ProgramData/PuppetLabs/puppet/cache/lib/facter -Recurse +- set PATH=C:\Program Files\Puppet Labs\Puppet\bin;%PATH% +- puppet --version +- puppet module install puppetlabs-stdlib +- puppet module install puppet-archive +- puppet config set --section main certname sensu_agent +- facter -p --debug +- ruby -v +- gem update --system 2.7.9 +- gem -v +- bundle -v +- bundle install --jobs 4 --retry 2 --path C:\vendor\bundle +test_script: +- bundle exec rake acceptance:windows +image: + # Windows 2012 R2 + - Visual Studio 2015 + # Windows 2016 + - Visual Studio 2017 +environment: + matrix: + - PUPPET_GEM_VERSION: '~>5.x' + PUPPET_REPO: puppet5 + RUBY_VERSION: 24-x64 + - PUPPET_GEM_VERSION: '~>6.x' + PUPPET_REPO: puppet6 + RUBY_VERSION: 25-x64 +matrix: + fast_finish: false diff --git a/data/os/windows.yaml b/data/os/windows.yaml index dbc7bd634b..8748bf625b 100644 --- a/data/os/windows.yaml +++ b/data/os/windows.yaml @@ -1,2 +1,8 @@ --- -sensu::etc_dir: 'C:/opt/sensu' +sensu::manage_repo: false +sensu::etc_dir: 'C:\ProgramData\Sensu\config' +sensu::ssl_dir: 'C:\ProgramData\Sensu\config\ssl' +sensu::agent::package_name: 'Sensu Agent' +sensu::agent::package_download_path: 'C:\' +sensu::agent::log_file: 'C:\ProgramData\sensu\log\sensu-agent.log' +sensu::agent::service_name: SensuAgent diff --git a/lib/facter/sensu_facts.rb b/lib/facter/sensu_facts.rb index 893385eaf8..5520fe6182 100644 --- a/lib/facter/sensu_facts.rb +++ b/lib/facter/sensu_facts.rb @@ -2,14 +2,23 @@ module SensuFacts def self.which(cmd) - path = Facter::Core::Execution.which(cmd) + path = nil + if File.exists?("C:\\Program Files\\sensu\\sensu-agent\\bin\\#{cmd}.exe") + path = "C:\\Program Files\\sensu\\sensu-agent\\bin\\#{cmd}.exe" + else + path = Facter::Core::Execution.which(cmd) + end path end def self.get_version_info(cmd) path = self.which(cmd) return nil unless path - output = Facter::Core::Execution.exec("#{path} version 2>&1") + if Facter.value(:kernel) == 'windows' + output = Facter::Core::Execution.exec("\"#{path}\" version") + else + output = Facter::Core::Execution.exec("#{path} version 2>&1") + end version = nil if output =~ /^#{cmd} version ([^,]+)/ version = $1.split('#')[0] diff --git a/manifests/agent.pp b/manifests/agent.pp index 5c7e716525..27e82dfa16 100644 --- a/manifests/agent.pp +++ b/manifests/agent.pp @@ -13,6 +13,13 @@ # @param version # Version of sensu agent to install. Defaults to `installed` to support # Windows MSI packaging and to avoid surprising upgrades. +# @param package_source +# Source of package for installing Windows. +# Paths with http:// or https:// will be downloaded +# Paths with puppet:// or absolute filesystem paths will also be installed. +# @param package_download_path +# Where to download the MSI for Windows. Defaults to `C:\`. +# This parameter only used when `package_source` is an URL or when it's a puppet source (`puppet://`). # @param package_name # Name of Sensu agent package. # @param service_name @@ -30,9 +37,14 @@ # Passing `backend-url` as part of `config_hash` takes precedence. # @param show_diff # Sets show_diff parameter for agent.yml configuration file +# @param log_file +# Path to agent log file, only for Windows. +# Defaults to `C:\ProgramData\sensu\log\sensu-agent.log` # class sensu::agent ( Optional[String] $version = undef, + Optional[String[1]] $package_source = undef, + Optional[Stdlib::Absolutepath] $package_download_path = undef, String $package_name = 'sensu-go-agent', String $service_name = 'sensu-agent', String $service_ensure = 'running', @@ -40,6 +52,7 @@ Hash $config_hash = {}, Array[Sensu::Backend_URL] $backends = ['localhost:8081'], Boolean $show_diff = true, + Optional[Stdlib::Absolutepath] $log_file = undef, ) { include ::sensu @@ -52,7 +65,7 @@ if $use_ssl { $backend_protocol = 'wss' $ssl_config = { - 'trusted-ca-file' => "${ssl_dir}/ca.crt", + 'trusted-ca-file' => $::sensu::trusted_ca_file_path, } $service_subscribe = Class['::sensu::ssl'] } else { @@ -72,17 +85,58 @@ } $config = $default_config + $ssl_config + $config_hash + if $facts['os']['family'] == 'windows' { + $sensu_agent_exe = "C:\\Program Files\\sensu\\sensu-agent\\bin\\sensu-agent.exe" + exec { 'install-agent-service': + command => "C:\\windows\\system32\\cmd.exe /c \"\"${sensu_agent_exe}\" service install --config-file \"${::sensu::agent_config_path}\" --log-file \"${log_file}\"\"", + unless => "C:\\windows\\system32\\sc.exe query SensuAgent", + before => Service['sensu-agent'], + require => [ + Package['sensu-go-agent'], + File['sensu_agent_config'], + ], + } + if $package_source and ($package_source =~ Stdlib::HTTPSUrl or $package_source =~ Stdlib::HTTPUrl) { + $package_source_basename = basename($package_source) + $_package_source = "${package_download_path}\\${package_source_basename}" + archive { 'sensu-go-agent.msi': + source => $package_source, + path => $_package_source, + extract => false, + cleanup => false, + before => Package['sensu-go-agent'], + } + } elsif $package_source and $package_source =~ /^puppet:/ { + $package_source_basename = basename($package_source) + $_package_source = "${package_download_path}\\${package_source_basename}" + file { 'sensu-go-agent.msi': + ensure => 'file', + path => $_package_source, + source => $package_source, + before => Package['sensu-go-agent'], + } + } else { + $_package_source = $package_source + } + } else { + $_package_source = undef + } + package { 'sensu-go-agent': ensure => $_version, name => $package_name, + source => $_package_source, before => File['sensu_etc_dir'], require => $::sensu::package_require, } file { 'sensu_agent_config': ensure => 'file', - path => "${etc_dir}/agent.yml", + path => $::sensu::agent_config_path, content => to_yaml($config), + owner => $::sensu::sensu_user, + group => $::sensu::sensu_group, + mode => $::sensu::file_mode, show_diff => $show_diff, require => Package['sensu-go-agent'], notify => Service['sensu-agent'], diff --git a/manifests/backend.pp b/manifests/backend.pp index 4630717f17..a97812768b 100644 --- a/manifests/backend.pp +++ b/manifests/backend.pp @@ -284,6 +284,9 @@ ensure => 'file', path => "${etc_dir}/backend.yml", content => to_yaml($config), + owner => $::sensu::user, + group => $::sensu::group, + mode => '0640', show_diff => $show_diff, require => Package['sensu-go-backend'], notify => Service['sensu-backend'], diff --git a/manifests/init.pp b/manifests/init.pp index 36fc997850..bedaa9c843 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -53,6 +53,21 @@ fail('sensu: ssl_ca_source must be defined when use_ssl is true') } + if $facts['os']['family'] == 'windows' { + $sensu_user = undef + $sensu_group = undef + $file_mode = undef + $trusted_ca_file_path = "${ssl_dir}\\ca.crt" + $agent_config_path = "${etc_dir}\\agent.yml" + } else { + $sensu_user = $user + $sensu_group = $group + $file_mode = '0640' + $join_path = '/' + $trusted_ca_file_path = "${ssl_dir}/ca.crt" + $agent_config_path = "${etc_dir}/agent.yml" + } + file { 'sensu_etc_dir': ensure => 'directory', path => $etc_dir, @@ -72,8 +87,11 @@ 'Debian': { $os_package_require = [Class['::apt::update']] } + 'windows': { + $os_package_require = [] + } default: { - fail("Detected osfamily <${facts['os']['family']}>. Only RedHat and Debian are supported.") + fail("Detected osfamily <${facts['os']['family']}>. Only RedHat, Debian and Windows are supported.") } } diff --git a/manifests/plugins.pp b/manifests/plugins.pp index 376fd763dc..3e44555145 100644 --- a/manifests/plugins.pp +++ b/manifests/plugins.pp @@ -38,6 +38,10 @@ Variant[Array, Hash] $extensions = [], ) { + if $facts['os']['family'] == 'windows' { + fail('sensu::plugins is not supported on Windows') + } + include ::sensu if $::sensu::manage_repo { diff --git a/manifests/ssl.pp b/manifests/ssl.pp index 5091c9de3c..791b5675a7 100644 --- a/manifests/ssl.pp +++ b/manifests/ssl.pp @@ -4,23 +4,31 @@ class sensu::ssl { include ::sensu + if $facts['os']['family'] == 'windows' { + $directory_mode = undef + $file_mode = undef + } else { + $directory_mode = '0700' + $file_mode = '0644' + } + file { 'sensu_ssl_dir': ensure => 'directory', path => $::sensu::ssl_dir, purge => $::sensu::ssl_dir_purge, recurse => $::sensu::ssl_dir_purge, force => $::sensu::ssl_dir_purge, - owner => $::sensu::user, - group => $::sensu::group, - mode => '0700', + owner => $::sensu::sensu_user, + group => $::sensu::sensu_group, + mode => $directory_mode, } file { 'sensu_ssl_ca': ensure => 'file', - path => "${::sensu::ssl_dir}/ca.crt", - owner => $::sensu::user, - group => $::sensu::group, - mode => '0644', + path => $::sensu::trusted_ca_file_path, + owner => $::sensu::sensu_user, + group => $::sensu::sensu_group, + mode => $file_mode, show_diff => false, source => $::sensu::ssl_ca_source, } diff --git a/metadata.json b/metadata.json index 4c6505ba42..6b2e22bf5f 100644 --- a/metadata.json +++ b/metadata.json @@ -47,6 +47,14 @@ "2018.03", "2" ] + }, + { + "operatingsystem": "Windows", + "operatingsystemrelease": [ + "Server 2008 R2", + "Server 2012 R2", + "Server 2016" + ] } ], "requirements": [ diff --git a/spec/acceptance/windows_spec.rb b/spec/acceptance/windows_spec.rb new file mode 100644 index 0000000000..01b7667aa8 --- /dev/null +++ b/spec/acceptance/windows_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper_acceptance_windows' if Gem.win_platform? +require 'json' + +describe 'sensu::agent class', if: Gem.win_platform? do + context 'default' do + pp = <<-EOS + class { '::sensu': } + class { '::sensu::agent': + package_source => 'https://s3-us-west-2.amazonaws.com/sensu.io/sensu-go/5.7.0/sensu-go-agent_5.7.0.2380_en-US.x64.msi', + backends => ['sensu_backend:8081'], + config_hash => { + 'name' => 'sensu_agent', + } + } + EOS + + File.open('C:\manifest-agent.pp', 'w') { |f| f.write(pp) } + puts "C:\manifest-agent.pp" + puts File.read('C:\manifest-agent.pp') + describe command('puppet apply --debug C:\manifest-agent.pp') do + its(:exit_status) { is_expected.to eq 0 } + end + + describe service('SensuAgent') do + it { should be_enabled } + it { should be_running } + end + describe 'sensu_agent.version fact' do + it 'has version fact' do + output = `facter --json -p sensu_agent` + data = JSON.parse(output.strip) + expect(data['sensu_agent']['version']).to match(/^[0-9\.]+$/) + end + end + end +end diff --git a/spec/classes/agent_spec.rb b/spec/classes/agent_spec.rb index 5b4ba054b1..ad166ebc62 100644 --- a/spec/classes/agent_spec.rb +++ b/spec/classes/agent_spec.rb @@ -5,15 +5,39 @@ context "on #{os}" do let(:facts) { facts } describe 'with default values for all parameters' do - it { should compile.with_all_deps } + # Unknown bug in rspec-puppet fails to compile windows paths + # when they are used for file source of sensu_ssl_ca, issue with windows mocking + # https://github.com/rodjek/rspec-puppet/issues/750 + if facts[:os]['family'] != 'windows' + it { should compile.with_all_deps } + end it { should contain_class('sensu')} it { should contain_class('sensu::agent')} + if facts[:os]['family'] == 'windows' + sensu_agent_exe = "C:\\Program Files\\sensu\\sensu-agent\\bin\\sensu-agent.exe" + it { + should contain_exec('install-agent-service').with({ + 'command' => "C:\\windows\\system32\\cmd.exe /c \"\"#{sensu_agent_exe}\" service install --config-file \"#{platforms[facts[:osfamily]][:agent_config_path]}\" --log-file \"#{platforms[facts[:osfamily]][:log_file]}\"\"", + 'unless' => 'C:\\windows\\system32\\sc.exe query SensuAgent', + 'before' => 'Service[sensu-agent]', + 'require' => [ + 'Package[sensu-go-agent]', + 'File[sensu_agent_config]', + ], + }) + } + else + it { should_not contain_exec('install-agent-service') } + end + it { should_not contain_archive('sensu-go-agent.msi') } + it { should contain_package('sensu-go-agent').with({ 'ensure' => 'installed', - 'name' => 'sensu-go-agent', + 'name' => platforms[facts[:osfamily]][:agent_package_name], + 'source' => nil, 'before' => 'File[sensu_etc_dir]', 'require' => platforms[facts[:osfamily]][:package_require], }) @@ -23,14 +47,17 @@ |--- |backend-url: |- wss://localhost:8081 - |trusted-ca-file: "/etc/sensu/ssl/ca.crt" + |trusted-ca-file: #{platforms[facts[:osfamily]][:ca_path_yaml]} END it { should contain_file('sensu_agent_config').with({ 'ensure' => 'file', - 'path' => '/etc/sensu/agent.yml', + 'path' => platforms[facts[:osfamily]][:agent_config_path], 'content' => agent_content, + 'owner' => platforms[facts[:osfamily]][:user], + 'group' => platforms[facts[:osfamily]][:group], + 'mode' => platforms[facts[:osfamily]][:agent_config_mode], 'require' => 'Package[sensu-go-agent]', 'notify' => 'Service[sensu-agent]', }) @@ -40,12 +67,59 @@ should contain_service('sensu-agent').with({ 'ensure' => 'running', 'enable' => true, - 'name' => 'sensu-agent', + 'name' => platforms[facts[:osfamily]][:agent_service_name], 'subscribe' => 'Class[Sensu::Ssl]', }) } end + context 'when package_source defined as URL' do + let(:params) {{ package_source: 'https://foo/sensu-go-agent.msi' }} + if facts[:os]['family'] == 'windows' + it { + should contain_archive('sensu-go-agent.msi').with({ + 'source' => 'https://foo/sensu-go-agent.msi', + 'path' => 'C:\\\\sensu-go-agent.msi', + 'extract'=> 'false', + 'cleanup'=> 'false', + 'before' => 'Package[sensu-go-agent]', + }) + } + it { should contain_package('sensu-go-agent').with_source('C:\\\\sensu-go-agent.msi') } + else + it { should_not contain_archive('sensu-go-agent.msi') } + it { should contain_package('sensu-go-agent').without_source } + end + end + + context 'when package_source defined as puppet' do + let(:params) {{ package_source: 'puppet:///modules/profile/sensu-go-agent.msi' }} + if facts[:os]['family'] == 'windows' + it { + should contain_file('sensu-go-agent.msi').with({ + 'ensure' => 'file', + 'source' => 'puppet:///modules/profile/sensu-go-agent.msi', + 'path' => 'C:\\\\sensu-go-agent.msi', + 'before' => 'Package[sensu-go-agent]', + }) + } + it { should contain_package('sensu-go-agent').with_source('C:\\\\sensu-go-agent.msi') } + else + it { should_not contain_archive('sensu-go-agent.msi') } + it { should contain_package('sensu-go-agent').without_source } + end + end + + context 'when package_source is local' do + let(:params) {{ package_source: 'C:\\sensu-go-agent.msi' }} + it { should_not contain_archive('sensu-go-agent.msi') } + if facts[:os]['family'] == 'windows' + it { should contain_package('sensu-go-agent').with_source('C:\\sensu-go-agent.msi') } + else + it { should contain_package('sensu-go-agent').without_source } + end + end + context 'with use_ssl => false' do let(:pre_condition) do "class { 'sensu': use_ssl => false }" @@ -60,8 +134,11 @@ it { should contain_file('sensu_agent_config').with({ 'ensure' => 'file', - 'path' => '/etc/sensu/agent.yml', + 'path' => platforms[facts[:osfamily]][:agent_config_path], 'content' => agent_content, + 'owner' => platforms[facts[:osfamily]][:user], + 'group' => platforms[facts[:osfamily]][:group], + 'mode' => platforms[facts[:osfamily]][:agent_config_mode], 'show_diff' => 'true', 'require' => 'Package[sensu-go-agent]', 'notify' => 'Service[sensu-agent]', @@ -98,7 +175,12 @@ context "with backends => #{backends}" do let(:params) { { :backends => backends } } - it { should compile.with_all_deps } + # Unknown bug in rspec-puppet fails to compile windows paths + # when they are used for file source of sensu_ssl_ca, issue with windows mocking + # https://github.com/rodjek/rspec-puppet/issues/750 + if facts[:os]['family'] != 'windows' + it { should compile.with_all_deps } + end if backends[0] =~ /(ws|wss):\/\// backend = backends[0] @@ -110,13 +192,13 @@ |--- |backend-url: |- #{backend} - |trusted-ca-file: "/etc/sensu/ssl/ca.crt" + |trusted-ca-file: #{platforms[facts[:osfamily]][:ca_path_yaml]} END it { should contain_file('sensu_agent_config').with({ 'ensure' => 'file', - 'path' => '/etc/sensu/agent.yml', + 'path' => platforms[facts[:osfamily]][:agent_config_path], 'content' => agent_content, 'require' => 'Package[sensu-go-agent]', 'notify' => 'Service[sensu-agent]', diff --git a/spec/classes/backend_spec.rb b/spec/classes/backend_spec.rb index 488a067705..8a77208754 100644 --- a/spec/classes/backend_spec.rb +++ b/spec/classes/backend_spec.rb @@ -2,6 +2,10 @@ describe 'sensu::backend', :type => :class do on_supported_os({facterversion: '3.8.0'}).each do |os, facts| + # Windows is not supported for backend + if facts[:os]['family'] == 'windows' + next + end context "on #{os}" do let(:facts) { facts } let(:node) { 'test.example.com' } @@ -117,6 +121,9 @@ 'ensure' => 'file', 'path' => '/etc/sensu/backend.yml', 'content' => backend_content, + 'owner' => 'sensu', + 'group' => 'sensu', + 'mode' => '0640', 'show_diff' => 'true', 'require' => 'Package[sensu-go-backend]', 'notify' => 'Service[sensu-backend]', diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 44b238b48d..de88bf91cd 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -5,16 +5,25 @@ context "on #{os}" do let(:facts) { facts } context 'with default values for all parameters' do - it { should compile } + # Unknown bug in rspec-puppet fails to compile windows paths + # when they are used for file source of sensu_ssl_ca, issue with windows mocking + # https://github.com/rodjek/rspec-puppet/issues/750 + if facts[:os]['family'] != 'windows' + it { should compile } + end it { should contain_class('sensu')} - it { should contain_class('sensu::repo')} + if facts[:os]['family'] == 'windows' + it { should_not contain_class('sensu::repo')} + else + it { should contain_class('sensu::repo')} + end it { should contain_class('sensu::ssl') } it { should contain_file('sensu_etc_dir').with({ 'ensure' => 'directory', - 'path' => '/etc/sensu', + 'path' => platforms[facts[:osfamily]][:etc_dir], 'purge' => true, 'recurse' => true, 'force' => true, diff --git a/spec/classes/plugins_spec.rb b/spec/classes/plugins_spec.rb index 195a398e79..3420c03f05 100644 --- a/spec/classes/plugins_spec.rb +++ b/spec/classes/plugins_spec.rb @@ -4,6 +4,12 @@ on_supported_os({facterversion: '3.8.0'}).each do |os, facts| context "on #{os}" do let(:facts) { facts } + if facts[:os]['family'] == 'windows' + it 'should fail' do + is_expected.to compile.and_raise_error(/is not supported/) + end + next + end context 'with default values for all parameters' do it { should compile } diff --git a/spec/classes/repo_community_spec.rb b/spec/classes/repo_community_spec.rb index 482a6f8202..533ef5497a 100644 --- a/spec/classes/repo_community_spec.rb +++ b/spec/classes/repo_community_spec.rb @@ -2,6 +2,10 @@ describe 'sensu::repo::community', :type => :class do on_supported_os({facterversion: '3.8.0'}).each do |os, facts| + # This class not used by Windows + if facts[:os]['family'] == 'windows' + next + end context "on #{os}" do let(:facts) { facts } case os diff --git a/spec/classes/repo_spec.rb b/spec/classes/repo_spec.rb index 2c5182799c..d039347854 100644 --- a/spec/classes/repo_spec.rb +++ b/spec/classes/repo_spec.rb @@ -2,6 +2,10 @@ describe 'sensu::repo', :type => :class do on_supported_os({facterversion: '3.8.0'}).each do |os, facts| + # repo class not used for Windows + if facts[:os]['family'] == 'windows' + next + end context "on #{os}" do let(:facts) { facts } case os diff --git a/spec/classes/ssl_spec.rb b/spec/classes/ssl_spec.rb index ae61f9655e..768a0bb1da 100644 --- a/spec/classes/ssl_spec.rb +++ b/spec/classes/ssl_spec.rb @@ -5,7 +5,12 @@ context "on #{os}" do let(:facts) { facts } context 'with default values for all parameters' do - it { should compile } + # Unknown bug in rspec-puppet fails to compile windows paths + # when they are used for file source of sensu_ssl_ca, issue with windows mocking + # https://github.com/rodjek/rspec-puppet/issues/750 + if facts[:os]['family'] != 'windows' + it { should compile } + end it { should create_class('sensu::ssl') } it { should contain_class('sensu') } @@ -13,25 +18,25 @@ it { should contain_file('sensu_ssl_dir').with({ 'ensure' => 'directory', - 'path' => '/etc/sensu/ssl', + 'path' => platforms[facts[:osfamily]][:ssl_dir], 'purge' => true, 'recurse' => true, 'force' => true, - 'owner' => 'sensu', - 'group' => 'sensu', - 'mode' => '0700', + 'owner' => platforms[facts[:osfamily]][:user], + 'group' => platforms[facts[:osfamily]][:group], + 'mode' => platforms[facts[:osfamily]][:ssl_dir_mode], }) } it { should contain_file('sensu_ssl_ca').with({ 'ensure' => 'file', - 'path' => '/etc/sensu/ssl/ca.crt', - 'owner' => 'sensu', - 'group' => 'sensu', - 'mode' => '0644', + 'path' => platforms[facts[:osfamily]][:ca_path], + 'owner' => platforms[facts[:osfamily]][:user], + 'group' => platforms[facts[:osfamily]][:group], + 'mode' => platforms[facts[:osfamily]][:ca_mode], 'show_diff' => 'false', - 'source' => '/dne/ca.pem', + 'source' => facts['puppet_localcacert'], }) } end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f6aff01bd0..31e670d366 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -45,7 +45,6 @@ :operatingsystem => 'RedHat', :operatingsystemmajrelease => '7', :fqdn => 'testfqdn.example.com', - :puppet_localcacert => '/dne/ca.pem', :puppet_hostcert => '/dne/cert.pem', :puppet_hostprivkey => '/dne/key.pem', } @@ -56,17 +55,68 @@ ] end +add_custom_fact :puppet_localcacert, ->(os, facts) { + case facts[:osfamily] + when 'windows' + "C:\\ProgramData\\ca.crt" + else + '/dne/ca.pem' + end +} + def platforms { 'Debian' => { :package_require => ['Class[Sensu::Repo]', 'Class[Apt::Update]'], :plugins_package_require => ['Class[Sensu::Repo::Community]', 'Class[Apt::Update]'], :plugins_dependencies => ['make','gcc','g++','libssl-dev'], + agent_package_name: 'sensu-go-agent', + :agent_config_path => '/etc/sensu/agent.yml', + agent_config_mode: '0640', + etc_dir: '/etc/sensu', + ssl_dir: '/etc/sensu/ssl', + ca_path: '/etc/sensu/ssl/ca.crt', + ca_path_yaml: '"/etc/sensu/ssl/ca.crt"', + user: 'sensu', + group: 'sensu', + ssl_dir_mode: '0700', + ca_mode: '0644', + agent_service_name: 'sensu-agent', + log_file: nil, }, 'RedHat' => { :package_require => ['Class[Sensu::Repo]'], :plugins_package_require => ['Class[Sensu::Repo::Community]'], :plugins_dependencies => ['make','gcc','gcc-c++','openssl-devel'], + agent_package_name: 'sensu-go-agent', + :agent_config_path => '/etc/sensu/agent.yml', + agent_config_mode: '0640', + ssl_dir: '/etc/sensu/ssl', + etc_dir: '/etc/sensu', + ca_path: '/etc/sensu/ssl/ca.crt', + ca_path_yaml: '"/etc/sensu/ssl/ca.crt"', + user: 'sensu', + group: 'sensu', + ssl_dir_mode: '0700', + ca_mode: '0644', + agent_service_name: 'sensu-agent', + log_file: nil, }, + 'windows' => { + agent_package_name: 'Sensu Agent', + :agent_config_path => 'C:\ProgramData\Sensu\config\agent.yml', + agent_config_mode: nil, + etc_dir: 'C:\\ProgramData\\Sensu\\config', + ssl_dir: 'C:\\ProgramData\\Sensu\\config\\ssl', + ca_path: 'C:\\ProgramData\\Sensu\\config\\ssl\\ca.crt', + ca_path_yaml: 'C:\\ProgramData\\Sensu\\config\\ssl\\ca.crt', + user: nil, + group: nil, + ssl_dir_mode: nil, + ca_mode: nil, + plugins_dependencies: [], + agent_service_name: 'SensuAgent', + log_file: 'C:\ProgramData\sensu\log\sensu-agent.log', + } } end diff --git a/spec/spec_helper_acceptance_windows.rb b/spec/spec_helper_acceptance_windows.rb new file mode 100644 index 0000000000..0c5d96b572 --- /dev/null +++ b/spec/spec_helper_acceptance_windows.rb @@ -0,0 +1,8 @@ +require 'serverspec' + +set :backend, :cmd + +RSpec.configure do |c| + # Readable test descriptions + c.formatter = :documentation +end diff --git a/spec/unit/facter/sensu_facts_spec.rb b/spec/unit/facter/sensu_facts_spec.rb index 28651bdd76..a8ca2c6362 100644 --- a/spec/unit/facter/sensu_facts_spec.rb +++ b/spec/unit/facter/sensu_facts_spec.rb @@ -5,6 +5,7 @@ context 'sensu_agent fact' do it 'returns version information' do allow(SensuFacts).to receive(:which).with('sensu-agent').and_return('/bin/sensu-agent') + allow(Facter).to receive(:value).with(:kernel).and_return('Linux') allow(Facter::Core::Execution).to receive(:exec).with('/bin/sensu-agent version 2>&1').and_return("sensu-agent version 5.1.0#b2ea9fc, build b2ea9fcdb21e236e6e9a7de12225a6d90c786c57, built '2018-12-18T21:31:11+0000'") SensuFacts.add_agent_facts expect(Facter.fact(:sensu_agent).value).to eq({'version' => '5.1.0', 'build' => 'b2ea9fcdb21e236e6e9a7de12225a6d90c786c57', 'built' => '2018-12-18T21:31:11+0000'}) @@ -12,11 +13,20 @@ it 'returns version information for 5.2.0' do allow(SensuFacts).to receive(:which).with('sensu-agent').and_return('/bin/sensu-agent') + allow(Facter).to receive(:value).with(:kernel).and_return('Linux') allow(Facter::Core::Execution).to receive(:exec).with('/bin/sensu-agent version 2>&1').and_return("sensu-agent version 5.2.0#21a24d9, build 21a24d9cf073863d6c2b02c0b7acaae673e4f597, built 2019-02-06T22:08:44Z") SensuFacts.add_agent_facts expect(Facter.fact(:sensu_agent).value).to eq({'version' => '5.2.0', 'build' => '21a24d9cf073863d6c2b02c0b7acaae673e4f597', 'built' => '2019-02-06T22:08:44Z'}) end + it 'returns version information for windows' do + allow(SensuFacts).to receive(:which).with('sensu-agent').and_return('C:\Program Files\sensu\sensu-agent\bin\sensu-agent.exe') + allow(Facter).to receive(:value).with(:kernel).and_return('windows') + allow(Facter::Core::Execution).to receive(:exec).with('"C:\Program Files\sensu\sensu-agent\bin\sensu-agent.exe" version').and_return("sensu-agent version 5.2.0#21a24d9, build 21a24d9cf073863d6c2b02c0b7acaae673e4f597, built 2019-02-06T22:08:44Z") + SensuFacts.add_agent_facts + expect(Facter.fact(:sensu_agent).value).to eq({'version' => '5.2.0', 'build' => '21a24d9cf073863d6c2b02c0b7acaae673e4f597', 'built' => '2019-02-06T22:08:44Z'}) + end + it 'returns nil' do allow(SensuFacts).to receive(:which).with('sensu-agent').and_return(nil) SensuFacts.add_agent_facts @@ -27,6 +37,7 @@ context 'sensu_backend fact' do it 'returns version information' do allow(SensuFacts).to receive(:which).with('sensu-backend').and_return('/bin/sensu-backend') + allow(Facter).to receive(:value).with(:kernel).and_return('Linux') allow(Facter::Core::Execution).to receive(:exec).with('/bin/sensu-backend version 2>&1').and_return("sensu-backend version 5.1.0#b2ea9fc, build b2ea9fcdb21e236e6e9a7de12225a6d90c786c57, built '2018-12-18T21:31:11+0000'") SensuFacts.add_backend_facts expect(Facter.fact(:sensu_backend).value).to eq({'version' => '5.1.0', 'build' => 'b2ea9fcdb21e236e6e9a7de12225a6d90c786c57', 'built' => '2018-12-18T21:31:11+0000'}) @@ -42,6 +53,7 @@ context 'sensuctl fact' do it 'returns version information' do allow(SensuFacts).to receive(:which).with('sensuctl').and_return('/bin/sensuctl') + allow(Facter).to receive(:value).with(:kernel).and_return('Linux') allow(Facter::Core::Execution).to receive(:exec).with('/bin/sensuctl version 2>&1').and_return("sensuctl version 5.1.0#b2ea9fc, build b2ea9fcdb21e236e6e9a7de12225a6d90c786c57, built '2018-12-18T21:31:11+0000'") SensuFacts.add_sensuctl_facts expect(Facter.fact(:sensuctl).value).to eq({'version' => '5.1.0', 'build' => 'b2ea9fcdb21e236e6e9a7de12225a6d90c786c57', 'built' => '2018-12-18T21:31:11+0000'}) diff --git a/tests/provision_basic_win.ps1 b/tests/provision_basic_win.ps1 new file mode 100644 index 0000000000..d490dac0c7 --- /dev/null +++ b/tests/provision_basic_win.ps1 @@ -0,0 +1,47 @@ +# Variables +$log = "C:/vagrant/puppet-agent.log" +$agent_url = "https://downloads.puppetlabs.com/windows/puppet5/puppet-agent-x64-latest.msi" +if ( Get-Command "puppet" -ErrorAction SilentlyContinue ) { + Write-Output "Puppet is already installed. Skipping install of $agent_url" +} else { + Write-Output "Installing Puppet from $agent_url" + Write-Output "Log will be written to $log" + if ( Test-Path $log ) { Remove-Item $log } + # Install puppet + Start-Process msiexec.exe -Wait -NoNewWindow -ArgumentList @("/i", "$agent_url", "/qn", "/l*", "$log") +} + +$hiera_file = "C:\ProgramData\PuppetLabs\puppet\etc\hiera.yaml" +$moduledir = "C:\ProgramData\PuppetLabs\code\environments\production\modules" +$vagrant = "C:\vagrant" +$hiera_content = @' +--- +version: 5 +hierarchy: + - name: Common + path: common.yaml +defaults: + data_hash: yaml_data + datadir: hieradata +'@ + +# Create the sensuclassic module directory. We only copy certain directories because +New-Item -Path $moduledir -ItemType directory -Force | Out-Null +# Remove the link if it exists. Remove-Item can't deal with links. +if ( Test-Path "$moduledir\sensu" ) { cmd /c rmdir "$moduledir\sensu" } +# Create a symbolic link. Requires Powereshell 2.0 or greater. +cmd /c mklink /d "$moduledir\sensu" "$vagrant" + +# Avoid deprecation warning (ASCII encoding avoids YAML UTF-8 error) +$hiera_content | Out-File -FilePath "$hiera_file" -Encoding ascii + +# There are multiple power shell scripts. The first installs Puppet, but +# puppet is not in the PATH. The second invokes a new shell which will have +# Puppet in the PATH. +$env:PATH += ";C:\Program Files\Puppet Labs\Puppet\bin" +iex "puppet module install puppetlabs-stdlib" +iex "puppet module install puppet-archive" +New-Item -Path "C:\ProgramData\PuppetLabs\puppet\etc\ssl" -ItemType directory -Force | Out-Null +Copy-Item -Path "C:\vagrant\tests\ssl\*" -Destination "C:\ProgramData\PuppetLabs\puppet\etc\ssl\" -Recurse -Force + +iex "puppet resource host sensu-backend.example.com ensure=present ip=192.168.52.10" diff --git a/tests/sensu-agent.pp b/tests/sensu-agent.pp index aa400f75c1..fbc2463d78 100644 --- a/tests/sensu-agent.pp +++ b/tests/sensu-agent.pp @@ -1,3 +1,10 @@ +if $facts['os']['family'] == 'windows' { + $package_source = 'https://s3-us-west-2.amazonaws.com/sensu.io/sensu-go/5.7.0/sensu-go-agent_5.7.0.2380_en-US.x64.msi' +} else { + $package_source = undef +} + class { '::sensu::agent': - backends => ['sensu-backend.example.com:8081'] + backends => ['sensu-backend.example.com:8081'], + package_source => $package_source, }