cloud_powers 0.2.7.23 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.test.env.example +6 -6
  4. data/.travis.yml +1 -1
  5. data/README +190 -0
  6. data/cloud_powers.gemspec +4 -4
  7. data/lib/cloud_powers.rb +3 -13
  8. data/lib/cloud_powers/aws_resources.rb +21 -4
  9. data/lib/cloud_powers/creatable.rb +122 -0
  10. data/lib/cloud_powers/helpers.rb +58 -0
  11. data/lib/cloud_powers/helpers/lang_help.rb +288 -0
  12. data/lib/cloud_powers/helpers/logic_help.rb +152 -0
  13. data/lib/cloud_powers/helpers/path_help.rb +90 -0
  14. data/lib/cloud_powers/node.rb +69 -68
  15. data/lib/cloud_powers/node/instance.rb +52 -0
  16. data/lib/cloud_powers/resource.rb +44 -0
  17. data/lib/cloud_powers/storage.rb +27 -14
  18. data/lib/{stubs → cloud_powers/stubs}/aws_stubs.rb +37 -14
  19. data/lib/cloud_powers/synapse/broadcast.rb +117 -0
  20. data/lib/cloud_powers/synapse/broadcast/channel.rb +44 -0
  21. data/lib/cloud_powers/synapse/pipe.rb +211 -0
  22. data/lib/cloud_powers/synapse/pipe/stream.rb +41 -0
  23. data/lib/cloud_powers/synapse/queue.rb +357 -0
  24. data/lib/cloud_powers/synapse/queue/board.rb +61 -95
  25. data/lib/cloud_powers/synapse/queue/poller.rb +29 -0
  26. data/lib/cloud_powers/synapse/synapse.rb +10 -12
  27. data/lib/cloud_powers/synapse/web_soc.rb +13 -0
  28. data/lib/cloud_powers/synapse/web_soc/soc_client.rb +52 -0
  29. data/lib/cloud_powers/synapse/web_soc/soc_server.rb +48 -0
  30. data/lib/cloud_powers/version.rb +1 -1
  31. data/lib/cloud_powers/zenv.rb +13 -12
  32. metadata +24 -13
  33. data/lib/cloud_powers/context.rb +0 -275
  34. data/lib/cloud_powers/delegator.rb +0 -113
  35. data/lib/cloud_powers/helper.rb +0 -453
  36. data/lib/cloud_powers/synapse/websocket/websocclient.rb +0 -53
  37. data/lib/cloud_powers/synapse/websocket/websocserver.rb +0 -46
  38. data/lib/cloud_powers/workflow_factory.rb +0 -160
@@ -0,0 +1,90 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+ require 'uri'
4
+
5
+ module Smash
6
+ module CloudPowers
7
+ module PathHelp
8
+
9
+ # Gives a common home for jobs to live so they can be easily grouped and
10
+ # found. This method will create nested directories, based on the
11
+ # <tt>#project_root()</tt> method and an additional 'lib/jobs' directory.
12
+ # If no project root has been set by the time this method is called, a new
13
+ # directory will be created relative to the gem's project root.
14
+ #
15
+ # Returns
16
+ # +String+
17
+ #
18
+ # Notes
19
+ # * # If no project root has been set by the time this method is called, a new
20
+ # directory will be created relative to the gem's project root. This might
21
+ # have deeper implications than you want to deal with so it's always a good
22
+ # idea to set your project root as soon as you can.
23
+ # * TODO: find a way to have this method figure out the actual project's
24
+ # root, as opposed to just making common <i>"good"</i> assumptions.
25
+ def job_home
26
+ string_th = FileUtils.mkdir_p("#{project_root}/lib/jobs/").first
27
+ @job_home ||= Pathname.new(string_th).realpath.to_s
28
+ end
29
+
30
+ # Gives the path from the project root to lib/jobs[/#{file}.rb]
31
+ #
32
+ # Parameters
33
+ # * file +String+ (optional) (default is '') - name of a file
34
+ #
35
+ # Returns
36
+ # * path/file +String+ if +file+ parameter is given. return has
37
+ # '.rb' extension included
38
+ # * file +String+ if +file+ parameter is not given it will return the
39
+ # <tt>#job_require_path()</tt>
40
+ #
41
+ # Notes
42
+ # * See <tt>#job_home</tt>
43
+ def job_path(file = '')
44
+ return job_home if file.empty?
45
+ Pathname.new("#{job_home}/#{file}").to_s
46
+ end
47
+
48
+
49
+ # Check if the job file exists in the job directory
50
+ #
51
+ # Parameters
52
+ # * file +String+
53
+ #
54
+ # Returns
55
+ # +Boolean+
56
+ #
57
+ # Notes
58
+ # * See +#job_home()+
59
+ def job_exist?(file)
60
+ begin
61
+ File.new("#{job_home}/#{file}")
62
+ true
63
+ rescue Errno::ENOENT
64
+ false
65
+ end
66
+ end
67
+
68
+ # Gives the path from the project root to lib/jobs[/file]
69
+ #
70
+ # Parameters String (optional)
71
+ # * file_name name of a file
72
+ #
73
+ # Returns
74
+ # * path/file +String+ if +file_name+ was given
75
+ # * path to job_directory if +file_name+ was <i>not</i> given
76
+ #
77
+ # Notes
78
+ # * Neither path nor file will have a file extension
79
+ # * See <tt>#job_home</tt>
80
+ def job_require_path(file_name = '')
81
+ begin
82
+ file_sans_extension = File.basename(file_name, '.*')
83
+ (Pathname.new(job_home) + file_sans_extension).to_s
84
+ rescue Errno::ENOENT
85
+ nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -1,36 +1,41 @@
1
- require_relative 'auth'
2
- require_relative 'helper'
3
- require_relative 'self_awareness'
4
- require_relative 'zenv'
1
+ require 'cloud_powers/auth'
2
+ require 'cloud_powers/helpers'
3
+ require 'cloud_powers/zenv'
4
+ require 'cloud_powers/node/instance'
5
5
 
6
6
  module Smash
7
7
  module CloudPowers
8
8
  module Node
9
9
  include Smash::CloudPowers::Auth
10
- include Smash::CloudPowers::Helper
11
- include Smash::CloudPowers::SelfAwareness
10
+ include Smash::CloudPowers::Helpers
12
11
  include Smash::CloudPowers::Zenv
13
12
 
14
- # These are sensible defaults that can be overriden by providing a Hash as a param.
13
+ # This method adds certain tags to an array of resource ids.
15
14
  #
16
15
  # Parameters
17
- # * opts +Hash+ (optional)
18
- # the opts Hash should have values that should be used instead of the default
19
- # configuration.
20
- def node_config(opts = {})
21
- {
22
- dry_run: zfind(:testing) || false,
23
- image_id: image('crawlbotprod').image_id, # image(:neuron).image_id
24
- instance_type: 't2.nano',
25
- min_count: opts[:max_count] || 0,
26
- max_count: 0,
27
- key_name: 'crawlBot',
28
- security_groups: ['webCrawler'],
29
- security_group_ids: ['sg-940edcf2'],
30
- placement: { availability_zone: 'us-west-2c' },
31
- disable_api_termination: 'false',
32
- instance_initiated_shutdown_behavior: 'terminate'
33
- }.merge(opts)
16
+ # * ids +Array+|+String+ - an Array or a single instance id, as an Array
17
+ # of Strings or a single String
18
+ # * tags +Array+ - an Array of key, value hash
19
+ #
20
+ # Returns
21
+ # * Returns an empty response.
22
+ #
23
+ # Examples
24
+ # create_tag('ami-2342354', tags: { key: "stack", value: "production"})
25
+ # or
26
+ # create_tag(['ami-2432342'], tags: [{ key: 'stack', value: 'production' }])
27
+ # or any permutation of those
28
+ def batch_tag(ids, tags, client: ec2)
29
+ tags_opts = { resources: ids, tags: tags }
30
+ ec2.create_tags(tags_opts)
31
+ logger.info "tags for #{ids} created"
32
+ end
33
+
34
+ def build_node(name:, client: ec2, **config)
35
+ resp = Smash::CloudPowers::Node::Instance.build(name: name, client: ec2, **config)
36
+ i_var_name = "#{name}_node"
37
+ instance_attr_accessor i_var_name
38
+ instance_variable_set(to_i_var(i_var_name), resp)
34
39
  end
35
40
 
36
41
  # Uses +Aws::EC2#run_instances()+ to create nodes (Neurons or Cerebrums), at
@@ -43,58 +48,54 @@ module Smash
43
48
  # * opts +Hash+ (optional)
44
49
  # an optional instance configuration hash can be passed, which will override
45
50
  # the values in the default configuration returned by #instance_config()
46
- def spin_up_neurons(opts = {}, tags = [])
47
- should_wait = opts.delete(:wait) || true
48
- ids = nil
49
- begin
50
-
51
- response = ec2.run_instances(node_config(opts))
52
- ids = response.instances.map(&:instance_id)
53
-
54
- if should_wait
55
- count = 0
56
- begin
57
- ec2.wait_until(:instance_running, instance_ids: ids) do
58
- logger.info "waiting for #{ids.count} Neurons to start..."
59
- end
60
- rescue Aws::Waiters::Errors::WaiterFailed => e
61
- # TODO: deal with failed instances
62
- # redo unless (count += 1 <=3 )
63
- end
64
- end
65
-
66
- batch_tag(ids, tags) unless tags.empty?
67
- ids
68
-
69
- rescue Aws::EC2::Errors::DryRunOperation
70
- ids = (1..(opts[:max_count] || 0)).to_a.map { |n| n.to_s }
71
- logger.info "waiting for #{ids.count} Neurons to start..."
72
- end
73
-
74
- ids
51
+ def create_node(name:, client: ec2, **config)
52
+ resp = Smash::CloudPowers::Node::Instance.create!(name: name, client: ec2, **config)
53
+ i_var_name = "#{name}_node"
54
+ instance_attr_accessor i_var_name
55
+ instance_variable_set(to_i_var(i_var_name), resp)
75
56
  end
76
57
 
77
- # This method adds certain tags to an array of resource ids.
58
+ # Uses +Aws::EC2#run_instances()+ to create nodes (Neurons or Cerebrums),
59
+ # at a rate of 0..(n <= 100) at a time, until the required number of
60
+ # instances has been started. The #instance_config() method is used to
61
+ # create instance configuration for the #run_instances method by using
62
+ # the opts hash that was provided as a parameter.
78
63
  #
79
64
  # Parameters
80
- # * ids +Array+|+String+ - an Array or a single instance id, as an Array of Strings or a single String
81
- # * tags +Array+ - an Array of key, value hash
65
+ # * opts +Hash+ (optional) - an optional instance configuration hash can
66
+ # be passed, which will override the values in the default configuration
67
+ # returned by <tt>#instance_config()</tt>
82
68
  #
83
69
  # Returns
84
- # * Returns an empty response.
85
- #
86
- # Examples
87
- # create_tag('ami-2342354', tags: { key: "stack", value: "production"})
88
- # or
89
- # create_tag(['ami-2432342'], tags: [{ key: 'stack', value: 'production' }])
90
- # or any permutation of those
91
-
92
- def batch_tag(ids, tags)
93
- tags_opts = { resources: ids, tags: tags }
94
- ec2.create_tags(tags_opts)
95
- logger.info "tags for #{ids} created"
70
+ # +Array+ of responses from
71
+ def create_nodes(name:, client: ec2, **config)
72
+ resp = ec2.run_instances(node_config(config)).instances
73
+ i_var_name = "#{name}_nodes"
74
+ instance_attr_accessor i_var_name
75
+ instance_variable_set(to_i_var(i_var_name), resp)
96
76
  end
97
77
 
78
+ # These are sensible defaults that can be overriden by providing a Hash as a param.
79
+ #
80
+ # Parameters
81
+ # * opts +Hash+ (optional)
82
+ # the opts Hash should have values that should be used instead of the default
83
+ # configuration.
84
+ def node_config(**config)
85
+ {
86
+ dry_run: zfind(:testing) || false,
87
+ image_id: image(zfind(:node_image)).image_id, # image(:neuron).image_id
88
+ instance_type: 't2.nano',
89
+ min_count: config[:max_count] || 0,
90
+ max_count: 0,
91
+ key_name: 'crawlBot',
92
+ security_groups: ['webCrawler'],
93
+ security_group_ids: ['sg-940edcf2'],
94
+ placement: { availability_zone: 'us-west-2c' },
95
+ disable_api_termination: 'false',
96
+ instance_initiated_shutdown_behavior: 'terminate'
97
+ }.merge(config)
98
+ end
98
99
  end
99
100
  end
100
101
  end
@@ -0,0 +1,52 @@
1
+ require 'cloud_powers/node'
2
+ require 'cloud_powers/resource'
3
+
4
+ module Smash
5
+ module CloudPowers
6
+ module Node
7
+ class Instance < Smash::CloudPowers::Resource
8
+ include Smash::CloudPowers::Node
9
+
10
+ # The name of the Aws::EC2 instance
11
+ attr_accessor :name
12
+ # An Aws::EC2::Client. See <tt>Smash::CloudPowers::AwsResources#ec2()</tt>
13
+ attr_accessor :ec2
14
+
15
+ def initialize(name:, client: ec2, **config)
16
+ super(name: name)
17
+ @ec2 = client
18
+ end
19
+
20
+ # Uses +Aws::EC2#run_instances()+ to create nodes (Neurons or Cerebrums), at
21
+ # a rate of 0..(n <= 100) at a time, until the required number of instances
22
+ # has been started. The #instance_config() method is used to create instance
23
+ # configuration for the #run_instances method by using the opts hash that was
24
+ # provided as a parameter.
25
+ #
26
+ # Parameters
27
+ # * opts +Hash+ (optional)
28
+ # an optional instance configuration hash can be passed, which will override
29
+ # the values in the default configuration returned by #instance_config()
30
+ def create_resource
31
+ # response = ec2.run_instances(
32
+ # node_config(max_count: 1, self.to_h)
33
+ # ).instances.first
34
+
35
+ instance_attr_accessor response
36
+ # id = @response[:instance_id]
37
+ begin
38
+ ec2.wait_until(:instance_running, instance_ids: [id]) do
39
+ logger.info "waiting for #{ids.count} Neurons to start..."
40
+ end
41
+ rescue Aws::Waiters::Errors::WaiterFailed => e
42
+ # TODO: retry stuff
43
+ # redo unless (count += 1 <=3 )
44
+ end
45
+
46
+ yield self if block_given?
47
+ self
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ require 'cloud_powers/aws_resources'
2
+ require 'cloud_powers/creatable'
3
+ require 'cloud_powers/helpers'
4
+ require 'cloud_powers/zenv'
5
+
6
+ module Smash
7
+ module CloudPowers
8
+ class Resource
9
+ include Smash::CloudPowers::Creatable
10
+ include Smash::CloudPowers::AwsResources
11
+ include Smash::CloudPowers::Helpers
12
+ include Smash::CloudPowers::Zenv
13
+
14
+ # client used to make HTTP requests to the cloud
15
+ attr_accessor :client
16
+ # the name this resource can be set and retrieved by
17
+ attr_accessor :call_name
18
+ # the given name for this resource
19
+ attr_accessor :name
20
+ # whether or not a call has been made to the cloud to back this resource
21
+ attr_accessor :linked
22
+ # the ID in the cloud; e.g. ARN for AWS, etc
23
+ attr_accessor :remote_id
24
+ # tracking on the remote resource that maps to this resource
25
+ attr_accessor :tags
26
+ # the type of resource this was instantiated as
27
+ attr_accessor :type
28
+
29
+ # Usually this method is called by calling +super+ in another class that
30
+ # inherits from this class. The initialize method follows the method
31
+ # signature for the active record-like pattern being followed throughout
32
+ # the code
33
+ def initialize(name:, client: nil, **config)
34
+ @linked = false
35
+ @saved = false
36
+ @client = client
37
+ @type = to_snake(self.class.name.split('::').last)
38
+ @call_name = to_snake("#{name}_#{@type}")
39
+ @name = name
40
+ @tags = Array.new
41
+ end
42
+ end
43
+ end
44
+ end
@@ -6,7 +6,11 @@ module Smash
6
6
  module Storage
7
7
  include Smash::CloudPowers::AwsResources
8
8
 
9
- # Searches a local task storage location for the given +file+ name
9
+ def local_job_file_exists?(file)
10
+ File.exists?(job_path(to_ruby_file_name(file)))
11
+ end
12
+
13
+ # Searches a local job storage location for the given +file+ name
10
14
  # if it exists - exit the method
11
15
  # if it does <i>not</i> exist - get the file from s3 and place it in
12
16
  # the directory that was just searched bucket using +#zfind()+
@@ -22,31 +26,32 @@ module Smash
22
26
  # project_root- |
23
27
  # |_sub_directory
24
28
  # | |_current_file.rb
25
- # |_task_storage
29
+ # |_job_storage
26
30
  # | |_demorific.rb
27
31
  # | |_foobar.rb
28
32
  # * code:
29
- # source_task('demorific')
33
+ # source_job('demorific')
30
34
  # # file tree doesn't change because this file exists
31
- # source_task('custom_greetings')
35
+ # source_job('custom_greetings')
32
36
  # # file tree now looks like this
33
37
  # * new file tree
34
38
  # project_root- |
35
39
  # |_sub_directory
36
40
  # | |_current_file.rb
37
- # |_task_storage
41
+ # |_job_storage
38
42
  # | |_demorific.rb
39
43
  # | |_foobar.rb
40
44
  # | |_custom_greetings.js # could be an after effects JS script
41
- def source_task(file)
45
+ def source_job(file)
42
46
  # TODO: better path management
43
- bucket = zfind('task storage')
44
- if task_path(to_ruby_file_name(file)).nil?
47
+ bucket = zfind('job storage')
48
+ if job_path(to_ruby_file_name(file)).nil?
45
49
  objects = s3.list_objects(bucket: bucket).contents.select do |f|
46
50
  /#{Regexp.escape file}/i =~ f.key
47
51
  end
52
+
48
53
  objects.each do |obj|
49
- s3.get_object(bucket: bucket, key: obj.key, response_target: task_path(file))
54
+ s3.get_object(bucket: bucket, key: obj.key, response_target: job_path(file))
50
55
  end
51
56
  end
52
57
  end
@@ -55,17 +60,25 @@ module Smash
55
60
  #
56
61
  # Parameters
57
62
  # * bucket +String+ - the bucket to search through in AWS
58
- # * pattern +Regex+ - the Regex pattern you want to use for the
59
- # search
63
+ # * pattern +Regexp+|+nil+ - the Regex pattern you want to use for the
64
+ # search. nil doesn't cause an exception but probably returns <tt>[]</tt>
60
65
  #
61
66
  # Example
62
- # matches = search('neuronTasks', /[Dd]emo\w*/)
67
+ # matches = search('neuronJobs', /[Dd]emo\w*/)
63
68
  # # => ['Aws::S3::Type::ListObjectOutPut','Aws::S3::Type::ListObjectOutPut',...] # anything that matched that regex
64
69
  # matches.first.contents.size
65
70
  # # => 238934 # integer representation of the file size
66
71
  def search(bucket, pattern)
67
- s3.list_objects(bucket: bucket).contents.select do |o|
68
- o.key =~ pattern
72
+ return [] if bucket.nil?
73
+
74
+ begin
75
+ pattern = /#{pattern}/ unless pattern.kind_of? Regexp
76
+ s3.list_objects(bucket: bucket).contents.select do |o|
77
+ o.key =~ pattern || /#{o.key}/ =~ pattern.to_s
78
+ end
79
+ rescue Aws::S3::Errors::NoSuchBucket => e
80
+ logger.info format_error_message e
81
+ return # log that the bucket doesn't exist but don't explode
69
82
  end
70
83
  end
71
84
 
@@ -74,7 +74,7 @@ module Smash
74
74
  # for your own custom configuration
75
75
  def self.node_stub(opts = {})
76
76
  time = opts[:launch_time] || Time.new((Time.now.utc.to_i / 86400 * 86400)) # midnight
77
- tags = [{key: 'task', value: 'test'}]
77
+ tags = [{key: 'job', value: 'test'}]
78
78
  {
79
79
  stub_responses: {
80
80
  create_tags: {},
@@ -143,18 +143,22 @@ module Smash
143
143
  stub.merge(opts.select { |k,v| stub.key? k })
144
144
  end
145
145
 
146
- # Get or create an Kinesis client and cache that client so that a Context is more well tied together
146
+ # Get or create an Kinesis client and cache that client so that a Context
147
+ # is more well tied together
147
148
  # Parameters
148
149
  # * opts <tt>Hash</tt>
149
- # * * stub_responses: defaulted to false but it can be overriden with the desired responses for local testing
150
- # * * region: defaulted to use the `#region()` method
151
- # * * AWS::Credentials object, which will also scour the context and environment for your keys
150
+ # * stub_responses: defaulted to false but it can be overriden with the
151
+ # desired responses for local testing
152
+ # * region: defaulted to use the `#region()` method
153
+ # * AWS::Credentials object, which will also scour the context and
154
+ # environment for your keys
152
155
  #
153
156
  # Returns
154
157
  # AWS::Kinesis client
155
158
  #
156
159
  # Example
157
- # # sets and gets an Kinesis client. No need to set a variable because one was
160
+ # # sets and gets an Kinesis client. No need to set a variable because
161
+ # one was
158
162
  # # just created as +@kinesis+ and is set to the client
159
163
  # kinesis(Smash::CloudPowers::AwsStubs.pipe_stub)
160
164
  #
@@ -175,6 +179,7 @@ module Smash
175
179
  },
176
180
  describe_stream: {
177
181
  stream_description: {
182
+ stream_creation_timestamp: Time.now - 10,
178
183
  stream_name: opts[:name] || 'testPipe',
179
184
  stream_arn: 'arnarnarnarnar',
180
185
  stream_status: 'ACTIVE',
@@ -223,7 +228,25 @@ module Smash
223
228
  def self.storage_stub(opts = {})
224
229
  {
225
230
  stub_responses: {
226
- head_bucket: {}
231
+ head_bucket: {
232
+
233
+ },
234
+ list_objects: {
235
+ is_truncated: false,
236
+ contents: [
237
+ key: 'testinz',
238
+ last_modified: Time.now,
239
+ etag: 'asdf1234',
240
+ size: 123,
241
+ storage_class: 'STANDARD',
242
+ owner: {display_name: 'snargle', id: 'id-bargle132'}
243
+ ],
244
+ name: 'testFile',
245
+ prefix: 'test',
246
+ delimiter: ' ',
247
+ max_keys: 1234,
248
+ common_prefixes: [{ prefix: 'test' }]
249
+ }
227
250
  }
228
251
  }
229
252
  end
@@ -250,14 +273,14 @@ module Smash
250
273
  # for your own custom configuration
251
274
  def self.queue_stub(opts = {})
252
275
  msg_body = if opts[:body]
253
- if opts[:body].kind_of? Hash
254
- opts[:body] = opts[:body].to_json
255
- elsif JSON.parse(opts[:body])
256
- begin
257
- opts[:body]
258
- rescue JSON::ParserError
259
- {foo: 'bar'}.to_json
276
+ begin
277
+ if opts[:body].kind_of? Hash
278
+ opts[:body] = opts[:body].to_json
279
+ elsif JSON.parse(opts[:body])
280
+ opts[:body]
260
281
  end
282
+ rescue JSON::ParserError
283
+ {foo: 'bar'}.to_json
261
284
  end
262
285
  end
263
286
  {