#!/usr/bin/env ruby require 'rubygems' begin gem 'instrumental_agent' rescue Gem::LoadError puts "Requires the Instrumental Agent gem:\n" puts ' gem install instrumental_agent' exit 1 end require 'etc' require 'instrumental_agent' require 'fileutils' require 'optparse' require 'socket' $: << File.join(File.dirname(__FILE__), "..", "lib") require 'instrumental_tools/version' require 'instrumental_tools/server_controller' def require_api_key(options, parser) if options[:api_key].to_s.strip.empty? print parser.help exit 1 end end default_script_directory = File.join(Dir.home, '.instrumental_scripts') default_command = :foreground options = { :collector => 'collector.instrumentalapp.com', :port => '8000', :hostname => Socket.gethostname, :pid_location => File.join(Dir.home, 'instrument_server.pid'), :log_location => File.join(Dir.home, 'instrument_server.log'), :enable_scripts => false, :script_location => default_script_directory, :report_interval => 30, :debug => false } option_parser = OptionParser.new do |opts| opts.banner = <<-EOBANNER Usage: instrument_server -k API_KEY [options] [#{ServerController::COMMANDS.join('|')}]" Default command: #{default_command.to_s} EOBANNER opts.on('-k', '--api_key API_KEY', 'API key of your project') do |api_key| options[:api_key] = api_key end opts.on('-c', '--collector COLLECTOR[:PORT]', "Collector (default #{options[:collector]}:#{options[:port]})") do |collector| address, port = collector.split(':') options[:collector] = address options[:port] = port if port end opts.on('-H', '--hostname HOSTNAME', "Hostname to report as (default #{options[:hostname]})") do |hostname| options[:hostname] = hostname end opts.on('-p', '--pid LOCATION', "Where daemon PID file is located (default #{options[:pid_location]})") do |pid_location| options[:pid_location] = pid_location end opts.on('-l', '--log LOCATION', "Where to put the instrument_server log file (default #{options[:log_location]})") do |log_location| options[:log_location] = log_location end opts.on('-r', '--report-interval INTERVAL_IN_SECONDS', "How often to report metrics to Instrumental (default #{options[:report_interval]})") do |interval| options[:report_interval] = interval.to_i end opts.on('-e', '--enable-scripts', "Enable custom metric gathering from local scripts (default #{options[:enable_scripts]})") do options[:enable_scripts] = true end opts.on('-s', '--script-location PATH_TO_DIRECTORY', "Directory where local scripts for custom metrics are located (default #{options[:script_location]})") do |path| options[:script_location] = path end opts.on('--debug', "Print all sent metrics to the log") do options[:debug] = true end opts.on('-h', '--help', 'Display this screen') do puts opts exit end end option_parser.parse! command = ARGV.first && ARGV.first.to_sym command ||= default_command options[:api_key] ||= ENV["INSTRUMENTAL_TOKEN"] if options[:pid_location].to_s.strip.empty? raise "You must provide a valid path for the PID file (-p PID_PATH)" end if !File.directory?(File.dirname(options[:pid_location])) raise "The directory specified for the pid file #{options[:pid_location]} does not exist, please create" end if options[:log_location].to_s.strip.empty? raise "You must provide a valid path for the log file (-l LOG_PATH)" end if !File.directory?(File.dirname(options[:log_location])) raise "The directory specified for the log file #{options[:log_location]} does not exist, please create" end if options[:report_interval].to_i < 1 raise "You must specify a reporting interval greater than 0" end if options[:enable_scripts] if options[:script_location].to_s.strip.empty? raise "You must specify a valid directory to execute custom scripts in." end if options[:script_location] == default_script_directory FileUtils.mkdir_p(default_script_directory, :mode => 0700) end if !File.directory?(options[:script_location]) raise "The directory #{options[:script_location]} does not exist." end stat = File::Stat.new(File.expand_path(options[:script_location])) if !stat.owned? || ((stat.mode & 0xFFF) ^ 0O700) != 0 uid = Process.uid username = Etc.getpwuid(uid).name raise "The directory #{options[:script_location]} is writable/readable by others. Please ensure it is only writable / readable by user/uid #{username}/#{uid}" end end if [:start, :restart, :foreground].include?(command) require_api_key(options, option_parser) end running_as_daemon = [:start, :restart].include?(command) controller = ServerController.spawn( :name => File.basename(__FILE__), :path => Dir.pwd, :pid_file => options[:pid_location], :verbose => true, :log_file => options[:log_location], :run_options => options.merge(:daemon => running_as_daemon) ) if ServerController::COMMANDS.include?(command) controller.send command else raise "Command must be one of: #{ServerController::COMMANDS.join(', ')}" end if running_as_daemon begin Timeout.timeout(5) do Process.waitpid(controller.pid) end rescue Timeout::Error end if !controller.running? raise "Failed to start process, see #{options[:log_location]}" end end