Module: Smash::CloudPowers::SelfAwareness

Extended by:
Helper, Smash::CloudPowers::Synapse::Pipe, Smash::CloudPowers::Synapse::Queue, Zenv
Includes:
AwsResources
Included in:
Smash::CloudPowers, Node
Defined in:
lib/cloud_powers/self_awareness.rb

Instance Method Summary collapse

Methods included from Helper

attr_map!, available_resources, called_from, create_logger, deep_modify_keys_with, format_error_message, log_file, logger, modify_keys_with, smart_retry, task_path, task_require_path, to_camel, to_hyph, to_i_var, to_pascal, to_ruby_file_name, to_snake, update_message_body, valid_json?, valid_url?

Methods included from Smash::CloudPowers::Synapse::Pipe

create_stream, flow_from_pipe, flow_to_pipe, from_pipe, message_body_collection, pipe_message_body, pipe_to, stream_config, stream_exists?, stream_status

Methods included from Zenv

env_vars, file_tree_search, i_vars, project_root, project_root=, system_vars, zfind

Methods included from AwsResources

#ec2, #image, #kinesis, #region, #s3, #sns, #sqs

Methods included from Auth

creds, region

Methods included from Smash::CloudPowers::Synapse::Queue

board_name, build_queue, create_queue!, delete_queue_message, get_queue_message_count, pluck_queue_message, poll, queue_exists?, queue_poller, queue_search, send_queue_message

Instance Method Details

#boot_time ⇒ Object

Gets the instance time or the time it was called and as seconds from epoch

Returns Integer

Notes

  • TODO: use time codes


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/cloud_powers/self_awareness.rb', line 25

def boot_time
  begin
    ec2(Smash::CloudPowers::AwsStubs.node_stub)
    sqs(Smash::CloudPowers::AwsStubs.queue_stub)
    sns(Smash::CloudPowers::AwsStubs.broadcast_stub)
    kinesis(Smash::CloudPowers::AwsStubs.pipe_stub)

    @boot_time ||=
      ec2.describe_instances(dry_run: zfind(:testing), instance_ids:[instance_id]).
        reservations[0].instances[0].launch_time.to_i
  rescue Aws::EC2::Errors::DryRunOperation => e
    logger.info "dry run for testing: #{e}"
    @boot_time ||= Time.now.to_i # comment the code below for development mode
  end
end

#die! ⇒ Object

Send a status message on the status Pipe then terminates the instance.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/cloud_powers/self_awareness.rb', line 42

def die!
  Thread.kill(@status_thread) unless @status_thread.nil?
  # blame = errors.sort_by(&:reverse).last.first
  logger.info("The cause for the shutdown is TODO: fix SmashErrors")

  pipe_to(:status_stream) do
    {
      instanceID: @instance_id,
      type: 'status-update',
      content: 'dying'
      # extraInfo: blame
    }
  end

  [:count, :wip].each do |queue|
    delete_queue_message(queue, max_number_of_messages: 1)
  end
  send_logs_to_s3
  begin
    ec2.terminate_instances(dry_run: zfind('testing'), ids: [@instance_id])
  rescue Aws::EC2::Error::DryRunOperation => e
    logger.info "dry run testing in die! #{format_error_message(e)}"
    @instance_id
  end
end

#get_awareness! ⇒ Object

Get resource metadata, public host, boot time and task name and set them as instance variables

Returns Array of values, each from a separate key, set in the order in the Array

Notes

  • See #metadata_request()
  • See #attr_map!()


78
79
80
81
82
83
84
# File 'lib/cloud_powers/self_awareness.rb', line 78

def get_awareness!
  keys = 
  attr_map!(keys) { |key| (key) }
  boot_time # gets and sets @boot_time
  task_name # gets and sets @task_name
  instance_url # gets and sets @instance_url
end

#instance_id ⇒ Object

Assures there is always a valid instance id because many other Aws calls require it

Returns String



90
91
92
# File 'lib/cloud_powers/self_awareness.rb', line 90

def instance_id
  @instance_id ||= ('instance_id')
end

#instance_url ⇒ Object

Gets and sets the public hostname of the instance

Returns String - the Public Hostname for this instance

Notes

  • When this is being called from somewhere other than an Aws instance, a hardcoded example URL is returned because of the way instance metadata is retrieved


102
103
104
105
106
107
108
109
# File 'lib/cloud_powers/self_awareness.rb', line 102

def instance_url
  @instance_url ||= unless zfind('TESTING')
    hostname_uri = 'http://169.254.169.254/latest/meta-data/public-hostname'
    HTTParty.get(hostname_uri).parsed_response
  else
    'http://ec2-000-0-000-00.compute-0.amazonaws.com'
  end
end

#metadata_request(key = '') ⇒ Object

Makes the http request to self/meta-data to get all the metadata keys or, if a key is given, the method makes the http request to get that particular key from the metadata

Parameters

  • key String (optional) (default is '') - the key for the metadata information you want from this instance.

Returns

  • Array if key is blank
  • String if key is given

Example metadata_request

=> a Hash containing every key => value pair AWS provides

metadata_request('instance-id')

=> 'abc-1234'



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/cloud_powers/self_awareness.rb', line 128

def (key = '')
  key = to_hyph(key)
  begin
    unless zfind('TESTING')
       = "http://169.254.169.254/latest/meta-data/#{key}"
      HTTParty.get().parsed_response.split("\n")
    else
      require_relative '../stubs/aws_stubs'
       = Smash::CloudPowers::AwsStubs.

      key.empty? ? .keys : [to_hyph(key)]
    end
  rescue Exception => e
    logger.fatal format_error_message e
  end
end

#run_time ⇒ Object

Return the time since boot_time

Returns Integer

Notes

  • TODO: refactor to use valid time stamps for better tracking.
  • reason -> separate regions or OSs etc.


153
154
155
# File 'lib/cloud_powers/self_awareness.rb', line 153

def run_time
  Time.now.to_i - boot_time
end

#send_frequent_status_updates(opts = {}) ⇒ Object

Send a message on a Pipe at an interval

Parameters

  • opts Hash (optional)
    • :interval - how long to wait between sending updates
    • :stream_name - name of stream you want to use


163
164
165
166
167
168
169
170
171
172
# File 'lib/cloud_powers/self_awareness.rb', line 163

def send_frequent_status_updates(opts = {})
  sleep_time = opts.delete(:interval) || 10
  stream = opts.delete(:stream_name)
  while true
    message = lambda { |o| update_message_body(o.merge(content: status)) }
    logger.info "Send update to status board #{message.call(opts)}"
    pipe_to(stream || :status_stream) { message.call(opts) }
    sleep sleep_time
  end
end

#status(id = @instance_id) ⇒ Object

Get the instance status.

Parameters

  • id String (optional) (default is @instance_id)

Returns String



180
181
182
183
184
185
186
187
188
# File 'lib/cloud_powers/self_awareness.rb', line 180

def status(id = @instance_id)
  begin
    ec2.describe_instances(dry_run: zfind('TESTING'), instance_ids: [id]).
      reservations[0].instances[0].state.name
  rescue Aws::EC2::Errors::DryRunOperation => e
    logger.info "Dry run flag set for testing: #{e}"
    'testing'
  end
end

#task_name(id = @instance_id) ⇒ Object

Check self-tags for 'task' and act as an attr_accessor. A different node's tag's can be checked for a task by passing that instance's id as a parameter

Parameters

  • id String (optional) (default is @instance_id) - instance you want a tag from

Returns String



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/cloud_powers/self_awareness.rb', line 199

def task_name(id = @instance_id)
  # get @task_name
  return @task_name unless @task_name.nil?
  # set @task_name
  # TODO: get all tasks instead of just the first
  resp = ec2.describe_instances(instance_ids: [id].flatten).reservations.first
  return @task_name = nil if resp.nil?
  @task_name = resp.instances[0].tags.select do |t|
    t.value if t.key == 'taskType'
  end.first
end

#time_is_up? ⇒ Boolean

This method will return true if:

  • The run time is more than 5 minutes and
  • The run time is 5 minutes from the hour mark from when the instance started and will return false otherwise

Returns Boolean

Returns:

  • (Boolean)


219
220
221
222
223
224
225
# File 'lib/cloud_powers/self_awareness.rb', line 219

def time_is_up?
  an_hours_time = 60 * 60
  five_minutes_time = 60 * 5

  return false if run_time < five_minutes_time
  run_time % an_hours_time < five_minutes_time
end