Module: RightScale::ModelsHelper
- Included in:
- GlobalObjectReplicatorSink, GlobalObjectReplicatorSource
- Defined in:
- lib/right_infrastructure_agent/models_helper.rb
Overview
Helper methods for accessing ActiveRecord models They are only usable when executing in a Rails environment
Constant Summary collapse
- WRONG_SHARD_ERROR =
Pattern in exception message from trigger in core when account is not in current shard
"ERROR_UPDATE_NOT_ALLOWED does not exist"
- WRONG_SHARD_RETRY_MESSAGE =
Retry exception message for when get wrong shard exception
"Account temporarily unavailable"
- DISABLED_SHARD_RETRY_MESSAGE =
Retry exception message for when account disabled due to shard migration
"Account temporarily unavailable"
- DEFAULT_RETRY_MESSAGE =
Default retry exception message
"RightScale database temporarily unavailable"
- RETRYABLE_ERRORS =
Mysql and ActiveRecord case-insensitive exception message content that if present causes the error to be propagated to the requester as retryable after max retries is exceeded internally
["deadlock found", "lock wait timeout", "can't connect to", "has gone away", WRONG_SHARD_ERROR]
Instance Method Summary collapse
-
#account(id, options = {}) ⇒ Object
Retrieve account with given id.
-
#audit_entry(id, options = {}) ⇒ Object
Retrieve AuditEntry with given id.
-
#instance(token_id, options = {}) ⇒ Object
Retrieve instance model with given API token id.
-
#instance_from_agent_id(agent_id, options = {}) ⇒ Object
Get instance model corresponding to instance agent with given identity.
-
#instance_from_token_id(token_id, options = {}) ⇒ Object
Get instance from API token id Cache all tokens retrieved.
-
#instance_token(id, options = {}) ⇒ Object
Retrieve InstanceApiToken model with given id.
-
#is_retryable_error?(e, local = false) ⇒ Boolean
Is given exception a MySQL exception worth retrying, e.g., a deadlock or timeout?.
-
#permission(id, options = {}) ⇒ Object
Retrieve Permission with given id.
-
#query(description, options = {}, &blk) ⇒ Object
Query database using retry on failure and reconnect handling Audit and/or log error if given block returns nil or raises, return block result otherwise Store any error message in @last_error.
-
#recipe(id, options = {}) ⇒ Object
Retrieve Chef recipe with given id.
-
#recipe_from_name(name, instance, options = {}) ⇒ Object
Retrieve recipe with given name on given instance.
-
#repositories(options = {}) ⇒ Object
Retrieve all software repositories.
-
#retrieve(description, options = {}) ⇒ Object
Retrieve database object using the ModelsHelper functions below that themselves use #run_query, e.g., retrieve(“recipe for instance”, :audit => audit) { recipe(id, :retryable_error => true } Audit and/or log error if block returns nil or raises exception, otherwise return block result Store any error message in @last_error.
-
#retrieve_or_create_audit(instance, summary = '', detail = '', account = instance.account, user = nil, options = {}) ⇒ Object
Retrieve existing audit or create new one.
-
#retrieve_or_default_user(instance, options = {}) ⇒ Object
Retrieve existing user or get default user stub Store any error message in @last_error.
-
#right_script(id, options = {}) ⇒ Object
Retrieve RightScript with given id.
-
#right_script_from_name(name, instance, options = {}) ⇒ Object
Retrieve RightScript with given name on given instance.
-
#run_query(options = {}) ⇒ Object
Run database query block When catch retryable MySQL and ActiveRecord errors, rerun block, retry up to 3 times.
-
#setting(id, options = {}) ⇒ Object
Retrieve Setting with given id.
-
#user_credential(id, options = {}) ⇒ Object
Retrieve UserCredential with given id.
-
#user_credential_from_fingerprint(public_value_fingerprint, options = {}) ⇒ Object
Retrieve UserCredential with given public value fingerprint.
Instance Method Details
#account(id, options = {}) ⇒ Object
Retrieve account with given id
Parameters
- id(Integer)
-
Id of account to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (Account|nil)
-
Corresponding account, or nil if no account with this id exists
336 337 338 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 336 def account(id, = {}) run_query() { Account.find(id) } end |
#audit_entry(id, options = {}) ⇒ Object
Retrieve AuditEntry with given id
Parameters
- id(Integer)
-
Id of AuditEntry to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (AuditEntry|nil)
-
Corresponding setting, or nil if no setting with this id exists
349 350 351 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 349 def audit_entry(id, = {}) run_query() { AuditEntry.find(id) } end |
#instance(token_id, options = {}) ⇒ Object
Retrieve instance model with given API token id
Parameters
- token_id(Integer)
-
Id of InstanceApiToken of instance to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- instance(Ec2Instance|Instance|nil)
-
Corresponding instance, or nil if no token with
this id exists
Raise
- RetryableError
-
If account disabled for shard migration, or if exceed maximum retries
269 270 271 272 273 274 275 276 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 269 def instance(token_id, = {}) token = instance_token(token_id, ) token && (instance = token.instance) if instance && instance.account.disabled_in_every_shard? raise RightScale::Exceptions::RetryableError.new(DISABLED_SHARD_RETRY_MESSAGE) end instance end |
#instance_from_agent_id(agent_id, options = {}) ⇒ Object
Get instance model corresponding to instance agent with given identity
Parameters
- identity(String)
-
Serialized instance agent identity
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (Instance)
-
Corresponding instance
Raise
- ArgumentError
-
Invalid agent identity
322 323 324 325 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 322 def instance_from_agent_id(agent_id, = {}) raise ArgumentError, "Invalid agent identity" unless AgentIdentity.valid?(agent_id) instance_from_token_id(AgentIdentity.parse(agent_id).base_id, ) end |
#instance_from_token_id(token_id, options = {}) ⇒ Object
Get instance from API token id Cache all tokens retrieved
Parameters
- token_id(Integer)
-
API token id
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- instance(Instance)
-
Corresponding instance
Raise
- RetryableError
-
If account disabled for shard migration, or if exceed maximum retries
- RightScale::Exceptions::Application
-
If InstanceApiToken or Instance not found
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 292 def instance_from_token_id(token_id, = {}) @tokens ||= {} if instance = @tokens[token_id] run_query() { instance.reload } else # Not in cache, look it up instance_api_token = instance_token(token_id) raise RightScale::Exceptions::Application, "Instance token with id '#{token_id}' not found" unless instance_api_token instance = instance_api_token.instance raise RightScale::Exceptions::Application, "Instance with token id '#{token_id}' not found" unless instance @tokens[token_id] = instance end if instance && instance.account.disabled_in_every_shard? raise RightScale::Exceptions::RetryableError.new(DISABLED_SHARD_RETRY_MESSAGE) end instance end |
#instance_token(id, options = {}) ⇒ Object
Retrieve InstanceApiToken model with given id
Parameters
- id(Integer)
-
Id of InstanceApiToken to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (InstanceApiToken|nil)
-
Corresponding API token, or nil if no token with this id exists
252 253 254 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 252 def instance_token(id, = {}) run_query() { InstanceApiToken.find(id) } end |
#is_retryable_error?(e, local = false) ⇒ Boolean
Is given exception a MySQL exception worth retrying, e.g., a deadlock or timeout?
Parameter
- e(Exception)
-
Exception to be tested
- local(Boolean)
-
Whether making this decision for local or external consumption
Return
- (Boolean)
-
true if worth retrying, otherwise false or nil
139 140 141 142 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 139 def is_retryable_error?(e, local = false) (e.is_a?(MysqlError) || e.is_a?(ActiveRecord::ActiveRecordError)) && (local || (RETRYABLE_ERRORS + ActiveRecord::ConnectionAdapters::MysqlAdapter::LOST_CONNECTION_ERROR_MESSAGES).find { |m| e. =~ /#{m}/i }) end |
#permission(id, options = {}) ⇒ Object
Retrieve Permission with given id
Parameters
- id(Integer)
-
Id of Permission to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (Permission|nil)
-
Corresponding permission, or nil if no permission with this id exists
426 427 428 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 426 def (id, = {}) run_query() { Permission.find(id) } end |
#query(description, options = {}, &blk) ⇒ Object
Query database using retry on failure and reconnect handling Audit and/or log error if given block returns nil or raises, return block result otherwise Store any error message in @last_error
Parameters
- description(String)
-
Description of query action that is used in error messages
- options(Hash)
-
Query options:
- :audit(AuditEntry)
-
Audit entry used to append error message if any
- :include_backtrace_in_last_error(Boolean)
-
Whether to pass :trace to Log.format for exceptions
- :email_errors(Boolean)
-
Whether to send email for errors
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Block
Accesses MySQL and returns result, required
Return
- (Object|nil)
-
Value returned by block, or nil if failed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 51 def query(description, = {}, &blk) begin @last_error = nil run_query(, &blk) rescue Exception => e retryable = e.is_a?(RightScale::Exceptions::RetryableError) description = "Failed to #{description}" + (retryable ? " but retryable" : "") Log.error(description, e, :trace) if [:include_backtrace_in_last_error] @last_error = Log.format(description, e, :trace) else @last_error = Log.format(description, e) end [:audit].append(AuditFormatter.error(@last_error)) if [:audit] if([:email_errors]) ExceptionMailer.deliver_notification(description, e., e) end raise if retryable nil end end |
#recipe(id, options = {}) ⇒ Object
Retrieve Chef recipe with given id
Parameters
- id(Integer)
-
Id of ServerTemplateChefRecipe to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (ServerTemplateChefRecipe|nil)
-
Corresponding recipe, or nil if no recipe with this id exists
394 395 396 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 394 def recipe(id, = {}) run_query() { ServerTemplateChefRecipe.find(id) } end |
#recipe_from_name(name, instance, options = {}) ⇒ Object
Retrieve recipe with given name on given instance
Parameters
- name(String)
-
Name of recipe that should be retrieved
- instance(Instance)
-
Instance on which recipe is defined
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- recipe(ServerTemplateChefRecipe)
-
Corresponding recipe
408 409 410 411 412 413 414 415 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 408 def recipe_from_name(name, instance, = {}) recipe = nil template = run_query() { instance.server_template } if template recipe = run_query() { template.server_template_chef_recipes.find_by_recipe(name) } end recipe end |
#repositories(options = {}) ⇒ Object
Retrieve all software repositories
Parameters
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (Array)
-
Array of Repository
477 478 479 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 477 def repositories( = {}) run_query() { Repository.find(:all) } end |
#retrieve(description, options = {}) ⇒ Object
Retrieve database object using the ModelsHelper functions below that themselves use #run_query, e.g., retrieve(“recipe for instance”, :audit => audit) { recipe(id, :retryable_error => true } Audit and/or log error if block returns nil or raises exception, otherwise return block result Store any error message in @last_error
Parameters
- description(String)
-
Description of object that is used in error messages
- options(Hash)
-
Query options:
- :audit(AuditEntry)
-
Audit entry used to append error message if any
- :log(Boolean)
-
Whether to log message when object does not exist
Block
Performs query to retrieve object, required The block should retrieve using other ModelsHelper functions like #instance or #account so that #run_query is applied rather than accessing models directly in the block
Return
- item(Object)
-
Value returned by block, or nil if not found or failed
Raise
- RuntimeError
-
Block is missing
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 222 def retrieve(description, = {}) raise 'Missing block' unless block_given? retryable = nil begin @last_error = nil unless item = yield @last_error = "Could not find #{description}" Log.warning(@last_error) if [:log] end rescue Exception => e retryable = e if e.is_a?(RightScale::Exceptions::RetryableError) description = "Failed to retrieve #{description}" + (retryable ? " but retryable" : "") Log.error(description, e, e.is_a?(RightScale::Exceptions) ? :caller : :trace) @last_error = Log.format(description, e) item = nil end [:audit].append(AuditFormatter.error(@last_error)) if [:audit] && item.nil? && @last_error raise retryable if retryable item end |
#retrieve_or_create_audit(instance, summary = '', detail = '', account = instance.account, user = nil, options = {}) ⇒ Object
Retrieve existing audit or create new one
Parameters
- instance(Instance)
-
Instance used as auditee
- summary(String)
-
New audit summary, default to empty string
- detail(String)
-
New audit detail, default to empty string
- account(Account)
-
Account associated with audit, default to instance’s account
- user(User)
-
User who caused the activity
- options(Hash)
-
Query options:
- :audit_id(Integer)
-
Audit entry id if audit is to be retrieved
- :agent_id(String)
-
Serialized agent identity if audit is to be created (:agent_identity
is an alternative deprecated option name)
:retryable_error(Boolean):: Whether to raise RetryableError if exceed maximum retries
Return
- (AuditEntry)
-
Audit entry model
Raise
- ArgumentError
-
If neither :audit_id nor :agent_id is specified
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 163 def retrieve_or_create_audit(instance, summary = '', detail = '', account = instance.account, user = nil, = {}) if [:audit_id] && [:audit_id] != -1 retrieve("audit with id #{[:audit_id]}") { audit_entry([:audit_id], ) } elsif (agent_id = [:agent_id] || [:agent_identity]) query("create audit for instance agent #{agent_id}", ) do AuditEntry.create!( {:auditee => instance, :summary => summary, :detail => detail, :account => account, :user => user} ) end else raise ArgumentError, "Must specify audit ID or agent ID" end end |
#retrieve_or_default_user(instance, options = {}) ⇒ Object
Retrieve existing user or get default user stub Store any error message in @last_error
Parameters
- instance(Instance)
-
Instance for account
- options(Hash)
-
Query options:
- :user_id(Integer)
-
User id or 0 meaning use stub
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- current_user(User)
-
User retrieved or stubbed
187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 187 def retrieve_or_default_user(instance, = {}) user_id = ([:user_id] || 0).to_i # ensure user id is non-nil integer if user_id == 0 current_user = User.new(:email => '[email protected]') current_user.id = 0 else account = instance.account current_user = query("User in account #{account} with id #{user_id}", ) do account.users.detect { |u| u.id == user_id } end end current_user end |
#right_script(id, options = {}) ⇒ Object
Retrieve RightScript with given id
Parameters
- id(Integer)
-
Id of RightScript to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (RightScript|nil)
-
Corresponding RightScript or nil if no RightScript with this id exists
362 363 364 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 362 def right_script(id, = {}) run_query() { RightScript.find(id) } end |
#right_script_from_name(name, instance, options = {}) ⇒ Object
Retrieve RightScript with given name on given instance
Parameters
- name(String)
-
Name of RightScript that should be retrieved
- instance(Instance)
-
Instance on which RightScript is defined
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- script(RightScript)
-
Corresponding RightScript
376 377 378 379 380 381 382 383 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 376 def right_script_from_name(name, instance, = {}) script = nil template = run_query() { instance.server_template } if template script = run_query() { template.right_scripts.find_by_name(name) } end script end |
#run_query(options = {}) ⇒ Object
Run database query block When catch retryable MySQL and ActiveRecord errors, rerun block, retry up to 3 times
Parameters
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Block
Accesses MySQL and returns result, required
Return
- res(Object|nil)
-
Value returned by given block, or nil if desired data was not found
Raise
- RuntimeError
-
Block is missing
RetryableError: If exceed max retries and :retryable_error option enabled Also re-raises any query block exceptions
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 92 def run_query( = {}) raise 'Missing block' unless block_given? res = nil disconnected = true while disconnected do retries = 0 begin res = yield disconnected = false rescue ActiveRecord::RecordNotFound res = nil disconnected = false rescue Exception => e if is_retryable_error?(e, local = true) if retries >= 3 Log.warning("Aborting query after 3 failed retries") if [:retryable_error] && is_retryable_error?(e) if e. =~ /#{WRONG_SHARD_ERROR}/i raise RightScale::Exceptions::RetryableError.new(WRONG_SHARD_RETRY_MESSAGE, e) else raise RightScale::Exceptions::RetryableError.new(DEFAULT_RETRY_MESSAGE, e) end else raise # re-raise the exception end else retries += 1 Log.error("Failed running MySQL query", e, :trace) Log.info("Retrying query...") retry end else raise # re-raise the exception end end end res end |
#setting(id, options = {}) ⇒ Object
Retrieve Setting with given id
Parameters
- id(Integer)
-
Id of Setting to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (Setting|nil)
-
Corresponding setting, or nil if no setting with this id exists
465 466 467 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 465 def setting(id, = {}) run_query() { Setting.find(id) } end |
#user_credential(id, options = {}) ⇒ Object
Retrieve UserCredential with given id
Parameters
- id(Integer)
-
Id of UserCredential to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (UserCredential|nil)
-
Corresponding user credential, or nil if no credential with this id exists
439 440 441 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 439 def user_credential(id, = {}) run_query() { UserCredential.find(id) } end |
#user_credential_from_fingerprint(public_value_fingerprint, options = {}) ⇒ Object
Retrieve UserCredential with given public value fingerprint
Parameters
- public_value_fingerprint(String)
-
Public value fingerprint of UserCredential to be retrieved
- options(Hash)
-
Query options:
- :retryable_error(Boolean)
-
Whether to raise RetryableError if exceed maximum retries
Return
- (UserCredential|nil)
-
Corresponding user credential, or nil if no credential with this fingerprint exists
452 453 454 |
# File 'lib/right_infrastructure_agent/models_helper.rb', line 452 def user_credential_from_fingerprint(public_value_fingerprint, = {}) run_query() { UserCredential.find_by_public_value_fingerprint(public_value_fingerprint) } end |