podnix 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ module Podnix
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
@@ -0,0 +1,18 @@
1
+ module Podnix
2
+ class API
3
+
4
+
5
+ def get_images
6
+ @options = {:path => '/images/list',:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+
16
+
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Podnix
2
+ class API
3
+
4
+ # GET /servers
5
+ def get_models
6
+ @options = {:path => '/models/list',:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+
16
+
17
+ end
18
+ end
@@ -0,0 +1,74 @@
1
+ module Podnix
2
+ class API
3
+
4
+ # GET /servers
5
+ def get_servers
6
+ @options = {:path => '/servers/list',:body => ""}.merge(@options)
7
+
8
+ request(
9
+ :expects => 200,
10
+ :method => :get,
11
+ :body => @options[:body]
12
+ )
13
+ end
14
+
15
+ def get_server(query)
16
+ @options = {:path => "/servers/list",:body => ""}.merge(@options)
17
+ @options[:query]=@options[:query].merge(query)
18
+ request(
19
+ :expects => 200,
20
+ :method => :get,
21
+ :body => @options[:body]
22
+ )
23
+ end
24
+
25
+ def add_server(new_server)
26
+ #https://api.podnix.com/servers/add?&name=test&model=1&image=2&password=Secret123&ssd=1&storage=10&key=123-znsbKicQwKl4tZHQOXo3Olwls8BOrR3O
27
+
28
+ @options = { :path => "/servers/add",:body => ""}.merge(@options)
29
+ @options[:query] = @options[:query].merge(new_server)
30
+ request(
31
+ :expects => 201,
32
+ :method => :post,
33
+ :body => @options[:body]
34
+ )
35
+ end
36
+
37
+ def start_server(server_id)
38
+ @options = {:path => "/servers/start",
39
+ :body => ""}.merge(@options)
40
+ @options[:query]=@options[:query].merge(server_id)
41
+ request(
42
+ :expects => 200,
43
+ :method => :post,
44
+ :body => @options[:body]
45
+ )
46
+ end
47
+
48
+
49
+ #Yet to be tested
50
+ # DELETE /nodes/:node_id
51
+ def delete_server(server_id)
52
+ @options = {:path => "/servers/delete",
53
+ :body => ""}.merge(@options)
54
+ @options[:query]=@options[:query].merge(server_id)
55
+ request(
56
+ :expects => 200,
57
+ :method => :delete,
58
+ :body => @options[:body]
59
+ )
60
+ end
61
+
62
+ def stop_server(server_id)
63
+ @options = {:path => "/servers/stop?id=#{server_id}",
64
+ :body => ""}.merge(@options)
65
+ @options[:query]=@options[:query].merge(server_id)
66
+ request(
67
+ :expects => 200,
68
+ :method => :post,
69
+ :body => @options[:body]
70
+ )
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module Podnix
2
+ class API
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
data/lib/podnix/api.rb ADDED
@@ -0,0 +1,208 @@
1
+ require "base64"
2
+ require "time"
3
+ require "excon"
4
+ require "uri"
5
+ require "zlib"
6
+ require 'openssl'
7
+ require 'multi_json'
8
+
9
+ # open it up when needed. This will be needed when a new customer onboarded via pug.
10
+ require "securerandom"
11
+
12
+ __LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
13
+ unless $LOAD_PATH.include?(__LIB_DIR__)
14
+ $LOAD_PATH.unshift(__LIB_DIR__)
15
+ end
16
+
17
+ require "podnix/api/errors"
18
+ require "podnix/api/version"
19
+ require "podnix/api/images"
20
+ require "podnix/api/models"
21
+ require "podnix/api/servers"
22
+ require "podnix/core/stuff"
23
+ require "podnix/core/text"
24
+ require "podnix/core/json_compat"
25
+ require "podnix/core/error"
26
+ require "podnix/core/images"
27
+ require "podnix/core/images_collection"
28
+ require "podnix/core/server"
29
+ require "podnix/core/server_collection"
30
+
31
+ #we may nuke logs out of the api
32
+ #require "megam/api/logs"
33
+
34
+ # Do you need a random seed now ?
35
+ #srand
36
+
37
+ module Podnix
38
+ class API
39
+
40
+ #text is used to print stuff in the terminal (message, log, info, warn etc.)
41
+ attr_accessor :text
42
+
43
+ HEADERS = {
44
+ 'Accept' => 'application/json',
45
+ 'Accept-Encoding' => 'gzip',
46
+ 'User-Agent' => "podnix/#{Podnix::API::VERSION}",
47
+ 'X-Ruby-Version' => RUBY_VERSION,
48
+ 'X-Ruby-Platform' => RUBY_PLATFORM
49
+ }
50
+
51
+ QUERY = {
52
+ :key => ENV['PODNIX_API_KEY']
53
+ }
54
+
55
+ OPTIONS = {
56
+ :headers => {},
57
+ :query => {},
58
+ :host => 'api.podnix.com',
59
+ :nonblock => false,
60
+ :scheme => 'https'
61
+ }
62
+
63
+ API_VERSION1 = ""
64
+
65
+ def text
66
+ @text ||= Podnix::Text.new(STDOUT, STDERR, STDIN, {})
67
+ end
68
+
69
+ def last_response
70
+ @last_response
71
+ end
72
+
73
+ # It is assumed that every API call will use an API_KEY/email. This ensures validity of the person
74
+ # really the same guy on who he claims.
75
+ # 3 levels of options exits
76
+ # 1. The global OPTIONS as available inside the API (OPTIONS)
77
+ # 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
78
+ # be merged into a class variable @options
79
+ # 3. Upon merge of the options, the api_key, email as available in the @options is deleted.
80
+ def initialize(options={})
81
+ @options = OPTIONS.merge(options)
82
+ @api_key = @options.delete(:api_key) || ENV['PODNIX_API_KEY']
83
+ #@email = @options.delete(:email)
84
+ raise ArgumentError, "You must specify [:email, :api_key]" if @api_key.nil?
85
+ end
86
+
87
+ def request(params,&block)
88
+ start = Time.now
89
+ text.msg "#{text.color("START", :cyan, :bold)}"
90
+ params.each do |pkey, pvalue|
91
+ text.msg("> #{pkey}: #{pvalue}")
92
+ end
93
+
94
+ begin
95
+ response = connection.request(params, &block)
96
+ rescue Excon::Errors::HTTPStatusError => error
97
+ klass = case error.response.status
98
+
99
+ when 401 then Podnix::API::Errors::Unauthorized
100
+ when 403 then Podnix::API::Errors::Forbidden
101
+ when 404 then Podnix::API::Errors::NotFound
102
+ when 408 then Podnix::API::Errors::Timeout
103
+ when 422 then Podnix::API::Errors::RequestFailed
104
+ when 423 then Podnix::API::Errors::Locked
105
+ when /50./ then Podnix::API::Errors::RequestFailed
106
+ else Podnix::API::Errors::ErrorWithResponse
107
+ end
108
+ reerror = klass.new(error.message, error.response)
109
+ reerror.set_backtrace(error.backtrace)
110
+ text.msg "#{text.color("#{reerror.response.body}", :white)}"
111
+
112
+ begin
113
+ response.body = MultiJson.load(reerror.response.body.chomp)
114
+ rescue
115
+ end
116
+
117
+ text.msg("#{text.color("RESPONSE ERR: Ruby Object", :magenta, :bold)}")
118
+ text.msg "#{text.color("#{reerror.response.body}", :white, :bold)}"
119
+ raise(reerror)
120
+ end
121
+
122
+ @last_response = response
123
+ text.msg("#{text.color("RESPONSE: HTTP Status and Header Data", :magenta, :bold)}")
124
+ text.msg("> HTTP #{response.remote_ip} #{response.status}")
125
+
126
+ response.headers.each do |header, value|
127
+ text.msg("> #{header}: #{value}")
128
+ end
129
+ text.info("End HTTP Status/Header Data.")
130
+
131
+ if response.body && !response.body.empty?
132
+ if response.headers['Content-Encoding'] == 'gzip'
133
+ response.body = Zlib::GzipReader.new(StringIO.new(response.body)).read
134
+ end
135
+ text.msg("#{text.color("RESPONSE: HTTP Body(JSON)", :magenta, :bold)}")
136
+ text.msg "#{text.color("#{response.body}", :white)}"
137
+
138
+ begin
139
+ begin
140
+ response.body = MultiJson.load(response.body.chomp)
141
+ rescue
142
+ end
143
+ text.msg("#{text.color("RESPONSE: Ruby Object", :magenta, :bold)}")
144
+
145
+ text.msg "#{text.color("#{response.body}", :white, :bold)}"
146
+ rescue Exception => jsonerr
147
+ text.error(jsonerr)
148
+ raise(jsonerr)
149
+ # exception = Podnix::JSONCompat.from_json(response_body)
150
+ # msg = "HTTP Request Returned #{response.code} #{response.message}: "
151
+ # msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
152
+ # text.error(msg)
153
+ end
154
+ end
155
+ text.msg "#{text.color("END(#{(Time.now - start).to_s}s)", :blue, :bold)}"
156
+ # reset (non-persistent) connection
157
+ @connection.reset
158
+ response
159
+ end
160
+
161
+ private
162
+
163
+ #Make a lazy connection.
164
+ def connection
165
+
166
+ #@options[:path]=@options[:path]+'&key='+"#{ENV['PODNIX_API_KEY']}"
167
+ @options[:query] = QUERY.merge(@options[:query])
168
+ @options[:headers] = HEADERS.merge(@options[:headers])
169
+
170
+ #SSL certificate file paths
171
+ #If ssl_ca_path and file specified shows error
172
+ #Only file pass through
173
+ #Excon.defaults[:ssl_ca_path] = "/etc/ssl/certs"
174
+ #ENV['SSL_CERT_DIR'] = "/etc/ssl/certs"
175
+ Excon.defaults[:ssl_ca_file] = File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem"))
176
+ #ENV['SSL_CERT_FILE'] = File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem"))
177
+
178
+ if !File.exist?(File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "cacert.pem")))
179
+ text.warn("Certificate file does not exist. SSL_VERIFY_PEER set as false")
180
+ Excon.defaults[:ssl_verify_peer] = false
181
+ #elsif !File.readable_real?(File.expand_path(File.join(File.dirname(__FILE__), "..", "certs", "test.pem")))
182
+ # text.warn("Certificate file is readable. SSL_VERIFY_PEER set as false")
183
+ # Excon.defaults[:ssl_verify_peer] = false
184
+ else
185
+ text.info("Certificate found")
186
+ Excon.defaults[:ssl_verify_peer] = true
187
+ end
188
+
189
+ text.info("HTTP Request Data:")
190
+ text.msg("> HTTP #{@options[:scheme]}://#{@options[:host]}/#{@options[:query]}")
191
+
192
+ @options.each do |key, value|
193
+ text.msg("> #{key}: #{value}")
194
+ end
195
+ text.info("End HTTP Request Data.")
196
+ @connection = Excon.new("#{@options[:scheme]}://#{@options[:host]}",@options)
197
+ end
198
+
199
+ ## encode header as per rules.
200
+ # The input hash will have
201
+ # :api_key, :email, :body, :path
202
+ # The output will have
203
+ # :hmac
204
+ # :date
205
+ # (Refer https://Github.com/indykish/megamplay.git/test/AuthenticateSpec.scala)
206
+ end
207
+
208
+ end
@@ -0,0 +1,91 @@
1
+ # Copyright:: Copyright (c) 2012-2013 Megam Systems, Inc.
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ module Podnix
17
+ class Auth
18
+
19
+ def initialize
20
+ @some_msg = {}
21
+ end
22
+
23
+ #used by resque workers and any other background job
24
+ def auth
25
+ self
26
+ end
27
+
28
+ def podnix_rest
29
+ Podnix::API.new(Podnix::Config[:email], Podnix::Config[:api_key])
30
+ end
31
+
32
+ def some_msg(arg=nil)
33
+ if arg != nil
34
+ @some_msg = arg
35
+ else
36
+ @some_msg
37
+ end
38
+ end
39
+
40
+ def error?
41
+ crocked = true if (some_msg.has_key?(:msg_type) && some_msg[:msg_type] == "error")
42
+ end
43
+
44
+ # Transform the ruby obj -> to a Hash
45
+ def to_hash
46
+ index_hash = Hash.new
47
+ index_hash["json_claz"] = self.class.name
48
+ index_hash["some_msg"] = some_msg
49
+ index_hash
50
+ end
51
+
52
+ # Serialize this object as a hash: called from JsonCompat.
53
+ # Verify if this called from JsonCompat during testing.
54
+ def to_json(*a)
55
+ for_json.to_json(*a)
56
+ end
57
+
58
+ def for_json
59
+ result = { }
60
+ result
61
+ end
62
+
63
+ # Create a Podnix::Account from JSON (used by the backgroud job workers)
64
+ def self.json_create(o)
65
+ acct = new
66
+ acct.some_msg[:code] = o["code"] if o.has_key?("code")
67
+ acct.some_msg[:msg_type] = o["msg_type"] if o.has_key?("msg_type")
68
+ acct.some_msg[:msg]= o["msg"] if o.has_key?("msg")
69
+ acct.some_msg[:links] = o["links"] if o.has_key?("links")
70
+ acct
71
+ end
72
+
73
+ def self.from_hash(o)
74
+ acct = self.new()
75
+ acct.from_hash(o)
76
+ acct
77
+ end
78
+
79
+ # just an auth
80
+ def self.auth
81
+ podnix_rest.post_auth
82
+ self
83
+ end
84
+
85
+
86
+ def to_s
87
+ Podnix::Stuff.styled_hash(to_hash)
88
+ #"---> Megam::Account:[error=#{error?}]\n"+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012-2013 Megam Systems.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'mixlib/config'
18
+
19
+ module Podnix
20
+ class Config
21
+
22
+ extend Mixlib::Config
23
+
24
+ def self.inspect
25
+ configuration.inspect
26
+ end
27
+
28
+ podnix_api_server_url "http://localhost:9000"
29
+
30
+ podnix_api_version "v1"
31
+
32
+ email nil
33
+ api_key nil
34
+
35
+ # Set these to enable SSL authentication / mutual-authentication
36
+ # with the server
37
+ ssl_client_cert nil
38
+ ssl_client_key nil
39
+ ssl_verify_mode :verify_none
40
+ ssl_ca_path nil
41
+ ssl_ca_file nil
42
+
43
+ end
44
+ end
@@ -0,0 +1,97 @@
1
+ # Copyright:: Copyright (c) 2012-2013 Megam Systems, Inc.
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ module Podnix
17
+ class Error
18
+
19
+ def initialize
20
+ @some_msg = {}
21
+ end
22
+
23
+ #used by resque workers and any other background job
24
+ def auth
25
+ self
26
+ end
27
+
28
+ def podnix_rest
29
+ Podnix::API.new(Podnix::Config[:email], Podnix::Config[:api_key])
30
+ end
31
+
32
+ def some_msg(arg=nil)
33
+ if arg != nil
34
+ @some_msg = arg
35
+ else
36
+ @some_msg
37
+ end
38
+ end
39
+
40
+ def error?
41
+ crocked = true if (some_msg.has_key?(:msg_type) && some_msg[:msg_type] == "error")
42
+ end
43
+
44
+ # Transform the ruby obj -> to a Hash
45
+ def to_hash
46
+ index_hash = Hash.new
47
+ index_hash["json_claz"] = self.class.name
48
+ index_hash["some_msg"] = some_msg
49
+ index_hash
50
+ end
51
+
52
+ # Serialize this object as a hash: called from JsonCompat.
53
+ # Verify if this called from JsonCompat during testing.
54
+ def to_json(*a)
55
+ for_json.to_json(*a)
56
+ end
57
+
58
+ def for_json
59
+ result = { }
60
+ result
61
+ end
62
+
63
+ # Create a Megam::Account from JSON (used by the backgroud job workers)
64
+ def self.json_create(o)
65
+ acct = new
66
+ acct.some_msg[:code] = o["code"] if o.has_key?("code")
67
+ acct.some_msg[:msg_type] = o["msg_type"] if o.has_key?("msg_type")
68
+ acct.some_msg[:msg]= o["msg"] if o.has_key?("msg")
69
+ acct.some_msg[:links] = o["links"] if o.has_key?("links")
70
+ acct
71
+ end
72
+
73
+ def self.from_hash(o)
74
+ acct = self.new()
75
+ acct.from_hash(o)
76
+ acct
77
+ end
78
+
79
+ def from_hash(o)
80
+ @some_msg[:code] = o["code"] if o.has_key?("code")
81
+ @some_msg[:msg_type] = o["msg_type"] if o.has_key?("msg_type")
82
+ @some_msg[:msg]= o["msg"] if o.has_key?("msg")
83
+ @some_msg[:links] = o["links"] if o.has_key?("links")
84
+ self
85
+ end
86
+
87
+ # just an auth
88
+ def self.auth
89
+ podnix_rest.post_auth
90
+ self
91
+ end
92
+
93
+
94
+ def to_s
95
+ Podnix::Stuff.styled_hash(to_hash) end
96
+ end
97
+ end