Skip to content

Commit

Permalink
merged 6119, 6118, 6097: change deployment script not to use rails
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Kayser committed Jan 21, 2009
1 parent 0de3ce7 commit 35ae910
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ spec = Gem::Specification.new do |s|
s.email = EMAIL
s.homepage = HOMEPAGE
s.require_path = 'lib'
s.files = %w(install.rb LICENSE README newrelic.yml Rakefile) + Dir.glob("{lib,recipes,test,ui}/**/*")
s.files = %w(install.rb LICENSE README newrelic.yml Rakefile) + Dir.glob("{lib,bin,recipes,test,ui}/**/*")

end

Expand Down
4 changes: 4 additions & 0 deletions bin/newrelic_cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby
# executes one of the commands in the new_relic/commands directory
# pass the name of the command as an argument
require File.dirname(__FILE__) + '/../lib/new_relic/commands/new_relic_commands'
10 changes: 1 addition & 9 deletions lib/new_relic/agent/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -660,15 +660,7 @@ def invoke_remote(method, *args)
# to go for higher compression instead, we could use Zlib::BEST_COMPRESSION and
# pay a little more CPU.
post_data = Zlib::Deflate.deflate(Marshal.dump(args), Zlib::BEST_SPEED)

# Proxy returns regular HTTP if @proxy_host is nil (the default)
http = Net::HTTP::Proxy(config.proxy_server.host, config.proxy_server.port,
config.proxy_server.user, config.proxy_server.password).new(config.server.host, config.server.port)
if config.use_ssl?
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

http = config.http_connection
http.read_timeout = @request_timeout

# params = {:method => method, :license_key => license_key, :protocol_version => PROTOCOL_VERSION }
Expand Down
110 changes: 110 additions & 0 deletions lib/new_relic/commands/deployments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# This is a class for executing commands related to deployment
# events. It runs without loading the rails environment

$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__),"..",".."))
require 'yaml'
require 'net/http'

# We need to use the Config object but we don't want to load
# the rails/merb environment. The defined? clause is so that
# it won't load it twice, something it does when run inside a test
require 'new_relic/config' unless defined? NewRelic::Config

module NewRelic
module Commands
class Deployments

attr_reader :config
def self.command; "deployments"; end

# Initialize the deployment uploader with command line args.
# Use -h to see options. Will possibly exit the VM
def initialize command_line_args
@config = NewRelic::Config.instance
@user = ENV['USER']
@description = options.parse(command_line_args).join " "
@application_id ||= config.app_name || config.env || 'development'
end

# Run the Deployment upload in RPM via Active Resource.
# Will possibly print errors and exit the VM
def run
begin
@description = nil if @description.empty?
create_params = {}
{
:application_id => @application_id,
:host => Socket.gethostname,
:description => @description,
:user => @user,
:revision => @revision,
:changelog => @changelog
}.each do |k, v|
create_params["deployment[#{k}]"] = v unless v.nil? || v == ''
end
http = config.http_connection(config.api_server)

uri = "/deployments.xml"

raise "license_key was not set in newrelic.yml for #{config.env}" if config['license_key'].nil?
request = Net::HTTP::Post.new(uri, 'HTTP_X_LICENSE_KEY' => config['license_key'])
request.content_type = "application/octet-stream"

request.set_form_data(create_params)

response = http.request(request)

if response.is_a? Net::HTTPSuccess
info "Recorded deployment to NewRelic RPM (#{@description || Time.now })"
else
err "Unexpected response from server: #{response.code}: #{response.message}"
# TODO look for errors in xml response
just_exit -1
end
rescue SystemCallError, SocketError => e
# These include Errno connection errors
err "Transient error attempting to connect to #{config.api_server} (#{e})"
just_exit -2
rescue Exception => e
err "Unexpected error attempting to connect to #{config.api_server} (#{e})"
info e.backtrace.join("\n")
just_exit 1
end
end

private

def options
OptionParser.new "Usage: #{self.class.command} [OPTIONS] [description] ", 40 do |o|
o.separator "OPTIONS:"
o.on("-a", "--appname=DIR", String,
"Set the application name.",
"Default is app_name setting in newrelic.yml") { |@application_id| }
o.on("-e ENV", String,
"Override the (RAILS|MERB|RUBY)_ENV setting",
"currently: #{config.env}") { |env| config.env=env }
o.on("-u", "--user=USER", String,
"Specify the user deploying.",
"Default: #{@user}") { |@user| }
o.on("-r", "--revision=REV", String,
"Specify the revision being deployed") { |@revision | }
o.on("-c", "--changes",
"Read in a change log from the standard input") { @changelog = STDIN.read }
o.on("-?", "Print this help") { info o.help; just_exit }
o.separator ""
o.separator 'description = "short text"'
end
end

def info message
STDOUT.puts message
end
def err message
STDERR.puts message
end
def just_exit status=0
exit status
end
end
end
end
25 changes: 25 additions & 0 deletions lib/new_relic/commands/new_relic_commands.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'optparse'

# Run the command given by the first argument. Right
# now all we have is deployments. We hope to have other
# kinds of events here later.

libdir = File.expand_path(File.join(File.dirname(__FILE__), '..','..'))
command_list = Dir[File.join(libdir,'new_relic','commands','*.rb')].map{|command| command =~ /.*\/(.*)\.rb/ && $1}
command_list.delete 'new_relic_commands'
extra = []
options = ARGV.options do |opts|
script_name = File.basename($0)
opts.banner = "Usage: #{__FILE__} #{ command_list.join(" | ")} [options]"
opts.separator "use -h to see detailed command options"
opts
end
extra = options.order!
command = extra.shift
if !command_list.include?(command)
STDERR.puts options
else
require File.join(libdir, 'new_relic','commands', command + ".rb")
command_class = NewRelic::Commands.const_get(command.capitalize)
command_class.new(extra).run
end
32 changes: 25 additions & 7 deletions lib/new_relic/config.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require 'yaml'
#require 'new_relic/version'
require 'singleton'
require 'new_relic/agent'
require 'erb'

# Configuration supports the behavior of the agent which is dependent
Expand All @@ -23,7 +22,6 @@ def self.instance
@instance ||= new_instance
end

attr_reader :settings


@settings = nil
Expand All @@ -49,6 +47,15 @@ def [](key)
fetch(key)
end
####################################
def env=(env_name)
@env = env_name
@settings = @yaml[env_name]
end

def settings
@settings ||= @yaml[env] || {}
end

def []=(key, value)
settings[key] = value
end
Expand Down Expand Up @@ -92,7 +99,7 @@ def server

def api_server
@api_server ||=
NewRelic::Config::Server.new fetch('api_host', 'rpm.newrelic.com'), fetch('api_port', use_ssl? ? 443 : 80).to_i
NewRelic::Config::Server.new fetch('api_host', 'rpm.newrelic.com'), fetch('api_port', fetch('port', use_ssl? ? 443 : 80)).to_i
end

def proxy_server
Expand All @@ -101,8 +108,19 @@ def proxy_server
fetch('proxy_user', nil), fetch('proxy_pass', nil)
end

####################################

# Return the Net::HTTP with proxy configuration given the NewRelic::Config::Server object.
# Default is the collector but for api calls you need to pass api_server
def http_connection(host = server)
# Proxy returns regular HTTP if @proxy_host is nil (the default)
http = Net::HTTP::Proxy(proxy_server.host, proxy_server.port,
proxy_server.user, proxy_server.password).new(host.host, host.port)
if use_ssl?
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
http
end

def to_s
puts self.inspect
"Config[#{self.app}]"
Expand Down Expand Up @@ -142,7 +160,7 @@ def @log.format_message(severity, timestamp, progname, msg)
end

def local_env
@env ||= NewRelic::LocalEnvironment.new
@local_env ||= NewRelic::LocalEnvironment.new
end

# send the given message to STDERR so that it shows
Expand Down Expand Up @@ -223,7 +241,7 @@ def initialize
else
yaml = ERB.new(File.read(config_file)).result(binding)
end
@settings = YAML.load(yaml)[env] || {}
@yaml = YAML.load(yaml)
rescue ScriptError, StandardError => e
puts e
puts e.backtrace.join("\n")
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/config/merb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class NewRelic::Config::Merb < NewRelic::Config
def app; :merb; end

def env
::Merb.env
@env ||= ::Merb.env
end
def root
::Merb.root
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/config/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class NewRelic::Config::Rails < NewRelic::Config
def app; :rails; end

def env
RAILS_ENV
@env ||= RAILS_ENV
end
def root
RAILS_ROOT
Expand Down
15 changes: 14 additions & 1 deletion lib/new_relic/config/ruby.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
class NewRelic::Config::Ruby < NewRelic::Config
def app; :ruby; end
def env
ENV['RUBY_ENV'] || 'development'
@env ||= ENV['RUBY_ENV'] || ENV['RAILS_ENV'] || 'development'
end
def root
Dir['.']
end
# Check a sequence of file locations for newrelic.yml
def config_file
files = []
files << File.join(root,"config","newrelic.yml")
files << File.join(root,"newrelic.yml")
files << File.join(ENV["HOME"], ".newrelic", "newrelic.yml")
files << File.join(ENV["HOME"], "newrelic.yml")
files.each do | file |
return File.expand_path(file) if File.exists? file
end
return File.expand_path(files.first)
end

end
9 changes: 6 additions & 3 deletions recipes/newrelic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
log_command = source.log(from_revision)
# Because new_relic_api could be plugins or the gem dir, we rely
# on the lib path to find it.
script = [ 'load "new_relic_api.rb"' ] <<
## script = [ ' ] <<
script = [ 'vendor/plugins/newrelic_rpm/bin/newrelic_cmd' ] <<
"deployments" <<
"-u" << ENV['USER'] <<
"-e" << rails_env <<
"-r" << current_revision <<
"-c"
"-c"

script = script.map { | arg | "'#{arg}'" }.join(" ")
begin
run "cd #{current_release}; #{log_command} | script/runner -e #{rails_env} #{script}" do | io, stream_id, output |
run "cd #{current_release}; #{log_command} | ruby #{script}" do | io, stream_id, output |
logger.trace(output)
end
rescue CommandError
Expand Down
30 changes: 23 additions & 7 deletions test/new_relic/tc_deployments_api.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
require File.expand_path(File.join(File.dirname(__FILE__),'/../test_helper'))
require 'new_relic_api'
#require File.expand_path(File.join(File.dirname(__FILE__),'/../test_helper'))
require File.expand_path(File.join(File.dirname(__FILE__),'../../lib/new_relic/commands/deployments'))
require 'rubygems'
require 'mocha'

class NewRelic::DeploymentsTests < Test::Unit::TestCase

def setup
NewRelic::API::Deployments.class_eval do
NewRelic::Commands::Deployments.class_eval do
attr_accessor :messages, :exit_status, :errors, :revision
def err(message); @errors = @errors ? @errors + message : message; end
def info(message); @messages = @messages ? @messages + message : message; end
Expand All @@ -17,21 +20,34 @@ def teardown
puts @deployment.exit_status
end
def test_help
@deployment = NewRelic::API::Deployments.new "-?"
@deployment = NewRelic::Commands::Deployments.new "-?"
assert_equal 0, @deployment.exit_status
assert_match /^Usage/, @deployment.messages
assert_nil @deployment.revision
@deployment = nil
end
def test_run
@deployment = NewRelic::API::Deployments.new(%w[-a APP -r 3838 --user=Bill] << "Some lengthy description")
mock_the_connection
@mock_response.expects(:body).returns("<xml>deployment</xml>")
@deployment = NewRelic::Commands::Deployments.new(%w[-a APP -r 3838 --user=Bill] << "Some lengthy description")
assert_nil @deployment.exit_status
assert_nil @deployment.errors
assert_equal '3838', @deployment.revision
@deployment.run
assert_equal 1, @deployment.exit_status
assert_match /Unable to upload/, @deployment.errors

# This should pass because it's a bogus deployment
#assert_equal 1, @deployment.exit_status
#assert_match /Unable to upload/, @deployment.errors

@deployment = nil
end
private
def mock_the_connection
mock_connection = mock()
@mock_response = mock()
@mock_response.expects(:is_a?).with(Net::HTTPSuccess).returns(true)
mock_connection.expects(:request).returns(@mock_response)
NewRelic::Config.instance.stubs(:http_connection).returns(mock_connection)
end

end

0 comments on commit 35ae910

Please sign in to comment.