Class: ScoutAgent::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_agent/server.rb

Overview

This class is a thin wrapper over RestClient for Scout’s check-in API. Public methods are provided for each action you can perform againt the API.

Defined Under Namespace

Classes: AgentTimeoutError

Constant Summary collapse

REQUEST_TIMEOUT =

Another Timeout for Net::Http requests. Their limit doesn’t always seem to trigger in the face of server errors, so we add this redundant check to avoid hanging on check-in.

5 * 60

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(log = WireTap.new(nil)) ⇒ Server

Create a new API wrapper, optionally with a log to write connection details to.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/scout_agent/server.rb', line 24

def initialize(log = WireTap.new(nil))
  @log         = log
  @rest_client = RestClient::Resource.new(
    Plan.agent_url,
    :headers     => { :client_version   => ScoutAgent::VERSION,
                      :accept_encoding  => "gzip" },
    :ssl_ca_file => File.join( File.dirname(__FILE__),
                               *%w[.. .. data cacert.pem] ),
    :verify_ssl  => OpenSSL::SSL::VERIFY_PEER |
                    OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
  )
  # make sure proxy is set, if needed
  RestClient.proxy = Plan.proxy_url
end

Instance Attribute Details

#logObject (readonly)

The log connection notes will be written to.



40
41
42
# File 'lib/scout_agent/server.rb', line 40

def log
  @log
end

Instance Method Details

#get_plan(additional_headers = { }) ⇒ Object

This method fetches the current plan for this agent. You can pass any additional_headers for the request if needed (mainly useful for :if_modified_since).

This method will return the raw plan when it is successfully fetched. An empty String is returned if the plan is malformed or unchanged. Finally, nil is returned if the plan cannot be retrieved for some reason.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/scout_agent/server.rb', line 51

def get_plan(additional_headers = { })
  request {
    @rest_client["plan.scout"].get(additional_headers)
  }
rescue Zlib::Error  # could not decompress response
  log.warn("Plan was malformed zipped data.")
  ""  # replace bad plan with empty plan
rescue RestClient::RequestFailed => error  # RestClient bug workaround
  log.warn("Plan was returned as a failure code:  #{error.http_code}.")
  nil  # failed to retrieve plan
rescue RestClient::NotModified
  log.info("Plan was not modified.")
  ""  # empty plan
rescue OpenSSL::SSL::SSLError
  log.warn("SSL verification failed.  The plan could not be trusted.")
  nil  # cannot trust any plan we received
rescue Exception => error  # networking problem
  log.warn("Plan could not be retrieved:  #{error.class}.")
  nil  # failed to retrieve plan
end

#post_checkin(data) ⇒ Object

This method can be used to send data to the Scout API as a check-in. The data is zipped before it is sent to reduce the cost duplicated reports as much as possible.

If the ckeck-in succeeds, true is returned. However, false doesn’t ensure that we failed. RestClient may timeout a long connection attempt, but the server may still complete it eventually.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/scout_agent/server.rb', line 81

def post_checkin(data)
  io   =  StringIO.new
  gzip =  Zlib::GzipWriter.new(io)
  gzip << data.to_json
  gzip.close
  request do
    @rest_client["checkin.scout"].post(
      io.string,
      :content_type     => "application/json",
      :content_encoding => "gzip"
    )
  end
  true
rescue Zlib::Error  # could not compress data for sending
  log.error("Check-in could not be zipped.")
  false
rescue RestClient::RequestFailed => error  # RestClient bug workaround
  log.warn( "Check-in was returned as a failure code:  " +
            "#{error.http_code}." )
  false
rescue OpenSSL::SSL::SSLError
  log.warn( "SSL verification failed.  " +
            "Could not send check-in to untrusted server." )
  false  # cannot trust server
rescue Exception => error  # networking problem
  # bypass for a RestClient bug
  if error.is_a?(NoMethodError) and
     error.backtrace            and
     error.backtrace.first.to_s =~ /\brest-client\b/
    return true
  end
  log.warn("Check-in could not be sent:  #{error.class}.")
  false  # we failed to send and will retry later
end

#post_log(log_file) ⇒ Object

Uploads log_file to the server for troubleshooting. Returns true if the upload succeeded.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/scout_agent/server.rb', line 120

def post_log(log_file)
  request do
    @rest_client["log.scout"].post(
      log_file.read,
      :content_type     => "text/plain",
      :content_encoding => "gzip"
    )
  end
  true
rescue RestClient::RequestFailed => error  # RestClient bug workaround
  log.warn( "Log upload was returned as a failure code:  " +
            "#{error.http_code}." )
  false
rescue OpenSSL::SSL::SSLError
  log.warn( "SSL verification failed.  " +
            "Could not send log to untrusted server." )
  false  # cannot trust server
rescue Exception => error  # networking problem
  log.warn("Log could not be sent:  #{error.class}.")
  false  # could not send log
end