cloud_powers 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +27 -21
- data/cloud_powers.gemspec +18 -17
- data/lib/cloud_powers/aws_resources.rb +39 -19
- data/lib/cloud_powers/context.rb +3 -3
- data/lib/cloud_powers/helper.rb +161 -21
- data/lib/cloud_powers/node.rb +60 -0
- data/lib/cloud_powers/self_awareness.rb +48 -31
- data/lib/cloud_powers/synapse/queue/board.rb +7 -2
- data/lib/cloud_powers/version.rb +1 -1
- data/lib/cloud_powers.rb +2 -0
- metadata +8 -7
- data/lib/cloud_powers/.DS_Store +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56d3873e3042a66d3fd0b284b3b5fe88f49803fb
|
4
|
+
data.tar.gz: dd5d52611cc92cd66c52a08ecb905e0bc0d28788
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f72cd7f864047a6b1c5060b31fb2d2f75a17df0b22ca72bdc6c545c39091a0e622d94d5db42e2f65840e29c0efd86b5bf2ac98501c7bafa5db4c4466db00f87a
|
7
|
+
data.tar.gz: b0403c5ae413ecbf7b94e0c4296384427b38e96381bf2d6c471cf1e8ae8190225e0e6439de7a66ab5b7a20ce83094e369cf5dc593af2355e875f02c2a0e141f9
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,13 @@
|
|
2
2
|
|
3
3
|
## Description
|
4
4
|
|
5
|
-
CloudPowers is a wrapper around certain AWS services. It was developed with the _need_ instead of the _resource_ in mind so even though AWS is the only service provider included now, it shouldn't be the only one forever. There are several pieces of the Cloud Power module to talk about:
|
5
|
+
CloudPowers is a wrapper around certain AWS services. It was developed with the _need_ instead of the _resource_ in mind so even though AWS is the only service provider included now, it shouldn't be the only one forever. There are several pieces of the Cloud Power module to talk about:
|
6
|
+
* SelfAwareness: Gathers data about "self"
|
7
|
+
* Synapse: Handles communication needs
|
8
|
+
* Delegator: dynamically include Ruby executables from S3
|
9
|
+
and more...
|
10
|
+
|
11
|
+
* Each module in Cloud Powers is in charge of a specific type of task that helps bring projects together and communicate with the outside world.
|
6
12
|
|
7
13
|
Below is a breakdown of the installation, common services provided and an example or 2 of popular methods.
|
8
14
|
_Better docs are on the way_
|
@@ -11,7 +17,7 @@ _Better docs are on the way_
|
|
11
17
|
|
12
18
|
Add this line to your application's Gemfile:
|
13
19
|
|
14
|
-
```
|
20
|
+
```Ruby
|
15
21
|
gem 'cloud_powers'
|
16
22
|
```
|
17
23
|
|
@@ -26,17 +32,21 @@ Or install it yourself as:
|
|
26
32
|
then either:
|
27
33
|
* set environment variables that matches the below group
|
28
34
|
* fill out a .env file and load it from the class that is using CloudPowers, like this
|
29
|
-
|
35
|
+
Notes:
|
36
|
+
* The code does its best in many cases to make a good guess at some of the configuration
|
37
|
+
for you but if you set them in your system environment variables or in the .env file,
|
38
|
+
it'll have a much better
|
39
|
+
```Ruby
|
30
40
|
require 'dotenv'
|
31
41
|
Dotenv.load('path to your .env file')
|
32
42
|
```
|
33
43
|
_things you need for pre-v1_:
|
34
|
-
```
|
44
|
+
```Ruby
|
35
45
|
# Aws access:
|
36
46
|
AWS_ACCESS_KEY_ID=""
|
37
47
|
AWS_SECRET_ACCESS_KEY=""
|
38
48
|
|
39
|
-
# Aws areas and auth-related locations:
|
49
|
+
# Aws areas and auth-related locations:
|
40
50
|
AWS_REGION=""
|
41
51
|
|
42
52
|
# Aws Build info:
|
@@ -72,11 +82,11 @@ FINISHED_QUEUE_ADDRESS=""
|
|
72
82
|
|
73
83
|
### SelfAwareness
|
74
84
|
* gets and sets info about the instance -> Neuron/Cerebrum/etc)
|
75
|
-
```
|
85
|
+
```Ruby
|
76
86
|
get_awareness!
|
77
87
|
```
|
78
88
|
* retrieves and sets all metadata from the EC2 instance and a few other things like the instance hostname (can find the instance IP from here).
|
79
|
-
* See EC2 metadata for details on the instance/node metadata that is set.
|
89
|
+
* See EC2 metadata for details on the instance/node metadata that is set.
|
80
90
|
* Additionally, the instance public hostname, id and a few others are set using other-than-EC2-metadata methods.
|
81
91
|
|
82
92
|
|
@@ -97,7 +107,7 @@ FINISHED_QUEUE_ADDRESS=""
|
|
97
107
|
* like a task list or a message board for asynchronous communication
|
98
108
|
* Board <Struct>:
|
99
109
|
* interface with Queue config, data, name, etc.
|
100
|
-
```
|
110
|
+
```Ruby
|
101
111
|
default_workflow = Workflow.new
|
102
112
|
board = Board.new(default_workflow)
|
103
113
|
board.name
|
@@ -110,15 +120,15 @@ FINISHED_QUEUE_ADDRESS=""
|
|
110
120
|
Example usage:
|
111
121
|
1. Give the entire stream name or a symbol or string that is found in the .env for the name of the Queue
|
112
122
|
2. provide a block that will create the record that gets sent to the board
|
113
|
-
```
|
123
|
+
```Ruby
|
114
124
|
poll(board_name <string|symbol>, opts <optional config Hash>) { block }
|
115
125
|
```
|
116
126
|
or
|
117
|
-
```
|
127
|
+
```Ruby
|
118
128
|
poll(:backlog) { |msg, stats| Task.new(instance_id, msg) if stats.success? }
|
119
129
|
```
|
120
130
|
or
|
121
|
-
```
|
131
|
+
```Ruby
|
122
132
|
poll(:backlog, wait_time: 30, delete: false) do
|
123
133
|
edited_message = sitrep.merge(foo: 'bar')
|
124
134
|
update = some_method(edited_message)
|
@@ -126,26 +136,26 @@ FINISHED_QUEUE_ADDRESS=""
|
|
126
136
|
```
|
127
137
|
#### Pipe
|
128
138
|
* Good for real time information passing, like status updates, results reporting, operations, etc.
|
129
|
-
* The Pipe module is for communicating via streams
|
139
|
+
* The Pipe module is for communicating via streams. Piping is meant to be a way to communicate status, problems and other general info or huge result sets and things of that nature. There can be very high traffic through the pipe or none at all. Very soon (probably V-0.2.6 or 7), Cerebrums will be data consumers, to the Java KCL and MultiLangDaemon level, so keeping messages straight is done via partition ID. The partition ID of any message is to identify which node the message is about or the "batch" or "group" or any other concept like that. So the instance ID is used in nodes like the Neuron and Cerebrum and other identifiers that are deemed best are used in other projects as a batch ID.
|
130
140
|
|
131
141
|
Example usage:
|
132
|
-
|
142
|
+
```Ruby
|
133
143
|
pipe_to(stream_name <string/symbol>) { &block }
|
134
144
|
```
|
135
|
-
|
145
|
+
```Ruby
|
136
146
|
pipe_to(:status_queue) { sitrep(content: 'workflowComplete') }
|
137
147
|
```
|
138
148
|
and for multiple records (KCL)
|
139
|
-
```
|
149
|
+
```Ruby
|
140
150
|
flow_to(stream_name <string/symbol>) { &block }
|
141
151
|
```
|
142
|
-
```
|
152
|
+
```Ruby
|
143
153
|
flow_to(:status_queue) do
|
144
154
|
interesting_instances = neurons.map do |neuron|
|
145
155
|
return neuron if neuron.workflow.done?
|
146
156
|
end
|
147
157
|
find_efficient_neurons(interesting_instances) # this gets sent through the Pipe
|
148
|
-
end
|
158
|
+
end
|
149
159
|
```
|
150
160
|
|
151
161
|
|
@@ -153,10 +163,6 @@ FINISHED_QUEUE_ADDRESS=""
|
|
153
163
|
* Allows the nodes to have a collective awareness of each other, the environment they work in and other Jobs (Coming soon...)
|
154
164
|
|
155
165
|
|
156
|
-
### Workflow
|
157
|
-
* The workflow is one of the integral ideas behind the Brain architecture. The workflow describes to us humans what state the Job/Task/etc is currently in and it let's the App(s) know what things it can do from any point in the process.
|
158
|
-
|
159
|
-
|
160
166
|
## Development
|
161
167
|
|
162
168
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/cloud_powers.gemspec
CHANGED
@@ -4,30 +4,31 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'cloud_powers/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.required_ruby_version =
|
8
|
-
spec.name =
|
9
|
-
spec.version =
|
10
|
-
spec.author =
|
11
|
-
spec.email =
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
7
|
+
spec.required_ruby_version = '~> 2.3.0'
|
8
|
+
spec.name = 'cloud_powers'
|
9
|
+
spec.version = CloudPowers::VERSION
|
10
|
+
spec.author = 'Adam Phillipps'
|
11
|
+
spec.email = 'adam.phillipps@gmail.com'
|
12
|
+
spec.summary = %q{Cloud providers wrapper. Currently only AWS is supported.}
|
13
|
+
spec.description = <<-EOF
|
14
14
|
CloudPowers is a wrapper around AWS and in the future, other cloud service Providers.
|
15
15
|
It was developed specifically for the Brain project but hopefully can be used
|
16
16
|
in any other ruby project that needs to use cloud service providers' resources.
|
17
|
-
Version 0.2.
|
18
|
-
|
17
|
+
Version 0.2.5 has a little EC2, S3, SQS, SNS and Kinesis and some a few other
|
18
|
+
features you can find in the docs.
|
19
|
+
The next versions will have websockets, IoT, more Kinesis and Workflow integration.
|
19
20
|
This project is actively being developed, so more additions, specs and docs
|
20
|
-
will be updated frequently with new funcionality but the gem will
|
21
|
-
practices for versioning. I always welcome input.
|
21
|
+
will be added and updated frequently with new funcionality but the gem will
|
22
|
+
follow good practices for versioning. I always welcome input.
|
22
23
|
Enjoy!
|
23
24
|
EOF
|
24
|
-
spec.homepage =
|
25
|
-
spec.license =
|
25
|
+
spec.homepage = 'https://github.com/adam-phillipps/cloud_powers'
|
26
|
+
spec.license = 'MIT'
|
26
27
|
|
27
|
-
spec.files =
|
28
|
-
spec.bindir =
|
29
|
-
spec.executables =
|
30
|
-
spec.require_paths =
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
29
|
+
spec.bindir = 'exe'
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ['lib']
|
31
32
|
|
32
33
|
spec.add_runtime_dependency 'activesupport-core-ext', '~> 4'
|
33
34
|
spec.add_runtime_dependency 'aws-sdk', '~> 2'
|
@@ -13,43 +13,63 @@ module Smash
|
|
13
13
|
zfind(:aws_region) || 'us-west-2'
|
14
14
|
end
|
15
15
|
|
16
|
-
def ec2
|
17
|
-
|
16
|
+
def ec2(opts = {})
|
17
|
+
config = {
|
18
|
+
stub_responses: false,
|
18
19
|
region: region,
|
19
20
|
credentials: Auth.creds
|
20
|
-
|
21
|
+
}
|
22
|
+
config = config.merge(opts.select { |k| config.key?(k) })
|
23
|
+
|
24
|
+
@ec2 ||= Aws::EC2::Client.new(config)
|
21
25
|
end
|
22
26
|
|
23
|
-
def image(name)
|
24
|
-
|
27
|
+
def image(name, opts = {})
|
28
|
+
config = {
|
25
29
|
filters: [{ name: 'tag:aminame', values: [name.to_s] }]
|
26
|
-
|
30
|
+
}
|
31
|
+
config = config.merge(opts.select { |k| config.key?(k) })
|
32
|
+
|
33
|
+
ec2(opts).describe_images(config).images.first
|
27
34
|
end
|
28
35
|
|
29
|
-
def kinesis
|
30
|
-
|
36
|
+
def kinesis(opts = {})
|
37
|
+
config = {
|
38
|
+
stub_responses: false,
|
31
39
|
region: region,
|
32
|
-
credentials: Auth.creds
|
33
|
-
|
40
|
+
credentials: Auth.creds
|
41
|
+
}
|
42
|
+
config = config.merge(opts.select { |k| config.key?(k) })
|
43
|
+
|
44
|
+
@kinesis ||= Aws::Kinesis::Client.new(config)
|
34
45
|
end
|
35
46
|
|
36
|
-
def s3
|
37
|
-
|
47
|
+
def s3(opts = {})
|
48
|
+
config = {
|
49
|
+
stub_responses: false,
|
38
50
|
region: region,
|
39
51
|
credentials: Auth.creds
|
40
|
-
|
52
|
+
}
|
53
|
+
config = config.merge(opts.select { |k| config.key?(k) })
|
54
|
+
|
55
|
+
@s3 ||= Aws::S3::Client.new(config)
|
41
56
|
end
|
42
57
|
|
43
|
-
def sns
|
44
|
-
|
58
|
+
def sns(opts = {})
|
59
|
+
config = {
|
60
|
+
stub_responses: false,
|
45
61
|
region: region,
|
46
62
|
credentials: Auth.creds
|
47
|
-
|
63
|
+
}
|
64
|
+
config = config.merge(opts.select { |k| config.key?(k) })
|
65
|
+
|
66
|
+
@sns ||= Aws::SNS::Client.new(config)
|
48
67
|
end
|
49
68
|
|
50
|
-
def sqs
|
51
|
-
@sqs ||= Aws::SQS::Client.new(
|
52
|
-
|
69
|
+
def sqs(opts = {})
|
70
|
+
@sqs ||= Aws::SQS::Client.new({
|
71
|
+
credentials: Auth.creds
|
72
|
+
}.merge(opts)
|
53
73
|
)
|
54
74
|
end
|
55
75
|
end
|
data/lib/cloud_powers/context.rb
CHANGED
@@ -28,7 +28,6 @@ module Smash
|
|
28
28
|
# === @returns
|
29
29
|
# `Smash::Context`
|
30
30
|
def initialize(args)
|
31
|
-
byebug unless valid_args?(args)
|
32
31
|
unless valid_args?(args)
|
33
32
|
raise ArgumentError.new 'Can be either a Hash, JSON, or an Enumerable ' +
|
34
33
|
"arguments: #{args}"
|
@@ -63,9 +62,10 @@ module Smash
|
|
63
62
|
@structure.to_a
|
64
63
|
end
|
65
64
|
|
66
|
-
#
|
65
|
+
# A Hash that represents the resources and some configuration for them
|
66
|
+
# === @returns Hash
|
67
67
|
def structure
|
68
|
-
|
68
|
+
modify_keys_with(@structure) { |key| key.to_sym }
|
69
69
|
end
|
70
70
|
|
71
71
|
# Valid scheme for @structure is assured by running the arguments through
|
data/lib/cloud_powers/helper.rb
CHANGED
@@ -8,36 +8,112 @@ module Smash
|
|
8
8
|
module CloudPowers
|
9
9
|
module Helper
|
10
10
|
|
11
|
+
# Sets an Array of instance variables, individually to a value that a
|
12
|
+
# user given block returns.
|
13
|
+
# === @params Array
|
14
|
+
# * each object will be used as the name for the instance variable that
|
15
|
+
# your block returns
|
16
|
+
# === @&block (optional)
|
17
|
+
# * this is called for each object in the Array and is used as the value
|
18
|
+
# for the instance variable that is being named and created for each key
|
19
|
+
# === @return Array
|
20
|
+
# * each object will either be the result of `#instance_variable_set(key, value)`
|
21
|
+
# or instance_variable_get(key)
|
22
|
+
# === Sampel use:
|
23
|
+
# keys = ['foo', 'bar', 'yo']
|
24
|
+
#
|
25
|
+
# attr_map!(keys) { |key| sleep 1; "#{key}:#{Time.now.to_i}" }
|
26
|
+
# # => ['foo:1475434058', 'bar:1475434059', 'yo:1475434060']
|
27
|
+
#
|
28
|
+
# puts @bar
|
29
|
+
# # => 'bar:1475434059'
|
11
30
|
def attr_map!(keys)
|
12
|
-
keys.map do |
|
13
|
-
|
31
|
+
keys.map do |key|
|
32
|
+
new_i_var = to_i_var(key)
|
14
33
|
value = yield key if block_given?
|
15
|
-
instance_variable_set(
|
34
|
+
instance_variable_set(new_i_var, value) unless instance_variable_get(new_i_var)
|
16
35
|
end
|
17
36
|
end
|
18
37
|
|
19
38
|
# This is a way to find out if you are trying to work with a resource
|
20
39
|
# available to CloudPowers
|
21
40
|
# === @returns <Array>
|
41
|
+
# * Use `.constants` to find all the modules and classes available.
|
42
|
+
# Notes:
|
43
|
+
# TODO: make this smartly pick up all the objects, within reason and
|
44
|
+
# considering need, that we have access to
|
22
45
|
def available_resources
|
23
46
|
[:Task].concat(Smash::CloudPowers.constants)
|
24
47
|
end
|
25
48
|
|
49
|
+
# Does its best job at guessing where this method was called from, in terms
|
50
|
+
# of where it is located on the file system. It helps track down where a
|
51
|
+
# project root is etc.
|
26
52
|
def called_from
|
27
53
|
File.expand_path(File.dirname($0))
|
28
54
|
end
|
29
55
|
|
56
|
+
# creates a default logger
|
57
|
+
# Notes:
|
58
|
+
# * TODO: at least make this have overridable defaults
|
30
59
|
def create_logger
|
31
60
|
logger = Logger.new(STDOUT)
|
32
61
|
logger.datetime_format = '%Y-%m-%d %H:%M:%S'
|
33
62
|
logger
|
34
63
|
end
|
35
64
|
|
65
|
+
# Allows you to modify all keys, including nested, with a block that you pass.
|
66
|
+
# If no block is passed, a copy is returned.
|
67
|
+
# === @params
|
68
|
+
# * params Hash|Array
|
69
|
+
# === @&block (optional)
|
70
|
+
# * should modify the key and return that value so it can be used in the copy
|
71
|
+
# === @returns
|
72
|
+
# * a copy of the given Array or Hash, with all Hash keys modified
|
73
|
+
# === Sample use:
|
74
|
+
# hash = { 'foo' => 'v1', 'bar' => { fleep: { 'florp' => 'yo' } } }
|
75
|
+
# modify_keys_with(hash) { |key| key.to_sym }
|
76
|
+
# # => { foo: 'v1', bar: { fleep: { florp: 'yo' } } }
|
77
|
+
# === Notes:
|
78
|
+
# * see `#modify_keys_with()` for handling first-level keys
|
79
|
+
# * see `#pass_the_buck()` for the way nested structures are handled
|
80
|
+
# * case for different types taken from _MultiXML_ (multi_xml.rb)
|
81
|
+
# * TODO: look at optimization
|
82
|
+
def deep_modify_keys_with(params)
|
83
|
+
case params
|
84
|
+
when Hash
|
85
|
+
params.inject({}) do |carry, (k, v)|
|
86
|
+
carry.tap do |h|
|
87
|
+
if block_given?
|
88
|
+
key = yield k
|
89
|
+
|
90
|
+
value = if v.kind_of?(Hash)
|
91
|
+
deep_modify_keys_with(v) do |new_key|
|
92
|
+
Proc.new.call(new_key)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
v
|
96
|
+
end
|
97
|
+
|
98
|
+
h[key] = value
|
99
|
+
else
|
100
|
+
h[k] = v
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
when Array
|
105
|
+
params.map{ |value| symbolize_keys(value) }
|
106
|
+
else
|
107
|
+
params
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
36
111
|
def errors
|
37
|
-
# TODO: needs work
|
112
|
+
# TODO: wow...needs work
|
38
113
|
$errors ||= SmashError.instance
|
39
114
|
end
|
40
115
|
|
116
|
+
# Join the message and backtrace into a String with line breaks
|
41
117
|
def format_error_message(error)
|
42
118
|
begin
|
43
119
|
[error.message, error.backtrace.join("\n")].join("\n")
|
@@ -58,11 +134,35 @@ module Smash
|
|
58
134
|
@logger ||= create_logger
|
59
135
|
end
|
60
136
|
|
137
|
+
# Allows you to modify all first-level keys with a block that you pass.
|
138
|
+
# If no block is passed, a copy is returned.
|
139
|
+
# === @params
|
140
|
+
# * params Hash|Array
|
141
|
+
# === @&block (optional)
|
142
|
+
# * should modify the key and return that value so it can be used in the copy
|
143
|
+
# === @returns
|
144
|
+
# * a copy of the given Array or Hash, with all Hash keys modified
|
145
|
+
# === Sample use:
|
146
|
+
# hash = { 'foo' => 'v1', 'bar' => { fleep: { 'florp' => 'yo' } } }
|
147
|
+
# modify_keys_with(hash) { |k| k.to_sym }
|
148
|
+
# # => { :foo => 'v1', :bar => { fleep: { 'florp' => 'yo' } } }
|
149
|
+
# === Notes:
|
150
|
+
# * see `#deep_modify_keys_with()` for handling nested keys
|
151
|
+
# * case for different types taken from _MultiXML_ (multi_xml.rb)
|
152
|
+
def modify_keys_with(params)
|
153
|
+
params.inject({}) do |carry, (k, v)|
|
154
|
+
carry.tap do |h|
|
155
|
+
key = block_given? ? (yield k) : k
|
156
|
+
h[key] = v
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
61
161
|
# Lets you retry a piece of logic with 1 second sleep in between attempts
|
62
162
|
# until another bit of logic does what it's supposed to, kind of like
|
63
163
|
# continuing to poll something and doing something when a package is ready
|
64
164
|
# to be taken and processed.
|
65
|
-
# @params:
|
165
|
+
# === @params:
|
66
166
|
# * [allowed_attempts] or Infinity(default) <Number>: The number of times
|
67
167
|
# the loop should be allowed to...well, loop, before a failed retry occurs.
|
68
168
|
# * &test <Block>: A predicate method or block of code that is callable
|
@@ -81,14 +181,10 @@ module Smash
|
|
81
181
|
end
|
82
182
|
end
|
83
183
|
|
84
|
-
def symbolize_keys(hash)
|
85
|
-
hash.inject({}) { |carry, (k, v)| carry.tap { |h| h[k.to_sym] = v } }
|
86
|
-
end
|
87
|
-
|
88
184
|
# Gives the path from the project root to lib/tasks[/#{file}.rb]
|
89
|
-
# @params:
|
185
|
+
# === @params:
|
90
186
|
# * [file] <String>: name of a file
|
91
|
-
# @returns:
|
187
|
+
# === @returns:
|
92
188
|
# * path[/file] <String>
|
93
189
|
# * If a `file` is given, it will have a '.rb' file extension
|
94
190
|
# * If no `file` is given, it will return the `#task_require_path`
|
@@ -98,16 +194,20 @@ module Smash
|
|
98
194
|
end
|
99
195
|
|
100
196
|
# Gives the path from the project root to lib/tasks[/file]
|
101
|
-
# @params
|
102
|
-
# *
|
103
|
-
# @returns:
|
104
|
-
# * path[/file]
|
197
|
+
# === @params String (optional)
|
198
|
+
# * file_name name of a file
|
199
|
+
# === @returns:
|
200
|
+
# * path[/file] String
|
105
201
|
# * Neither path nor file will have a file extension
|
106
202
|
def task_require_path(file_name = '')
|
107
203
|
file = File.basename(file_name, File.extname(file_name))
|
108
204
|
Pathname(__FILE__).parent.dirname + 'tasks' + file
|
109
205
|
end
|
110
206
|
|
207
|
+
# Change strings into camelCase
|
208
|
+
# === @params var String
|
209
|
+
# === @returns String
|
210
|
+
# * givenString
|
111
211
|
def to_camel(var)
|
112
212
|
var = var.to_s unless var.kind_of? String
|
113
213
|
step_one = to_snake(var)
|
@@ -115,20 +215,56 @@ module Smash
|
|
115
215
|
step_two[0, 1].downcase + step_two[1..-1]
|
116
216
|
end
|
117
217
|
|
218
|
+
# Change strings hyphen-delimited-string
|
219
|
+
# === @params var String
|
220
|
+
# === @returns String
|
221
|
+
# * given-string
|
222
|
+
def to_hyph(var)
|
223
|
+
var = var.to_s unless var.kind_of? String
|
224
|
+
|
225
|
+
var.gsub(/:{2}|\//, '-').
|
226
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
227
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
228
|
+
gsub(/\s+/, '-').
|
229
|
+
tr("_", "-").
|
230
|
+
gsub(/^\W/, '').
|
231
|
+
downcase
|
232
|
+
end
|
233
|
+
|
234
|
+
# Change strings into snake_case and add '@' at the front
|
235
|
+
# === @params var String
|
236
|
+
# === @returns String
|
237
|
+
# * @given_string
|
118
238
|
def to_i_var(var)
|
119
239
|
var = var.to_s unless var.kind_of? String
|
120
240
|
/^\W*@\w+/ =~ var ? to_snake(var) : "@#{to_snake(var)}"
|
121
241
|
end
|
122
242
|
|
243
|
+
# Change strings into PascalCase
|
244
|
+
# === @params var String
|
245
|
+
# === @returns String
|
246
|
+
# * givenString
|
123
247
|
def to_pascal(var)
|
124
248
|
var = var.to_s unless var.kind_of? String
|
125
249
|
var.gsub(/^(.{1})|\W.{1}|\_.{1}/) { |s| s.gsub(/[^a-z0-9]+/i, '').capitalize }
|
126
250
|
end
|
127
251
|
|
252
|
+
# Change strings into a ruby_file_name with extension
|
253
|
+
# === @params var String
|
254
|
+
# === @returns String
|
255
|
+
# * given_string.rb
|
256
|
+
# * includes ruby file extension
|
257
|
+
# * see #to_snake()
|
128
258
|
def to_ruby_file_name(name)
|
129
259
|
name[/\.rb$/].nil? ? "#{to_snake(name)}.rb" : "#{to_snake(name)}"
|
130
260
|
end
|
131
261
|
|
262
|
+
# Change strings into snake_case
|
263
|
+
# === @params var String
|
264
|
+
# === @returns String
|
265
|
+
# * given_string
|
266
|
+
# * will not have file extensions
|
267
|
+
# * see #to_ruby_file_name()
|
132
268
|
def to_snake(var)
|
133
269
|
var = var.to_s unless var.kind_of? String
|
134
270
|
|
@@ -140,13 +276,17 @@ module Smash
|
|
140
276
|
downcase
|
141
277
|
end
|
142
278
|
|
279
|
+
# This method provides a default overrideable message body for things like
|
280
|
+
# basic status updates.
|
281
|
+
# === @params Hash
|
282
|
+
# - instanceId:
|
283
|
+
# === Notes:
|
284
|
+
# - camel casing is used on the keys because most other languages prefer
|
285
|
+
# that and it's not a huge problem in ruby. Besides, there's some other
|
286
|
+
# handy methods in this module to get you through those issues, like
|
287
|
+
# `#to_snake()` and or `#symbolize_keys_with()`
|
143
288
|
def update_message_body(opts = {})
|
144
|
-
# TODO:
|
145
|
-
# this should be fixed with Job #sitrep_message
|
146
|
-
# TODO: find a way to trim this method down and get rid
|
147
|
-
# of a lof of the repitition with these messages
|
148
|
-
# IDEA: throw events and have a separate thread listening. the separate
|
149
|
-
# thread could be a communication or status update thread
|
289
|
+
# TODO: Better implementation of merging message bodies and config needed
|
150
290
|
unless opts.kind_of? Hash
|
151
291
|
update = opts.to_s
|
152
292
|
opts[:extraInfo] = { message: update }
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'auth'
|
2
|
+
require_relative 'helper'
|
3
|
+
require_relative 'zenv'
|
4
|
+
|
5
|
+
module Smash
|
6
|
+
module CloudPowers
|
7
|
+
module Node
|
8
|
+
include Smash::CloudPowers::Auth
|
9
|
+
include Smash::CloudPowers::Helper
|
10
|
+
include Smash::CloudPowers::Zenv
|
11
|
+
# These are sensible defaults that can be overriden by providing a Hash as a param.
|
12
|
+
# @params [opts <Hash>]
|
13
|
+
# the opts Hash should have values that should be used instead of the given
|
14
|
+
# configuration.
|
15
|
+
def node_config(opts = {})
|
16
|
+
{
|
17
|
+
dry_run: zfind(:testing) || false,
|
18
|
+
image_id: image('crawlbotprod').image_id, # image(:neuron).image_id
|
19
|
+
instance_type: 't2.nano',
|
20
|
+
min_count: opts[:max_count] || 0,
|
21
|
+
max_count: 0,
|
22
|
+
key_name: 'crawlBot',
|
23
|
+
security_groups: ['webCrawler'],
|
24
|
+
security_group_ids: ['sg-940edcf2'],
|
25
|
+
placement: { availability_zone: 'us-west-2c' },
|
26
|
+
disable_api_termination: 'false',
|
27
|
+
instance_initiated_shutdown_behavior: 'terminate'
|
28
|
+
}.merge(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Uses `Aws::EC2#run_instances` to create nodes (Neurons or Cerebrums), at
|
32
|
+
# a rate of 0..(n <= 100) at a time, until the required number of instances
|
33
|
+
# has been started. The #instance_config() method is used to create instance
|
34
|
+
# configuration for the #run_instances method by using the opts hash that was
|
35
|
+
# provided as a parameter.
|
36
|
+
#
|
37
|
+
# === @params opts Hash (optional)
|
38
|
+
# an optional instance configuration hash can be passed, which will override
|
39
|
+
# the values in the default configuration returned by #instance_config()
|
40
|
+
def spin_up_neurons(opts = {})
|
41
|
+
ids = nil
|
42
|
+
begin
|
43
|
+
response = ec2.run_instances(node_config(opts))
|
44
|
+
ids = response.instances.map(&:instance_id)
|
45
|
+
|
46
|
+
ec2.wait_until(:instance_running, instance_ids: ids) do
|
47
|
+
logger.info "waiting for #{ids.count} Neurons to start..."
|
48
|
+
end
|
49
|
+
|
50
|
+
# tag(ids, { key: 'task', value: to_camel(self.class.to_s) })
|
51
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
52
|
+
ids = (1..(opts[:max_count] || 0)).to_a.map { |n| n.to_s }
|
53
|
+
logger.info "waiting for #{ids.count} Neurons to start..."
|
54
|
+
end
|
55
|
+
|
56
|
+
ids
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -17,14 +17,14 @@ module Smash
|
|
17
17
|
|
18
18
|
# Gets the instance time or the time it was called and as seconds from
|
19
19
|
# epoch
|
20
|
+
# === @returns Integer
|
20
21
|
# TODO: use time codes
|
21
22
|
def boot_time
|
22
23
|
begin
|
23
24
|
@boot_time ||=
|
24
|
-
ec2.describe_instances(dry_run: zfind(
|
25
|
+
ec2.describe_instances(dry_run: zfind(:testing), instance_ids:[instance_id]).
|
25
26
|
reservations[0].instances[0].launch_time.to_i
|
26
|
-
|
27
|
-
rescue Exception => e
|
27
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
28
28
|
logger.info "dry run for testing: #{e}"
|
29
29
|
@boot_time ||= Time.now.to_i # comment the code below for development mode
|
30
30
|
end
|
@@ -67,28 +67,18 @@ module Smash
|
|
67
67
|
instance_url # gets and sets @instance_url
|
68
68
|
end
|
69
69
|
|
70
|
-
#
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
def task_name(id = @instance_id)
|
75
|
-
# get @task_name
|
76
|
-
return @task_name unless @task_name.nil?
|
77
|
-
# set @task_name
|
78
|
-
# TODO: get all tasks instead of just the first
|
79
|
-
resp = ec2.describe_instances(instance_ids: [id].flatten).reservations.first
|
80
|
-
return @task_name = nil if resp.nil?
|
81
|
-
@task_name = resp.instances[0].tags.select do |t|
|
82
|
-
t.value if t.key == 'taskType'
|
83
|
-
end.first
|
70
|
+
# Assures there is always a valid instance id because many other Aws calls require it
|
71
|
+
# === @returns: String
|
72
|
+
def instance_id
|
73
|
+
@instance_id ||= metadata_request('instance_id')
|
84
74
|
end
|
85
75
|
|
86
76
|
# Gets and sets the public hostname of the instance
|
87
77
|
def instance_url
|
88
78
|
@instance_url ||= if zfind('TESTING')
|
89
|
-
'
|
79
|
+
'abc-1234'
|
90
80
|
else
|
91
|
-
hostname_uri = 'http://169.254.169.254/latest/meta-data/
|
81
|
+
hostname_uri = 'http://169.254.169.254/latest/meta-data/instance-id'
|
92
82
|
HTTParty.get(hostname_uri).parsed_response
|
93
83
|
end
|
94
84
|
end
|
@@ -96,28 +86,35 @@ module Smash
|
|
96
86
|
# Makes the http request to self/meta-data to get all the metadata keys or,
|
97
87
|
# if a key is given, the method makes the http request to get that
|
98
88
|
# particular key from the metadata
|
99
|
-
# @param:
|
89
|
+
# === @param: key String (optional)
|
90
|
+
# === @returns:
|
100
91
|
def metadata_request(key = '')
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@z ||= ['i-9254d106', 'ami-id', 'ami-launch-index', 'ami-manifest-path', 'network/thing']
|
107
|
-
if key == ''
|
108
|
-
@boogs = ['instance-id', 'ami-id', 'ami-launch-index', 'ami-manifest-path', 'network/interfaces/macs/mac/device-number']
|
92
|
+
key = to_hyph(key)
|
93
|
+
begin
|
94
|
+
unless zfind('TESTING')
|
95
|
+
metadata_uri = "http://169.254.169.254/latest/meta-data/#{key}"
|
96
|
+
HTTParty.get(metadata_uri).parsed_response.split("\n")
|
109
97
|
else
|
110
|
-
|
98
|
+
require_relative '../../spec/stubs/aws_stubs'
|
99
|
+
stubbed_metadata = Smash::CloudPowers::AwsStubs::INSTANCE_METADATA_STUB
|
100
|
+
|
101
|
+
key.empty? ? stubbed_metadata.keys : stubbed_metadata[to_hyph(key)]
|
111
102
|
end
|
103
|
+
rescue Exception => e
|
104
|
+
logger.fatal format_error_message e
|
112
105
|
end
|
113
106
|
end
|
114
107
|
|
108
|
+
# Return the time since boot_time
|
109
|
+
# === @returns: Integer
|
110
|
+
# Notes:
|
111
|
+
# * TODO: refactor to use valid time stamps for better tracking.
|
112
|
+
# * reason -> separate regions or OSs etc.
|
115
113
|
def run_time
|
116
|
-
# TODO: refactor to use valid time stamps for better tracking.
|
117
|
-
# reason -> separate regions or OSs etc.
|
118
114
|
Time.now.to_i - boot_time
|
119
115
|
end
|
120
116
|
|
117
|
+
# Send a message on a Pipe at an interval
|
121
118
|
def send_frequent_status_updates(opts = {})
|
122
119
|
sleep_time = opts.delete(:interval) || 10
|
123
120
|
stream = opts.delete(:stream_name)
|
@@ -129,6 +126,10 @@ module Smash
|
|
129
126
|
end
|
130
127
|
end
|
131
128
|
|
129
|
+
# Get the instance status.
|
130
|
+
# === @params: id String (optional)
|
131
|
+
# * if no id is given, self-instance ID is returned
|
132
|
+
# === @returns: String
|
132
133
|
def status(id = @instance_id)
|
133
134
|
begin
|
134
135
|
ec2.describe_instances(dry_run: zfind('TESTING'), instance_ids: [id]).
|
@@ -139,6 +140,22 @@ module Smash
|
|
139
140
|
end
|
140
141
|
end
|
141
142
|
|
143
|
+
# Check self-tags for 'task' and act as an attr_accessor.
|
144
|
+
# A different node's tag's can be checked for a task by passing
|
145
|
+
# the id param
|
146
|
+
# see also: SelfAwareness#task_names
|
147
|
+
def task_name(id = @instance_id)
|
148
|
+
# get @task_name
|
149
|
+
return @task_name unless @task_name.nil?
|
150
|
+
# set @task_name
|
151
|
+
# TODO: get all tasks instead of just the first
|
152
|
+
resp = ec2.describe_instances(instance_ids: [id].flatten).reservations.first
|
153
|
+
return @task_name = nil if resp.nil?
|
154
|
+
@task_name = resp.instances[0].tags.select do |t|
|
155
|
+
t.value if t.key == 'taskType'
|
156
|
+
end.first
|
157
|
+
end
|
158
|
+
|
142
159
|
# This method will return true if:
|
143
160
|
# * The run time is more than 5 minutes
|
144
161
|
# and
|
@@ -66,8 +66,13 @@ module Smash
|
|
66
66
|
# Creates an actual Queue in SQS using the standard format for a queue name (camel case)
|
67
67
|
# @returns: Queue::Board
|
68
68
|
def create_queue!
|
69
|
-
|
70
|
-
|
69
|
+
begin
|
70
|
+
sqs.create_queue(queue_name: to_camel(@name))
|
71
|
+
self
|
72
|
+
rescue Aws::SQS::Errors::QueueDeletedRecently => e
|
73
|
+
sleep 5
|
74
|
+
retry
|
75
|
+
end
|
71
76
|
end
|
72
77
|
|
73
78
|
# Deletes an actual Queue from SQS
|
data/lib/cloud_powers/version.rb
CHANGED
data/lib/cloud_powers.rb
CHANGED
@@ -3,6 +3,7 @@ require 'cloud_powers/aws_resources'
|
|
3
3
|
require 'cloud_powers/context'
|
4
4
|
require 'cloud_powers/delegator'
|
5
5
|
require 'cloud_powers/helper'
|
6
|
+
require 'cloud_powers/node'
|
6
7
|
require 'cloud_powers/self_awareness'
|
7
8
|
require 'cloud_powers/storage'
|
8
9
|
require 'cloud_powers/version'
|
@@ -17,5 +18,6 @@ module Smash
|
|
17
18
|
include Smash::CloudPowers::SelfAwareness
|
18
19
|
include Smash::CloudPowers::Storage
|
19
20
|
include Smash::CloudPowers::Synapse
|
21
|
+
include Smash::CloudPowers::Node
|
20
22
|
end
|
21
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloud_powers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Phillipps
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport-core-ext
|
@@ -154,11 +154,12 @@ description: |2
|
|
154
154
|
CloudPowers is a wrapper around AWS and in the future, other cloud service Providers.
|
155
155
|
It was developed specifically for the Brain project but hopefully can be used
|
156
156
|
in any other ruby project that needs to use cloud service providers' resources.
|
157
|
-
Version 0.2.
|
158
|
-
|
157
|
+
Version 0.2.5 has a little EC2, S3, SQS, SNS and Kinesis and some a few other
|
158
|
+
features you can find in the docs.
|
159
|
+
The next versions will have websockets, IoT, more Kinesis and Workflow integration.
|
159
160
|
This project is actively being developed, so more additions, specs and docs
|
160
|
-
will be updated frequently with new funcionality but the gem will
|
161
|
-
practices for versioning. I always welcome input.
|
161
|
+
will be added and updated frequently with new funcionality but the gem will
|
162
|
+
follow good practices for versioning. I always welcome input.
|
162
163
|
Enjoy!
|
163
164
|
email: adam.phillipps@gmail.com
|
164
165
|
executables: []
|
@@ -179,12 +180,12 @@ files:
|
|
179
180
|
- bin/setup
|
180
181
|
- cloud_powers.gemspec
|
181
182
|
- lib/cloud_powers.rb
|
182
|
-
- lib/cloud_powers/.DS_Store
|
183
183
|
- lib/cloud_powers/auth.rb
|
184
184
|
- lib/cloud_powers/aws_resources.rb
|
185
185
|
- lib/cloud_powers/context.rb
|
186
186
|
- lib/cloud_powers/delegator.rb
|
187
187
|
- lib/cloud_powers/helper.rb
|
188
|
+
- lib/cloud_powers/node.rb
|
188
189
|
- lib/cloud_powers/self_awareness.rb
|
189
190
|
- lib/cloud_powers/smash_error.rb
|
190
191
|
- lib/cloud_powers/storage.rb
|
data/lib/cloud_powers/.DS_Store
DELETED
Binary file
|