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
# File 'lib/cloud_powers/self_awareness.rb', line 25

def boot_time
  begin
    @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.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/cloud_powers/self_awareness.rb', line 37

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!()


73
74
75
76
77
78
79
# File 'lib/cloud_powers/self_awareness.rb', line 73

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



85
86
87
# File 'lib/cloud_powers/self_awareness.rb', line 85

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


97
98
99
100
101
102
103
104
# File 'lib/cloud_powers/self_awareness.rb', line 97

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'



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/cloud_powers/self_awareness.rb', line 123

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::INSTANCE_METADATA_STUB

      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.


148
149
150
# File 'lib/cloud_powers/self_awareness.rb', line 148

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


158
159
160
161
162
163
164
165
166
167
# File 'lib/cloud_powers/self_awareness.rb', line 158

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



175
176
177
178
179
180
181
182
183
# File 'lib/cloud_powers/self_awareness.rb', line 175

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



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/cloud_powers/self_awareness.rb', line 194

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)


214
215
216
217
218
219
220
# File 'lib/cloud_powers/self_awareness.rb', line 214

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