podnix 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.
@@ -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