megam_api 0.1.0

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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.project +17 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +5 -0
  6. data/README.md +83 -0
  7. data/Rakefile +10 -0
  8. data/lib/certs/cacert.pem +3554 -0
  9. data/lib/megam/api.rb +244 -0
  10. data/lib/megam/api/accounts.rb +29 -0
  11. data/lib/megam/api/appdefns.rb +26 -0
  12. data/lib/megam/api/appreqs.rb +27 -0
  13. data/lib/megam/api/boltdefns.rb +27 -0
  14. data/lib/megam/api/boltreqs.rb +27 -0
  15. data/lib/megam/api/cloud_tools.rb +35 -0
  16. data/lib/megam/api/errors.rb +27 -0
  17. data/lib/megam/api/login.rb +14 -0
  18. data/lib/megam/api/logs.rb +18 -0
  19. data/lib/megam/api/nodes.rb +50 -0
  20. data/lib/megam/api/predef_clouds.rb +35 -0
  21. data/lib/megam/api/predefs.rb +35 -0
  22. data/lib/megam/api/requests.rb +37 -0
  23. data/lib/megam/api/version.rb +5 -0
  24. data/lib/megam/core/account.rb +170 -0
  25. data/lib/megam/core/appdefns.rb +192 -0
  26. data/lib/megam/core/appdefns_collection.rb +148 -0
  27. data/lib/megam/core/appreqs.rb +224 -0
  28. data/lib/megam/core/appreqs_collection.rb +148 -0
  29. data/lib/megam/core/auth.rb +91 -0
  30. data/lib/megam/core/boltdefns.rb +198 -0
  31. data/lib/megam/core/boltdefns_collection.rb +148 -0
  32. data/lib/megam/core/boltreqs.rb +224 -0
  33. data/lib/megam/core/boltreqs_collection.rb +148 -0
  34. data/lib/megam/core/cloudinstruction.rb +110 -0
  35. data/lib/megam/core/cloudinstruction_collection.rb +145 -0
  36. data/lib/megam/core/cloudinstruction_group.rb +99 -0
  37. data/lib/megam/core/cloudtemplate.rb +127 -0
  38. data/lib/megam/core/cloudtemplate_collection.rb +145 -0
  39. data/lib/megam/core/cloudtool.rb +153 -0
  40. data/lib/megam/core/cloudtool_collection.rb +145 -0
  41. data/lib/megam/core/config.rb +44 -0
  42. data/lib/megam/core/error.rb +99 -0
  43. data/lib/megam/core/json_compat.rb +183 -0
  44. data/lib/megam/core/log.rb +33 -0
  45. data/lib/megam/core/node.rb +347 -0
  46. data/lib/megam/core/node_collection.rb +166 -0
  47. data/lib/megam/core/predef.rb +208 -0
  48. data/lib/megam/core/predef_collection.rb +164 -0
  49. data/lib/megam/core/predefcloud.rb +229 -0
  50. data/lib/megam/core/predefcloud_collection.rb +168 -0
  51. data/lib/megam/core/request.rb +187 -0
  52. data/lib/megam/core/request_collection.rb +145 -0
  53. data/lib/megam/core/stuff.rb +69 -0
  54. data/lib/megam/core/text.rb +88 -0
  55. data/lib/megam_api.rb +1 -0
  56. data/megam_api.gemspec +26 -0
  57. data/test/test_accounts.rb +46 -0
  58. data/test/test_appdefns.rb +23 -0
  59. data/test/test_appreqs.rb +28 -0
  60. data/test/test_boltdefns.rb +23 -0
  61. data/test/test_boltreqs.rb +28 -0
  62. data/test/test_cloudtools.rb +22 -0
  63. data/test/test_helper.rb +67 -0
  64. data/test/test_login.rb +12 -0
  65. data/test/test_logs.rb +15 -0
  66. data/test/test_nodes.rb +141 -0
  67. data/test/test_predefclouds.rb +67 -0
  68. data/test/test_predefs.rb +72 -0
  69. data/test/test_requests.rb +85 -0
  70. metadata +213 -0
@@ -0,0 +1,244 @@
1
+ require "base64"
2
+ require "time"
3
+ require "excon"
4
+ require "uri"
5
+ require "zlib"
6
+ require 'openssl'
7
+
8
+ # open it up when needed. This will be needed when a new customer onboarded via pug.
9
+ require "securerandom"
10
+
11
+ __LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
12
+ unless $LOAD_PATH.include?(__LIB_DIR__)
13
+ $LOAD_PATH.unshift(__LIB_DIR__)
14
+ end
15
+
16
+ require "megam/api/errors"
17
+ require "megam/api/version"
18
+ require "megam/api/login"
19
+ require "megam/api/accounts"
20
+ require "megam/api/nodes"
21
+ require "megam/api/appdefns"
22
+ require "megam/api/appreqs"
23
+ require "megam/api/boltreqs"
24
+ require "megam/api/boltdefns"
25
+ require "megam/api/requests"
26
+ require "megam/api/predefs"
27
+ require "megam/api/predef_clouds"
28
+ require "megam/api/cloud_tools"
29
+ require "megam/core/config"
30
+ require "megam/core/stuff"
31
+ require "megam/core/text"
32
+ require "megam/core/json_compat"
33
+ require "megam/core/auth"
34
+ require "megam/core/error"
35
+ require "megam/core/account"
36
+ require "megam/core/node"
37
+ require "megam/core/appdefns"
38
+ require "megam/core/appreqs"
39
+ require "megam/core/boltreqs"
40
+ require "megam/core/boltdefns"
41
+ require "megam/core/node_collection"
42
+ require "megam/core/appdefns_collection"
43
+ require "megam/core/appreqs_collection"
44
+ require "megam/core/boltreqs_collection"
45
+ require "megam/core/boltdefns_collection"
46
+ require "megam/core/request"
47
+ require "megam/core/request_collection"
48
+ require "megam/core/predef"
49
+ require "megam/core/predef_collection"
50
+ require "megam/core/predefcloud"
51
+ require "megam/core/predefcloud_collection"
52
+ require "megam/core/cloudtool"
53
+ require "megam/core/cloudtool_collection"
54
+ require "megam/core/cloudtemplate"
55
+ require "megam/core/cloudtemplate_collection"
56
+ require "megam/core/cloudinstruction_group"
57
+ require "megam/core/cloudinstruction_collection"
58
+ require "megam/core/cloudinstruction"
59
+
60
+ #we may nuke logs out of the api
61
+ #require "megam/api/logs"
62
+
63
+ # Do you need a random seed now ?
64
+ #srand
65
+
66
+ module Megam
67
+ class API
68
+
69
+ #text is used to print stuff in the terminal (message, log, info, warn etc.)
70
+ attr_accessor :text
71
+
72
+ HEADERS = {
73
+ 'Accept' => 'application/json',
74
+ 'Accept-Encoding' => 'gzip',
75
+ 'User-Agent' => "megam-api/#{Megam::API::VERSION}",
76
+ 'X-Ruby-Version' => RUBY_VERSION,
77
+ 'X-Ruby-Platform' => RUBY_PLATFORM
78
+ }
79
+
80
+ OPTIONS = {
81
+ :headers => {},
82
+ :host => 'api.megam.co',
83
+ :nonblock => false,
84
+ :scheme => 'https'
85
+ }
86
+
87
+ API_VERSION1 = "/v1"
88
+
89
+ def text
90
+ @text ||= Megam::Text.new(STDOUT, STDERR, STDIN, {})
91
+ end
92
+
93
+ def last_response
94
+ @last_response
95
+ end
96
+
97
+ # It is assumed that every API call will use an API_KEY/email. This ensures validity of the person
98
+ # really the same guy on who he claims.
99
+ # 3 levels of options exits
100
+ # 1. The global OPTIONS as available inside the API (OPTIONS)
101
+ # 2. The options as passed via the instantiation of API will override global options. The ones that are passed are :email and :api_key and will
102
+ # be merged into a class variable @options
103
+ # 3. Upon merge of the options, the api_key, email as available in the @options is deleted.
104
+ def initialize(options={})
105
+ @options = OPTIONS.merge(options)
106
+ @api_key = @options.delete(:api_key) || ENV['MEGAM_API_KEY']
107
+ @email = @options.delete(:email)
108
+ raise ArgumentError, "You must specify [:email, :api_key]" if @email.nil? || @api_key.nil?
109
+ end
110
+
111
+ def request(params,&block)
112
+ start = Time.now
113
+ text.msg "#{text.color("START", :cyan, :bold)}"
114
+ params.each do |pkey, pvalue|
115
+ text.msg("> #{pkey}: #{pvalue}")
116
+ end
117
+
118
+ begin
119
+ response = connection.request(params, &block)
120
+ rescue Excon::Errors::HTTPStatusError => error
121
+ klass = case error.response.status
122
+
123
+ when 401 then Megam::API::Errors::Unauthorized
124
+ when 403 then Megam::API::Errors::Forbidden
125
+ when 404 then Megam::API::Errors::NotFound
126
+ when 408 then Megam::API::Errors::Timeout
127
+ when 422 then Megam::API::Errors::RequestFailed
128
+ when 423 then Megam::API::Errors::Locked
129
+ when /50./ then Megam::API::Errors::RequestFailed
130
+ else Megam::API::Errors::ErrorWithResponse
131
+ end
132
+ reerror = klass.new(error.message, error.response)
133
+ reerror.set_backtrace(error.backtrace)
134
+ text.msg "#{text.color("#{reerror.response.body}", :white)}"
135
+ reerror.response.body = Megam::JSONCompat.from_json(reerror.response.body.chomp)
136
+ text.msg("#{text.color("RESPONSE ERR: Ruby Object", :magenta, :bold)}")
137
+ text.msg "#{text.color("#{reerror.response.body}", :white, :bold)}"
138
+ raise(reerror)
139
+ end
140
+
141
+ @last_response = response
142
+ text.msg("#{text.color("RESPONSE: HTTP Status and Header Data", :magenta, :bold)}")
143
+ text.msg("> HTTP #{response.remote_ip} #{response.status}")
144
+
145
+ response.headers.each do |header, value|
146
+ text.msg("> #{header}: #{value}")
147
+ end
148
+ text.info("End HTTP Status/Header Data.")
149
+
150
+ if response.body && !response.body.empty?
151
+ if response.headers['Content-Encoding'] == 'gzip'
152
+ response.body = Zlib::GzipReader.new(StringIO.new(response.body)).read
153
+ end
154
+ text.msg("#{text.color("RESPONSE: HTTP Body(JSON)", :magenta, :bold)}")
155
+
156
+ text.msg "#{text.color("#{response.body}", :white)}"
157
+
158
+ begin
159
+ response.body = Megam::JSONCompat.from_json(response.body.chomp)
160
+ text.msg("#{text.color("RESPONSE: Ruby Object", :magenta, :bold)}")
161
+
162
+ text.msg "#{text.color("#{response.body}", :white, :bold)}"
163
+ rescue Exception => jsonerr
164
+ text.error(jsonerr)
165
+ raise(jsonerr)
166
+ # exception = Megam::JSONCompat.from_json(response_body)
167
+ # msg = "HTTP Request Returned #{response.code} #{response.message}: "
168
+ # msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
169
+ # text.error(msg)
170
+ end
171
+ end
172
+ text.msg "#{text.color("END(#{(Time.now - start).to_s}s)", :blue, :bold)}"
173
+ # text.msg "#{text.color("END(#{(Megam::Stuff.time_ago(start))})", :blue, :bold)}"
174
+
175
+ # reset (non-persistent) connection
176
+ @connection.reset
177
+ response
178
+ end
179
+
180
+ private
181
+
182
+ #Make a lazy connection.
183
+ def connection
184
+ @options[:path] =API_VERSION1+ @options[:path]
185
+ encoded_api_header = encode_header(@options)
186
+ @options[:headers] = HEADERS.merge({
187
+ 'X-Megam-HMAC' => encoded_api_header[:hmac],
188
+ 'X-Megam-Date' => encoded_api_header[:date],
189
+ }).merge(@options[:headers])
190
+
191
+ #SSL certificate file paths
192
+ #If ssl_ca_path and file specified shows error
193
+ #Only file pass through
194
+ #Excon.defaults[:ssl_ca_path] = "/etc/ssl/certs"
195
+ #ENV['SSL_CERT_DIR'] = "/etc/ssl/certs"
196
+ Excon.defaults[:ssl_ca_file] = File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem"))
197
+ #ENV['SSL_CERT_FILE'] = File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem"))
198
+
199
+ if !File.exist?(File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem")))
200
+ text.warn("Certificate file does not exist. SSL_VERIFY_PEER set as false")
201
+ Excon.defaults[:ssl_verify_peer] = false
202
+ #elsif !File.readable_real?(File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "test.pem")))
203
+ # puts "==================> Test CER 2===============>"
204
+ # text.warn("Certificate file is readable. SSL_VERIFY_PEER set as false")
205
+ # Excon.defaults[:ssl_verify_peer] = false
206
+ else
207
+ text.info("Certificate found")
208
+ Excon.defaults[:ssl_verify_peer] = true
209
+ end
210
+
211
+ text.info("HTTP Request Data:")
212
+ text.msg("> HTTP #{@options[:scheme]}://#{@options[:host]}")
213
+ @options.each do |key, value|
214
+ text.msg("> #{key}: #{value}")
215
+ end
216
+ text.info("End HTTP Request Data.")
217
+ @connection = Excon.new("#{@options[:scheme]}://#{@options[:host]}",@options)
218
+ end
219
+
220
+ ## encode header as per rules.
221
+ # The input hash will have
222
+ # :api_key, :email, :body, :path
223
+ # The output will have
224
+ # :hmac
225
+ # :date
226
+ # (Refer https://Github.com/indykish/megamplay.git/test/AuthenticateSpec.scala)
227
+ def encode_header(cmd_parms)
228
+ header_params ={}
229
+ body_digest = OpenSSL::Digest::MD5.digest(cmd_parms[:body])
230
+ body_base64 = Base64.encode64(body_digest)
231
+
232
+ current_date = Time.now.strftime("%Y-%m-%d %H:%M")
233
+
234
+ data="#{current_date}"+"\n"+"#{cmd_parms[:path]}"+"\n"+"#{body_base64}"
235
+
236
+ digest = OpenSSL::Digest::Digest.new('sha1')
237
+ movingFactor = data.rstrip!
238
+ hash = OpenSSL::HMAC.hexdigest(digest, @api_key, movingFactor)
239
+ final_hmac = @email+':' + hash
240
+ header_params = { :hmac => final_hmac, :date => current_date}
241
+ end
242
+ end
243
+
244
+ end
@@ -0,0 +1,29 @@
1
+ module Megam
2
+ class API
3
+ # GET /accounts
4
+ #Yet to be tested
5
+ def get_accounts(email)
6
+
7
+ @options = {:path => "/accounts/#{email}",
8
+ :body => ''}.merge(@options)
9
+
10
+ request(
11
+ :expects => 200,
12
+ :method => :get,
13
+ :body => @options[:body]
14
+ )
15
+ end
16
+
17
+ # The body content needs to be a json.
18
+ def post_accounts(new_account)
19
+ @options = {:path => '/accounts/content',
20
+ :body => Megam::JSONCompat.to_json(new_account)}.merge(@options)
21
+
22
+ request(
23
+ :expects => 201,
24
+ :method => :post,
25
+ :body => @options[:body]
26
+ )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ module Megam
2
+ class API
3
+
4
+ def get_appdefn(node_name)
5
+ @options = {:path => "/appdefns/#{node_name}",:body => ""}.merge(@options)
6
+
7
+ request(
8
+ :expects => 200,
9
+ :method => :get,
10
+ :body => @options[:body]
11
+ )
12
+ end
13
+
14
+ def post_appdefn(new_appdefn)
15
+ @options = {:path => '/appdefns/content',
16
+ :body => Megam::JSONCompat.to_json(new_appdefn)}.merge(@options)
17
+
18
+ request(
19
+ :expects => 201,
20
+ :method => :post,
21
+ :body => @options[:body]
22
+ )
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ module Megam
2
+ class API
3
+
4
+ #=begin
5
+ def get_appreq(node_name)
6
+ @options = {:path => "/appreqs/#{node_name}",:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+ #=end
15
+ def post_appreq(new_appreq)
16
+ @options = {:path => '/appreqs/content',
17
+ :body => Megam::JSONCompat.to_json(new_appreq)}.merge(@options)
18
+
19
+ request(
20
+ :expects => 201,
21
+ :method => :post,
22
+ :body => @options[:body]
23
+ )
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Megam
2
+ class API
3
+
4
+
5
+ def get_boltdefn(boltdefns_id)
6
+ @options = {:path => "/boltdefns/#{boltdefns_id}",:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+ def post_boltdefn(new_boltdefn)
16
+ @options = {:path => '/boltdefns/content',
17
+ :body => Megam::JSONCompat.to_json(new_boltdefn)}.merge(@options)
18
+
19
+ request(
20
+ :expects => 201,
21
+ :method => :post,
22
+ :body => @options[:body]
23
+ )
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Megam
2
+ class API
3
+
4
+
5
+ def get_boltreq(node_name)
6
+ @options = {:path => "/boltreqs/#{node_name}",:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+ def post_boltreq(new_boltreq)
16
+ @options = {:path => '/boltreqs/content',
17
+ :body => Megam::JSONCompat.to_json(new_boltreq)}.merge(@options)
18
+
19
+ request(
20
+ :expects => 201,
21
+ :method => :post,
22
+ :body => @options[:body]
23
+ )
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ module Megam
2
+ class API
3
+ def get_cloudtools
4
+ @options = {:path => '/cloudtools',:body => ""}.merge(@options)
5
+
6
+ request(
7
+ :expects => 200,
8
+ :method => :get,
9
+ :body => @options[:body]
10
+ )
11
+ end
12
+
13
+ def get_cloudtool(cloudtool_name)
14
+ @options = {:path => "/cloudtools/#{cloudtool_name}",:body => ""}.merge(@options)
15
+
16
+ request(
17
+ :expects => 200,
18
+ :method => :get,
19
+ :body => @options[:body]
20
+ )
21
+ end
22
+
23
+ def post_cloudtool(new_cloudtool)
24
+ @options = {:path => '/cloudtools/content',
25
+ :body => Megam::JSONCompat.to_json(new_cloudtool)}.merge(@options)
26
+
27
+ request(
28
+ :expects => 201,
29
+ :method => :post,
30
+ :body => @options[:body]
31
+ )
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Megam
2
+ class API
3
+ module Errors
4
+ class Error < StandardError; end
5
+
6
+ class ErrorWithResponse < Error
7
+ attr_reader :response
8
+
9
+ def initialize(message, response)
10
+ super message
11
+ @response = response
12
+ end
13
+
14
+
15
+ end
16
+
17
+ class Unauthorized < ErrorWithResponse; end
18
+ class Forbidden < ErrorWithResponse; end
19
+ class NotFound < ErrorWithResponse; end
20
+ class Timeout < ErrorWithResponse; end
21
+ class Locked < ErrorWithResponse; end
22
+ class Socket < ErrorWithResponse; end
23
+ class RequestFailed < ErrorWithResponse; end
24
+
25
+ end
26
+ end
27
+ end