chef/cookbooks/cpe_metricbeat/resources/cpe_metricbeat.rb (191 lines of code) (raw):
#
# Cookbook:: cpe_metricbeat
# Resources:: cpe_metricbeat
#
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
#
# Copyright:: (c) 2019-present, Uber Technologies, Inc.
# All rights reserved.
#
# This source code is licensed under the Apache 2.0 license found in the
# LICENSE file in the root directory of this source tree.
#
unified_mode true
resource_name :cpe_metricbeat
provides :cpe_metricbeat, :os => ['darwin', 'linux', 'windows']
default_action :manage
action :manage do
install if install?
configure if configure?
cleanup if !install? && !configure?
end
action_class do # rubocop:disable Metrics/BlockLength
def install?
node['cpe_metricbeat']['install']
end
def configure?
node['cpe_metricbeat']['configure']
end
def install
return if pkg_info.nil? || pkg_info.empty? || pkg_name.nil? || pkg_name.empty?
# Create metricbeat directory
create_metricbeat_directory
# Remove older zip archive from cache
remove_stale_cache unless current_version?
# Stop and remove the service if we are updating binaries
remove_metricbeat_service if windows?
# Download and extract zip archive
cpe_remote_zip 'metricbeat_zip' do
zip_name pkg_name
zip_checksum pkg_info['checksum']
folder_name 'metricbeat'
extract_location metricbeat_dir
end
end
def configure
# Return if no configuration is specified
if metricbeat_conf.empty? || metricbeat_conf.nil?
Chef::Log.warn('config is not populated, skipping configuration')
return
end
setup_macos_service if macos?
setup_debian_service if debian?
prefix = node['cpe_launchd']['prefix'] || 'com.uber.chef'
service_info = value_for_platform_family(
'mac_os_x' => { 'launchd' => "#{prefix}.metricbeat" },
'debian' => { 'systemd_unit' => 'metricbeat.service' },
'windows' => { 'windows_service' => 'metricbeat' },
'default' => nil,
)
service_type, service_name = service_info.first
# Make a fake service to notify for macOS since we are using cpe_launchd
if macos?
launchd service_name do
action :nothing
only_if { ::File.exist?("/Library/LaunchDaemons/#{service_name}.plist") }
subscribes :restart, 'cpe_remote_zip[metricbeat_zip]'
end
end
config = ::File.join(metricbeat_dir, 'metricbeat.chef.yml')
file config do
owner root_owner
group node['root_group']
content YAML.dump(metricbeat_conf)
notifies :restart, "#{service_type}[#{service_name}]"
end
# Because windows services are annoying and start immediately, so this
# must come after the config is placed
setup_windows_service if windows?
end
def setup_windows_service
bin_path = "#{metricbeat_bin} -c #{metricbeat_dir}\\metricbeat.chef.yml"
windows_service 'metricbeat' do
action %i[create start]
binary_path_name bin_path
startup_type :automatic
delayed_start true
only_if { ::File.exists?(metricbeat_bin) }
end
end
def setup_debian_service
metricbeat_dir = node['cpe_metricbeat']['dir']
unit = {
'Unit' => {
'Description' =>
'metricbeat sends log files to Logstash or directly to Elasticsearch.',
'Documentation' => ['https://www.elastic.co/products/beats/metricbeat'],
'Wants' => 'network-online.target',
'After' => 'network-online.target',
},
'Service' => {
'Type' => 'simple',
'ExecStart' => "#{metricbeat_dir}/metricbeat" \
" -c #{metricbeat_dir}/metricbeat.chef.yml",
'Restart' => 'always',
},
'Install' => {
'WantedBy' => 'multi-user.target',
},
}
systemd_unit 'metricbeat.service' do
content(unit)
action %i[create start]
subscribes :restart, 'cpe_remote_zip[metricbeat_zip]'
end
end
def setup_macos_service
metricbeat_dir = node['cpe_metricbeat']['dir']
ld = {
'program_arguments' => [
"#{metricbeat_dir}/metricbeat",
'-c',
"#{metricbeat_dir}/metricbeat.chef.yml",
],
'disabled' => false,
'run_at_load' => true,
'keep_alive' => true,
'type' => 'daemon',
}
node.default['cpe_launchd']['metricbeat'] = ld
end
def cleanup_windows
windows_service 'metricbeat' do
action %i[stop delete]
end
end
def cleanup_debian
systemd_unit 'metricbeat.service.app' do
action %i[stop delete]
end
end
def cleanup
cleanup_debian if debian?
cleanup_windows if windows?
directory node['cpe_metricbeat']['dir'] do
action :delete
recursive true
end
end
def current_version?
if metricbeat_exists?
if macos? || debian?
return shell_out("#{metricbeat_bin} version").stdout.include?(pkg_info['version'])
elsif windows?
powershell_cmd = "(Get-Item #{metricbeat_bin}).VersionInfo.FileVersion"
return powershell_out(powershell_cmd).stdout.include?(pkg_info['version'])
end
end
return false
end
def metricbeat_exists?
# Check if metricbeat Exist
Dir.exist?(metricbeat_dir) && ::File.exist?(metricbeat_bin)
end
def metricbeat_dir
node['cpe_metricbeat']['dir']
end
def metricbeat_bin
::File.join(metricbeat_dir, node['cpe_metricbeat']['bin'])
end
def metricbeat_conf
node['cpe_metricbeat']['config'].to_h.compact
end
def pkg_info
pf = node['platform_family']
node['cpe_metricbeat']['zip_info'][pf].compact.reject { |_k, v| v.nil? }
end
def pkg_name
value_for_platform_family(
'mac_os_x' => "metricbeat-#{pkg_info['version']}-darwin-x86_64.zip",
'debian' => "metricbeat-#{pkg_info['version']}-linux-x86_64.zip",
'windows' => "metricbeat-#{pkg_info['version']}-windows-x86_64.zip",
'default' => nil,
)
end
def create_metricbeat_directory
directory metricbeat_dir do
recursive true
owner root_owner
group node['root_group']
end
end
def metricbeat_cache
if macos? || debian?
::File.join(Chef::Config[:file_cache_path], 'remote_zip/opt/metricbeat')
elsif windows?
::File.join(Chef::Config[:file_cache_path], 'remote_zip\\C\\ProgramData\\metricbeat')
end
end
def remove_stale_cache
directory metricbeat_cache do
recursive true
action :delete
ignore_failure true if windows?
end
end
def remove_metricbeat_service
windows_service 'metricbeat' do
action %i[stop delete]
not_if { current_version? }
end
end
end