newrelic_rpm 2.8.0 → 2.8.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of newrelic_rpm might be problematic. Click here for more details.
- data/Rakefile +4 -2
- data/bin/newrelic_cmd +4 -0
- data/lib/new_relic/agent/agent.rb +5 -26
- data/lib/new_relic/commands/deployments.rb +147 -0
- data/lib/new_relic/commands/new_relic_commands.rb +30 -0
- data/lib/new_relic/config.rb +65 -5
- data/lib/new_relic/config/merb.rb +1 -1
- data/lib/new_relic/config/rails.rb +1 -1
- data/lib/new_relic/config/ruby.rb +14 -1
- data/lib/new_relic/recipes.rb +71 -0
- data/lib/new_relic/shim_agent.rb +2 -2
- data/lib/new_relic/version.rb +10 -2
- data/lib/new_relic_api.rb +4 -29
- data/recipes/newrelic.rb +5 -45
- data/test/config/newrelic.yml +2 -0
- data/test/new_relic/tc_deployments_api.rb +38 -11
- data/ui/controllers/newrelic_controller.rb +1 -1
- metadata +11 -8
- data/lib/new_relic/api/deployments.rb +0 -92
data/Rakefile
CHANGED
@@ -22,8 +22,10 @@ spec = Gem::Specification.new do |s|
|
|
22
22
|
s.email = EMAIL
|
23
23
|
s.homepage = HOMEPAGE
|
24
24
|
s.require_path = 'lib'
|
25
|
-
s.files = %w(install.rb LICENSE README newrelic.yml Rakefile) + Dir.glob("{lib,recipes,test,ui}/**/*")
|
26
|
-
|
25
|
+
s.files = %w(install.rb LICENSE README newrelic.yml Rakefile) + Dir.glob("{lib,bin,recipes,test,ui}/**/*")
|
26
|
+
s.bindir = "bin" # Use these for applications.
|
27
|
+
s.executables = ["newrelic_cmd"]
|
28
|
+
s.default_executable = "newrelic_cmd"
|
27
29
|
end
|
28
30
|
|
29
31
|
Rake::GemPackageTask.new(spec) do |pkg|
|
data/bin/newrelic_cmd
ADDED
@@ -149,8 +149,6 @@ module NewRelic::Agent
|
|
149
149
|
attr_reader :error_collector
|
150
150
|
attr_reader :worker_loop
|
151
151
|
attr_reader :license_key
|
152
|
-
attr_reader :remote_host
|
153
|
-
attr_reader :remote_port
|
154
152
|
attr_reader :record_sql
|
155
153
|
attr_reader :identifier
|
156
154
|
|
@@ -415,7 +413,6 @@ module NewRelic::Agent
|
|
415
413
|
|
416
414
|
@error_collector.ignore(ignore_errors)
|
417
415
|
|
418
|
-
|
419
416
|
@capture_params = config.fetch('capture_params', false)
|
420
417
|
|
421
418
|
sampler_config = config.fetch('transaction_tracer', {})
|
@@ -430,17 +427,6 @@ module NewRelic::Agent
|
|
430
427
|
log.info "Transaction tracing is enabled in agent config" if @use_transaction_sampler
|
431
428
|
log.warn "Agent is configured to send raw SQL to RPM service" if @record_sql == :raw
|
432
429
|
|
433
|
-
@use_ssl = config.fetch('ssl', false)
|
434
|
-
default_port = @use_ssl ? 443 : 80
|
435
|
-
|
436
|
-
@remote_host = config.fetch('host', 'collector.newrelic.com')
|
437
|
-
@remote_port = config.fetch('port', default_port)
|
438
|
-
|
439
|
-
@proxy_host = config.fetch('proxy_host', nil)
|
440
|
-
@proxy_port = config.fetch('proxy_port', nil)
|
441
|
-
@proxy_user = config.fetch('proxy_user', nil)
|
442
|
-
@proxy_pass = config.fetch('proxy_pass', nil)
|
443
|
-
|
444
430
|
@prod_mode_enabled = force_enable || config['enabled']
|
445
431
|
|
446
432
|
# Initialize transaction sampler
|
@@ -518,7 +504,7 @@ module NewRelic::Agent
|
|
518
504
|
config.settings
|
519
505
|
@report_period = invoke_remote :get_data_report_period, @agent_id
|
520
506
|
|
521
|
-
log! "Connected to NewRelic Service at #{
|
507
|
+
log! "Connected to NewRelic Service at #{config.server}"
|
522
508
|
log.debug "Agent ID = #{@agent_id}."
|
523
509
|
|
524
510
|
# Ask the server for permission to send transaction samples. determined by subscription license.
|
@@ -539,7 +525,7 @@ module NewRelic::Agent
|
|
539
525
|
return false
|
540
526
|
|
541
527
|
rescue Timeout::Error, StandardError => e
|
542
|
-
log.info "Unable to establish connection with New Relic RPM Service at #{
|
528
|
+
log.info "Unable to establish connection with New Relic RPM Service at #{config.server}"
|
543
529
|
unless e.instance_of? IgnoreSilentlyException
|
544
530
|
log.error e.message
|
545
531
|
log.debug e.backtrace.join("\n")
|
@@ -674,14 +660,7 @@ module NewRelic::Agent
|
|
674
660
|
# to go for higher compression instead, we could use Zlib::BEST_COMPRESSION and
|
675
661
|
# pay a little more CPU.
|
676
662
|
post_data = Zlib::Deflate.deflate(Marshal.dump(args), Zlib::BEST_SPEED)
|
677
|
-
|
678
|
-
# Proxy returns regular HTTP if @proxy_host is nil (the default)
|
679
|
-
http = Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pass).new(@remote_host, @remote_port.to_i)
|
680
|
-
if @use_ssl
|
681
|
-
http.use_ssl = true
|
682
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
683
|
-
end
|
684
|
-
|
663
|
+
http = config.http_connection
|
685
664
|
http.read_timeout = @request_timeout
|
686
665
|
|
687
666
|
# params = {:method => method, :license_key => license_key, :protocol_version => PROTOCOL_VERSION }
|
@@ -740,9 +719,9 @@ module NewRelic::Agent
|
|
740
719
|
end
|
741
720
|
|
742
721
|
def graceful_disconnect
|
743
|
-
if @connected && !(
|
722
|
+
if @connected && !(config.server.host == "localhost" && @identifier == '3000')
|
744
723
|
begin
|
745
|
-
log.debug "Sending graceful shutdown message to #{
|
724
|
+
log.debug "Sending graceful shutdown message to #{config.server}"
|
746
725
|
|
747
726
|
@request_timeout = 5
|
748
727
|
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# This is a class for executing commands related to deployment
|
2
|
+
# events. It runs without loading the rails environment
|
3
|
+
|
4
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__),"..",".."))
|
5
|
+
require 'yaml'
|
6
|
+
require 'net/http'
|
7
|
+
require 'rexml/document'
|
8
|
+
|
9
|
+
# We need to use the Config object but we don't want to load
|
10
|
+
# the rails/merb environment. The defined? clause is so that
|
11
|
+
# it won't load it twice, something it does when run inside a test
|
12
|
+
require 'new_relic/config' unless defined? NewRelic::Config
|
13
|
+
|
14
|
+
module NewRelic
|
15
|
+
module Commands
|
16
|
+
# Capture a failure to execute the command.
|
17
|
+
# Ask it for a return status to exit the vm with,
|
18
|
+
# if appropriate.
|
19
|
+
class CommandFailure < StandardError
|
20
|
+
attr_reader :exit_code
|
21
|
+
def initialize message, return_status=nil
|
22
|
+
super message
|
23
|
+
@exit_code = return_status || 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Deployments
|
28
|
+
|
29
|
+
attr_reader :config
|
30
|
+
def self.command; "deployments"; end
|
31
|
+
|
32
|
+
# Initialize the deployment uploader with command line args.
|
33
|
+
# Use -h to see options.
|
34
|
+
# When command_line_args is a hash, we are invoking directly and
|
35
|
+
# it's treated as an options with optional sttring values for
|
36
|
+
# :user, :description, :appname, :revision, :environment,
|
37
|
+
# and :changes.
|
38
|
+
#
|
39
|
+
# Will throw CommandFailed exception if there's any error.
|
40
|
+
#
|
41
|
+
def initialize command_line_args
|
42
|
+
@config = NewRelic::Config.instance
|
43
|
+
@user = ENV['USER']
|
44
|
+
if Hash === command_line_args
|
45
|
+
# command line args is an options hash
|
46
|
+
command_line_args.each do | key, value |
|
47
|
+
if %w[user environment description appname revision changelog].include? key.to_s
|
48
|
+
instance_variable_set "@#{key}", value.to_s if value
|
49
|
+
else
|
50
|
+
raise "Unrecognized option #{key}=#{value}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
# parse command line args. Throw an exception on a bad arg.
|
55
|
+
@description = options.parse(command_line_args).join " "
|
56
|
+
end
|
57
|
+
config.env = @environment if @environment
|
58
|
+
@appname ||= config.app_name || config.env || 'development'
|
59
|
+
end
|
60
|
+
|
61
|
+
# Run the Deployment upload in RPM via Active Resource.
|
62
|
+
# Will possibly print errors and exit the VM
|
63
|
+
def run
|
64
|
+
begin
|
65
|
+
@description = nil if @description && @description.strip.empty?
|
66
|
+
create_params = {}
|
67
|
+
{
|
68
|
+
:application_id => @appname,
|
69
|
+
:host => Socket.gethostname,
|
70
|
+
:description => @description,
|
71
|
+
:user => @user,
|
72
|
+
:revision => @revision,
|
73
|
+
:changelog => @changelog
|
74
|
+
}.each do |k, v|
|
75
|
+
create_params["deployment[#{k}]"] = v unless v.nil? || v == ''
|
76
|
+
end
|
77
|
+
http = config.http_connection(config.api_server)
|
78
|
+
|
79
|
+
uri = "/deployments.xml"
|
80
|
+
|
81
|
+
raise "license_key was not set in newrelic.yml for #{config.env}" if config['license_key'].nil?
|
82
|
+
request = Net::HTTP::Post.new(uri, {'x-license-key' => config['license_key']})
|
83
|
+
request.content_type = "application/octet-stream"
|
84
|
+
|
85
|
+
request.set_form_data(create_params)
|
86
|
+
|
87
|
+
response = http.request(request)
|
88
|
+
|
89
|
+
if response.is_a? Net::HTTPSuccess
|
90
|
+
info "Recorded deployment to NewRelic RPM (#{@description || Time.now })"
|
91
|
+
else
|
92
|
+
err_string = [ "Unexpected response from server: #{response.code}: #{response.message}" ]
|
93
|
+
begin
|
94
|
+
doc = REXML::Document.new(response.body)
|
95
|
+
doc.elements.each('errors/error') do |error|
|
96
|
+
err_string << "Error: #{error.text}"
|
97
|
+
end
|
98
|
+
rescue
|
99
|
+
end
|
100
|
+
raise CommandFailure.new(err_string.join("\n"), -1)
|
101
|
+
end
|
102
|
+
rescue SystemCallError, SocketError => e
|
103
|
+
# These include Errno connection errors
|
104
|
+
err_string = "Transient error attempting to connect to #{config.api_server} (#{e})"
|
105
|
+
raise CommandFailure.new(err_string, -1)
|
106
|
+
rescue CommandFailure
|
107
|
+
raise
|
108
|
+
rescue Exception => e
|
109
|
+
err "Unexpected error attempting to connect to #{config.api_server}"
|
110
|
+
info e.backtrace.join("\n")
|
111
|
+
raise CommandFailure.new(e.to_s, -1)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def options
|
118
|
+
OptionParser.new "Usage: #{self.class.command} [OPTIONS] [description] ", 40 do |o|
|
119
|
+
o.separator "OPTIONS:"
|
120
|
+
o.on("-a", "--appname=DIR", String,
|
121
|
+
"Set the application name.",
|
122
|
+
"Default is app_name setting in newrelic.yml") { |@appname| }
|
123
|
+
o.on("-e", "--environment=name", String,
|
124
|
+
"Override the (RAILS|MERB|RUBY)_ENV setting",
|
125
|
+
"currently: #{config.env}") { | @environment| }
|
126
|
+
o.on("-u", "--user=USER", String,
|
127
|
+
"Specify the user deploying.",
|
128
|
+
"Default: #{@user}") { |@user| }
|
129
|
+
o.on("-r", "--revision=REV", String,
|
130
|
+
"Specify the revision being deployed") { |@revision | }
|
131
|
+
o.on("-c", "--changes",
|
132
|
+
"Read in a change log from the standard input") { @changelog = STDIN.read }
|
133
|
+
o.on("-?", "Print this help") { raise CommandFailure.new(o.help, 0) }
|
134
|
+
o.separator ""
|
135
|
+
o.separator 'description = "short text"'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def info message
|
140
|
+
STDOUT.puts message
|
141
|
+
end
|
142
|
+
def err message
|
143
|
+
STDERR.puts message
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
# Run the command given by the first argument. Right
|
4
|
+
# now all we have is deployments. We hope to have other
|
5
|
+
# kinds of events here later.
|
6
|
+
|
7
|
+
libdir = File.expand_path(File.join(File.dirname(__FILE__), '..','..'))
|
8
|
+
command_list = Dir[File.join(libdir,'new_relic','commands','*.rb')].map{|command| command =~ /.*\/(.*)\.rb/ && $1}
|
9
|
+
command_list.delete 'new_relic_commands'
|
10
|
+
extra = []
|
11
|
+
options = ARGV.options do |opts|
|
12
|
+
script_name = File.basename($0)
|
13
|
+
opts.banner = "Usage: #{__FILE__} #{ command_list.join(" | ")} [options]"
|
14
|
+
opts.separator "use -h to see detailed command options"
|
15
|
+
opts
|
16
|
+
end
|
17
|
+
extra = options.order!
|
18
|
+
command = extra.shift
|
19
|
+
if !command_list.include?(command)
|
20
|
+
STDERR.puts options
|
21
|
+
else
|
22
|
+
require File.join(libdir, 'new_relic','commands', command + ".rb")
|
23
|
+
command_class = NewRelic::Commands.const_get(command.capitalize)
|
24
|
+
begin
|
25
|
+
command_class.new(extra).run
|
26
|
+
rescue NewRelic::Commands::CommandFailure => failure
|
27
|
+
STDERR.puts failure.message
|
28
|
+
exit failure.exit_code
|
29
|
+
end
|
30
|
+
end
|
data/lib/new_relic/config.rb
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
#require 'new_relic/version'
|
3
3
|
require 'singleton'
|
4
|
-
require 'new_relic/agent'
|
5
4
|
require 'erb'
|
5
|
+
require 'net/https'
|
6
6
|
|
7
7
|
# Configuration supports the behavior of the agent which is dependent
|
8
8
|
# on what environment is being monitored: rails, merb, ruby, etc
|
9
9
|
# It is an abstract factory with concrete implementations under
|
10
10
|
# the config folder.
|
11
11
|
module NewRelic
|
12
|
+
|
12
13
|
class Config
|
13
14
|
|
15
|
+
# Structs holding info for the remote server and proxy server
|
16
|
+
class Server < Struct.new :host, :port
|
17
|
+
def to_s; "#{host}:#{port}"; end
|
18
|
+
end
|
19
|
+
|
20
|
+
ProxyServer = Struct.new :host, :port, :user, :password
|
21
|
+
|
14
22
|
def self.instance
|
15
23
|
@instance ||= new_instance
|
16
24
|
end
|
17
25
|
|
18
|
-
attr_reader :settings
|
19
26
|
|
20
27
|
|
21
28
|
@settings = nil
|
@@ -40,9 +47,26 @@ module NewRelic
|
|
40
47
|
def [](key)
|
41
48
|
fetch(key)
|
42
49
|
end
|
50
|
+
####################################
|
51
|
+
def env=(env_name)
|
52
|
+
@env = env_name
|
53
|
+
@settings = @yaml[env_name]
|
54
|
+
end
|
55
|
+
|
56
|
+
def settings
|
57
|
+
@settings ||= (@yaml && @yaml[env]) || {}
|
58
|
+
end
|
59
|
+
|
60
|
+
def []=(key, value)
|
61
|
+
settings[key] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_config(key,value)
|
65
|
+
self[key]=value
|
66
|
+
end
|
43
67
|
|
44
68
|
def fetch(key, default=nil)
|
45
|
-
|
69
|
+
settings[key].nil? ? default : settings[key]
|
46
70
|
end
|
47
71
|
|
48
72
|
###################################
|
@@ -65,10 +89,44 @@ module NewRelic
|
|
65
89
|
fetch('app_name', nil)
|
66
90
|
end
|
67
91
|
|
92
|
+
def use_ssl?
|
93
|
+
@use_ssl ||= fetch('ssl', false)
|
94
|
+
end
|
95
|
+
|
96
|
+
def server
|
97
|
+
@remote_server ||=
|
98
|
+
NewRelic::Config::Server.new fetch('host', 'collector.newrelic.com'), fetch('port', use_ssl? ? 443 : 80).to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
def api_server
|
102
|
+
@api_server ||=
|
103
|
+
NewRelic::Config::Server.new fetch('api_host', 'rpm.newrelic.com'), fetch('api_port', fetch('port', use_ssl? ? 443 : 80)).to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
def proxy_server
|
107
|
+
@proxy_server ||=
|
108
|
+
NewRelic::Config::ProxyServer.new fetch('proxy_host', nil), fetch('proxy_port', nil),
|
109
|
+
fetch('proxy_user', nil), fetch('proxy_pass', nil)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return the Net::HTTP with proxy configuration given the NewRelic::Config::Server object.
|
113
|
+
# Default is the collector but for api calls you need to pass api_server
|
114
|
+
def http_connection(host = server)
|
115
|
+
# Proxy returns regular HTTP if @proxy_host is nil (the default)
|
116
|
+
http = Net::HTTP::Proxy(proxy_server.host, proxy_server.port,
|
117
|
+
proxy_server.user, proxy_server.password).new(host.host, host.port)
|
118
|
+
if use_ssl?
|
119
|
+
http.use_ssl = true
|
120
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
121
|
+
end
|
122
|
+
http
|
123
|
+
end
|
124
|
+
|
68
125
|
def to_s
|
69
126
|
puts self.inspect
|
70
127
|
"Config[#{self.app}]"
|
71
128
|
end
|
129
|
+
|
72
130
|
def log
|
73
131
|
# If we try to get a log before one has been set up, return a stdout log
|
74
132
|
unless @log
|
@@ -103,7 +161,7 @@ module NewRelic
|
|
103
161
|
end
|
104
162
|
|
105
163
|
def local_env
|
106
|
-
@
|
164
|
+
@local_env ||= NewRelic::LocalEnvironment.new
|
107
165
|
end
|
108
166
|
|
109
167
|
# send the given message to STDERR so that it shows
|
@@ -145,6 +203,7 @@ module NewRelic
|
|
145
203
|
end
|
146
204
|
|
147
205
|
def log_file_name(identifier="")
|
206
|
+
identifier ||= ""
|
148
207
|
"newrelic_agent.#{identifier.gsub(/[^-\w.]/, '_')}.log"
|
149
208
|
end
|
150
209
|
|
@@ -179,12 +238,13 @@ module NewRelic
|
|
179
238
|
yml_file = File.expand_path(File.join(__FILE__,"..","..","..","newrelic.yml"))
|
180
239
|
yaml = ::ERB.new(File.read(yml_file)).result(binding)
|
181
240
|
log! "Cannot find newrelic.yml file at #{config_file}."
|
241
|
+
log! "Be sure to run this from the app root dir."
|
182
242
|
log! "Using #{yml_file} file."
|
183
243
|
log! "Signup at rpm.newrelic.com to get a newrelic.yml file configured for a free Lite account."
|
184
244
|
else
|
185
245
|
yaml = ERB.new(File.read(config_file)).result(binding)
|
186
246
|
end
|
187
|
-
@
|
247
|
+
@yaml = YAML.load(yaml)
|
188
248
|
rescue ScriptError, StandardError => e
|
189
249
|
puts e
|
190
250
|
puts e.backtrace.join("\n")
|
@@ -1,9 +1,22 @@
|
|
1
1
|
class NewRelic::Config::Ruby < NewRelic::Config
|
2
2
|
def app; :ruby; end
|
3
3
|
def env
|
4
|
-
ENV['RUBY_ENV'] || 'development'
|
4
|
+
@env ||= ENV['RUBY_ENV'] || ENV['RAILS_ENV'] || 'development'
|
5
5
|
end
|
6
6
|
def root
|
7
7
|
Dir['.']
|
8
8
|
end
|
9
|
+
# Check a sequence of file locations for newrelic.yml
|
10
|
+
def config_file
|
11
|
+
files = []
|
12
|
+
files << File.join(root,"config","newrelic.yml")
|
13
|
+
files << File.join(root,"newrelic.yml")
|
14
|
+
files << File.join(ENV["HOME"], ".newrelic", "newrelic.yml")
|
15
|
+
files << File.join(ENV["HOME"], "newrelic.yml")
|
16
|
+
files.each do | file |
|
17
|
+
return File.expand_path(file) if File.exists? file
|
18
|
+
end
|
19
|
+
return File.expand_path(files.first)
|
20
|
+
end
|
21
|
+
|
9
22
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# When installed as a plugin this is loaded automatically.
|
2
|
+
#
|
3
|
+
# When installed as a gem, you need to add
|
4
|
+
# require 'new_relic/recipes'
|
5
|
+
# to your deploy.rb
|
6
|
+
#
|
7
|
+
# Defined deploy:notify_rpm which will send information about the deploy to RPM.
|
8
|
+
# The task will run on app servers except where no_release is true.
|
9
|
+
# If it fails, it will not affect the task execution or do a rollback.
|
10
|
+
#
|
11
|
+
make_notify_task = lambda do
|
12
|
+
|
13
|
+
namespace :newrelic do
|
14
|
+
|
15
|
+
# on all deployments, notify RPM
|
16
|
+
desc "Record a deployment in New Relic RPM (rpm.newrelic.com)"
|
17
|
+
task :notice_deployment, :roles => :app, :except => {:no_release => true } do
|
18
|
+
rails_env = fetch(:rails_env, "production")
|
19
|
+
begin
|
20
|
+
require File.join(File.dirname(__FILE__), 'commands', 'deployments.rb')
|
21
|
+
# Try getting the changelog from the server. Then fall back to local changelog
|
22
|
+
# if it doesn't work. Problem is that I don't know what directory the .git is
|
23
|
+
# in when using git. I could possibly use the remote cache but i don't know
|
24
|
+
# if that's always there.
|
25
|
+
=begin
|
26
|
+
run "cd #{current_release}; #{log_command}" do | io, stream_id, output |
|
27
|
+
changelog = output
|
28
|
+
end
|
29
|
+
=end
|
30
|
+
# allow overrides to be defined for revision, description, changelog and appname
|
31
|
+
rev = fetch(:newrelic_revision) if exists?(:newrelic_revision)
|
32
|
+
description = fetch(:newrelic_desc) if exists?(:newrelic_desc)
|
33
|
+
changelog = fetch(:newrelic_changelog) if exists?(:newrelic_changelog)
|
34
|
+
appname = fetch(:newrelic_appname) if exists?(:newrelic_appname)
|
35
|
+
if !changelog
|
36
|
+
logger.debug "Getting log of changes for New Relic Deployment details"
|
37
|
+
from_revision = source.next_revision(current_revision)
|
38
|
+
log_command = "#{source.log(from_revision)}"
|
39
|
+
changelog = `#{log_command}`
|
40
|
+
end
|
41
|
+
new_revision = rev || source.query_revision(source.head()) { |cmd| `#{cmd}` }
|
42
|
+
deploy_options = { :environment => rails_env,
|
43
|
+
:revision => new_revision,
|
44
|
+
:changelog => changelog,
|
45
|
+
:description => description,
|
46
|
+
:appname => appname }
|
47
|
+
logger.debug "Uploading deployment to New Relic"
|
48
|
+
deployment = NewRelic::Commands::Deployments.new deploy_options
|
49
|
+
deployment.run
|
50
|
+
logger.info "Uploaded deployment information to New Relic"
|
51
|
+
rescue ScriptError => e
|
52
|
+
logger.info "error creating New Relic deployment (#{e})\n#{e.backtrace.join("\n")}"
|
53
|
+
rescue NewRelic::Commands::CommandFailure => e
|
54
|
+
logger.info "unable to notify New Relic of the deployment (#{e})... skipping"
|
55
|
+
rescue CommandError
|
56
|
+
logger.info "unable to notify New Relic of the deployment... skipping"
|
57
|
+
end
|
58
|
+
# WIP: For rollbacks, let's update the deployment we created with an indication of the failure:
|
59
|
+
# on_rollback do
|
60
|
+
# run(...)
|
61
|
+
# end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
instance = Capistrano::Configuration.instance
|
67
|
+
if instance
|
68
|
+
instance.load &make_notify_task
|
69
|
+
else
|
70
|
+
make_notify_task.call
|
71
|
+
end
|
data/lib/new_relic/shim_agent.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'new_relic/stats'
|
2
2
|
|
3
3
|
# This agent is loaded by the plug when the plug-in is disabled
|
4
4
|
# It recreates just enough of the API to not break any clients that
|
@@ -28,7 +28,7 @@ module NewRelic
|
|
28
28
|
module Agent
|
29
29
|
|
30
30
|
class << self
|
31
|
-
@@dummy_stats = MethodTraceStats.new
|
31
|
+
@@dummy_stats = NewRelic::MethodTraceStats.new
|
32
32
|
def agent
|
33
33
|
NewRelic::Agent::Agent.instance
|
34
34
|
end
|
data/lib/new_relic/version.rb
CHANGED
@@ -3,14 +3,22 @@ module NewRelic
|
|
3
3
|
module VERSION #:nodoc:
|
4
4
|
MAJOR = 2
|
5
5
|
MINOR = 8
|
6
|
-
TINY =
|
6
|
+
TINY = 1
|
7
7
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
8
8
|
def self.changes
|
9
|
-
puts "NewRelic RPM Plugin Version: #{NewRelic::VERSION}"
|
9
|
+
puts "NewRelic RPM Plugin Version: #{NewRelic::VERSION::STRING}"
|
10
10
|
puts CHANGELOG
|
11
11
|
end
|
12
12
|
|
13
13
|
CHANGELOG = <<EOF
|
14
|
+
|
15
|
+
2009-01-27 version 2.8.1
|
16
|
+
* Convert the deployment information upload script to an executable
|
17
|
+
and put in the bin directory. When installed as a gem this command
|
18
|
+
is symlinked to /usr/bin. Usage: newrelic_cmd deployments --help
|
19
|
+
* Fix issue invoking api when host is not set in newrelic.yml
|
20
|
+
* Fix deployments api so it will work from a gem
|
21
|
+
* Fix thin incompatibility in developer mode
|
14
22
|
2008-12-18 version 2.8.0
|
15
23
|
* add beta of api in new_relic_api.rb
|
16
24
|
* instrumented dynamic finders in ActiveRecord
|
data/lib/new_relic_api.rb
CHANGED
@@ -105,7 +105,9 @@ module NewRelicApi
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def site_url
|
108
|
-
|
108
|
+
host = NewRelicApi.host || NewRelic::Config.instance.api_server.host
|
109
|
+
port = NewRelicApi.port || NewRelic::Config.instance.api_server.port
|
110
|
+
"#{port == 443 ? 'https' : 'http'}://#{host}:#{port}"
|
109
111
|
end
|
110
112
|
|
111
113
|
def reset!
|
@@ -245,31 +247,4 @@ module NewRelicApi
|
|
245
247
|
end
|
246
248
|
|
247
249
|
end
|
248
|
-
|
249
|
-
# Run the command given by the first argument. Right
|
250
|
-
# now all we have is deployments. We hope to have other
|
251
|
-
# kinds of events here later
|
252
|
-
command = "(no command given)"
|
253
|
-
extra = [command]
|
254
|
-
ARGV.options do |opts|
|
255
|
-
script_name = File.basename($0)
|
256
|
-
opts.banner = "Usage: #{__FILE__} command [options]"
|
257
|
-
|
258
|
-
opts.separator ""
|
259
|
-
|
260
|
-
opts.on("-e", "--environment=name", String,
|
261
|
-
"Specifies the environment for the runner to operate under (test/development/production).",
|
262
|
-
"Default: development")
|
263
|
-
|
264
|
-
extra = opts.order!
|
265
|
-
end
|
266
|
-
command = extra.shift
|
267
|
-
begin
|
268
|
-
require "new_relic/api/#{command}"
|
269
|
-
command_class = NewRelic::API.const_get(command.camelize)
|
270
|
-
rescue
|
271
|
-
STDERR.puts "Unknown command: #{command}"
|
272
|
-
exit 1
|
273
|
-
end
|
274
|
-
command_class.new(extra).run
|
275
|
-
end
|
250
|
+
|
data/recipes/newrelic.rb
CHANGED
@@ -1,46 +1,6 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# If it fails, it will not affect the task execution or do a rollback.
|
6
|
-
#
|
1
|
+
# The capistrano recipes in plugins are automatically
|
2
|
+
# loaded from here. From gems, they are available from
|
3
|
+
# the lib directory. We have to make them available from
|
4
|
+
# both locations
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
namespace :newrelic do
|
11
|
-
|
12
|
-
# on all deployments, notify RPM
|
13
|
-
desc "Record a deployment in New Relic RPM (rpm.newrelic.com)"
|
14
|
-
task :notice_deployment, :roles => :app, :except => {:no_release => true } do
|
15
|
-
rails_env = fetch(:rails_env, "production")
|
16
|
-
from_revision = source.next_revision(current_revision)
|
17
|
-
log_command = source.log(from_revision)
|
18
|
-
# I don't believe this will work if rpm is installed as a gem, or
|
19
|
-
# if they put the plugin elsewhere. Need to revisit.
|
20
|
-
script = [ "vendor/plugins/newrelic_rpm/lib/new_relic_api.rb" ] <<
|
21
|
-
"deployments" <<
|
22
|
-
"-u" << ENV['USER'] <<
|
23
|
-
"-r" << current_revision <<
|
24
|
-
"-c"
|
25
|
-
script = script.map { | arg | "'#{arg}'" }.join(" ")
|
26
|
-
begin
|
27
|
-
run "cd #{current_release}; #{log_command} | script/runner -e #{rails_env} #{script}" do | io, stream_id, output |
|
28
|
-
logger.trace(output)
|
29
|
-
end
|
30
|
-
rescue CommandError
|
31
|
-
logger.info "unable to notify New Relic of the deployment... skipping"
|
32
|
-
end
|
33
|
-
# For rollbacks, let's update the deployment we created with an indication of the failure:
|
34
|
-
#on_rollback do
|
35
|
-
# run(command.gsub(/Subject:.*\\n/, "Subject: #{ENV['USER']} deployed a ROLLBACK\\n"))
|
36
|
-
#end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
instance = Capistrano::Configuration.instance
|
42
|
-
if instance
|
43
|
-
instance.load make_notify_task
|
44
|
-
else
|
45
|
-
make_notify_task.call
|
46
|
-
end
|
6
|
+
require File.join(File.dirname(__FILE__),'..','lib','new_relic','recipes')
|
data/test/config/newrelic.yml
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__),'/../test_helper'))
|
2
|
-
require '
|
1
|
+
#require File.expand_path(File.join(File.dirname(__FILE__),'/../test_helper'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'../../lib/new_relic/commands/deployments'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'mocha'
|
5
|
+
|
3
6
|
class NewRelic::DeploymentsTests < Test::Unit::TestCase
|
4
7
|
|
5
8
|
def setup
|
6
|
-
NewRelic::
|
9
|
+
NewRelic::Commands::Deployments.class_eval do
|
7
10
|
attr_accessor :messages, :exit_status, :errors, :revision
|
8
11
|
def err(message); @errors = @errors ? @errors + message : message; end
|
9
12
|
def info(message); @messages = @messages ? @messages + message : message; end
|
@@ -17,21 +20,45 @@ class NewRelic::DeploymentsTests < Test::Unit::TestCase
|
|
17
20
|
puts @deployment.exit_status
|
18
21
|
end
|
19
22
|
def test_help
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
begin
|
24
|
+
NewRelic::Commands::Deployments.new "-?"
|
25
|
+
fail "should have thrown"
|
26
|
+
rescue NewRelic::Commands::CommandFailure => c
|
27
|
+
assert_match /^Usage/, c.message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
def test_interactive
|
31
|
+
mock_the_connection
|
32
|
+
@deployment = NewRelic::Commands::Deployments.new :appname => 'APP', :revision => 3838, :user => 'Bill', :description => "Some lengthy description"
|
33
|
+
assert_nil @deployment.exit_status
|
34
|
+
assert_nil @deployment.errors
|
35
|
+
assert_equal '3838', @deployment.revision
|
36
|
+
@deployment.run
|
24
37
|
@deployment = nil
|
25
38
|
end
|
26
|
-
|
27
|
-
|
39
|
+
|
40
|
+
def test_command_line_run
|
41
|
+
mock_the_connection
|
42
|
+
# @mock_response.expects(:body).returns("<xml>deployment</xml>")
|
43
|
+
@deployment = NewRelic::Commands::Deployments.new(%w[-a APP -r 3838 --user=Bill] << "Some lengthy description")
|
28
44
|
assert_nil @deployment.exit_status
|
29
45
|
assert_nil @deployment.errors
|
30
46
|
assert_equal '3838', @deployment.revision
|
31
47
|
@deployment.run
|
32
|
-
|
33
|
-
|
48
|
+
|
49
|
+
# This should pass because it's a bogus deployment
|
50
|
+
#assert_equal 1, @deployment.exit_status
|
51
|
+
#assert_match /Unable to upload/, @deployment.errors
|
52
|
+
|
34
53
|
@deployment = nil
|
35
54
|
end
|
55
|
+
private
|
56
|
+
def mock_the_connection
|
57
|
+
mock_connection = mock()
|
58
|
+
@mock_response = mock()
|
59
|
+
@mock_response.expects(:is_a?).with(Net::HTTPSuccess).returns(true)
|
60
|
+
mock_connection.expects(:request).returns(@mock_response)
|
61
|
+
NewRelic::Config.instance.stubs(:http_connection).returns(mock_connection)
|
62
|
+
end
|
36
63
|
|
37
64
|
end
|
@@ -150,7 +150,7 @@ class NewrelicController < ActionController::Base
|
|
150
150
|
:last_modified => last_modified,
|
151
151
|
:type => 'text/plain'
|
152
152
|
else
|
153
|
-
response.headers['Last-Modified'] = last_modified
|
153
|
+
response.headers['Last-Modified'] = last_modified.to_formatted_s(:rfc822)
|
154
154
|
expires_in 24.hours
|
155
155
|
send_file file, :content_type => mime_type_from_extension(file), :disposition => 'inline' #, :filename => File.basename(file)
|
156
156
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: newrelic_rpm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.8.
|
4
|
+
version: 2.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bill Kayser
|
@@ -9,14 +9,14 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
13
|
-
default_executable:
|
12
|
+
date: 2009-01-29 00:00:00 -08:00
|
13
|
+
default_executable: newrelic_cmd
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: New Relic Ruby Performance Monitoring Agent
|
17
17
|
email: bkayser@newrelic.com
|
18
|
-
executables:
|
19
|
-
|
18
|
+
executables:
|
19
|
+
- newrelic_cmd
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
@@ -61,8 +61,9 @@ files:
|
|
61
61
|
- lib/new_relic/agent/transaction_sampler.rb
|
62
62
|
- lib/new_relic/agent/worker_loop.rb
|
63
63
|
- lib/new_relic/agent.rb
|
64
|
-
- lib/new_relic/
|
65
|
-
- lib/new_relic/
|
64
|
+
- lib/new_relic/commands
|
65
|
+
- lib/new_relic/commands/deployments.rb
|
66
|
+
- lib/new_relic/commands/new_relic_commands.rb
|
66
67
|
- lib/new_relic/config
|
67
68
|
- lib/new_relic/config/merb.rb
|
68
69
|
- lib/new_relic/config/rails.rb
|
@@ -74,6 +75,7 @@ files:
|
|
74
75
|
- lib/new_relic/metric_spec.rb
|
75
76
|
- lib/new_relic/metrics.rb
|
76
77
|
- lib/new_relic/noticed_error.rb
|
78
|
+
- lib/new_relic/recipes.rb
|
77
79
|
- lib/new_relic/shim_agent.rb
|
78
80
|
- lib/new_relic/stats.rb
|
79
81
|
- lib/new_relic/transaction_analysis.rb
|
@@ -85,6 +87,7 @@ files:
|
|
85
87
|
- lib/tasks/agent_tests.rake
|
86
88
|
- lib/tasks/all.rb
|
87
89
|
- lib/tasks/install.rake
|
90
|
+
- bin/newrelic_cmd
|
88
91
|
- recipes/newrelic.rb
|
89
92
|
- test/config
|
90
93
|
- test/config/newrelic.yml
|
@@ -172,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
175
|
requirements: []
|
173
176
|
|
174
177
|
rubyforge_project: newrelic
|
175
|
-
rubygems_version: 1.3.
|
178
|
+
rubygems_version: 1.3.0
|
176
179
|
signing_key:
|
177
180
|
specification_version: 2
|
178
181
|
summary: New Relic Ruby Performance Monitoring Agent
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# This is a class for executing commands related to deployment
|
2
|
-
# events
|
3
|
-
|
4
|
-
require 'optparse'
|
5
|
-
|
6
|
-
module NewRelic::API
|
7
|
-
|
8
|
-
class Deployments
|
9
|
-
|
10
|
-
def self.command; "deployments"; end
|
11
|
-
|
12
|
-
# Initialize the deployment uploader with command line args.
|
13
|
-
# Use -h to see options. Will possibly exit the VM
|
14
|
-
def initialize command_line_args
|
15
|
-
@application_id = NewRelic::Config.instance.app_name || RAILS_ENV
|
16
|
-
@user = ENV['USER']
|
17
|
-
@description = options.parse(command_line_args).join " "
|
18
|
-
end
|
19
|
-
|
20
|
-
# Run the Deployment upload in RPM via Active Resource.
|
21
|
-
# Will possibly print errors and exit the VM
|
22
|
-
def run
|
23
|
-
begin
|
24
|
-
@description = nil if @description.blank?
|
25
|
-
create_params = {
|
26
|
-
:application_id => @application_id,
|
27
|
-
:host => Socket.gethostname,
|
28
|
-
:description => @description,
|
29
|
-
:user => @user,
|
30
|
-
:revision => @revision,
|
31
|
-
:changelog => @changelog
|
32
|
-
}
|
33
|
-
d = NewRelicApi::Deployment.create(create_params)
|
34
|
-
rescue Exception => e
|
35
|
-
err "Attempting to connect to #{NewRelicApi::BaseResource.site_url}\nUnable to upload deployment (#{e.message})"
|
36
|
-
info e.backtrace.join("\n")
|
37
|
-
just_exit 1
|
38
|
-
end
|
39
|
-
if d.nil?
|
40
|
-
err "No value returned from create!"
|
41
|
-
just_exit 1
|
42
|
-
elsif d.valid?
|
43
|
-
puts "Recorded deployment to NewRelic RPM (#{d.description})"
|
44
|
-
else
|
45
|
-
err "Could not record deployment to NewRelic RPM:"
|
46
|
-
err d.errors.full_messages.join("\n")
|
47
|
-
just_exit 1
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def options
|
54
|
-
OptionParser.new "Usage: #{self.class.command} [OPTIONS] [description] ", 40 do |o|
|
55
|
-
o.separator "OPTIONS:"
|
56
|
-
o.on("-a", "--appname=DIR", String,
|
57
|
-
"Specify an application name.",
|
58
|
-
"Default: #{@application_id}") { |@application_id| }
|
59
|
-
o.on("-u", "--user=USER", String,
|
60
|
-
"Specify the user deploying.",
|
61
|
-
"Default: #{ENV['USER']}") { |@user| }
|
62
|
-
o.on("-r", "--revision=REV", String,
|
63
|
-
"Specify the revision being deployed") { |@revision | }
|
64
|
-
o.on("-c", "--changes",
|
65
|
-
"Read in a change log from the standard input") { @changelog = STDIN.read }
|
66
|
-
o.on("-?", "Print this help") { info o.help; just_exit }
|
67
|
-
o.separator ""
|
68
|
-
o.separator 'description = "short text"'
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def help(message)
|
73
|
-
if message
|
74
|
-
err message
|
75
|
-
info options.help
|
76
|
-
just_exit 1
|
77
|
-
else
|
78
|
-
info options
|
79
|
-
just_exit 0
|
80
|
-
end
|
81
|
-
end
|
82
|
-
def info message
|
83
|
-
STDOUT.puts message
|
84
|
-
end
|
85
|
-
def err message
|
86
|
-
STDERR.puts message
|
87
|
-
end
|
88
|
-
def just_exit status=0
|
89
|
-
exit status
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|