rest_connection 0.0.23 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.rdoc +3 -3
  2. data/Rakefile +5 -4
  3. data/VERSION +1 -1
  4. data/git_hooks/post-commit.disabled +4 -0
  5. data/git_hooks/post-merge.disabled +4 -0
  6. data/git_hooks/pre-commit +53 -0
  7. data/lib/rest_connection.rb +75 -12
  8. data/lib/rest_connection/patches.rb +106 -0
  9. data/lib/rest_connection/rightscale/account.rb +27 -0
  10. data/lib/rest_connection/rightscale/alert_spec.rb +2 -2
  11. data/lib/rest_connection/rightscale/audit_entry.rb +5 -5
  12. data/lib/rest_connection/rightscale/child_account.rb +27 -0
  13. data/lib/rest_connection/rightscale/cloud.rb +26 -0
  14. data/lib/rest_connection/rightscale/credential.rb +1 -1
  15. data/lib/rest_connection/rightscale/deployment.rb +33 -7
  16. data/lib/rest_connection/rightscale/ec2_ebs_snapshot.rb +3 -1
  17. data/lib/rest_connection/rightscale/ec2_ebs_volume.rb +3 -1
  18. data/lib/rest_connection/rightscale/ec2_elastic_ip.rb +1 -1
  19. data/lib/rest_connection/rightscale/ec2_security_group.rb +2 -2
  20. data/lib/rest_connection/rightscale/ec2_server_array.rb +7 -5
  21. data/lib/rest_connection/rightscale/ec2_ssh_key.rb +11 -2
  22. data/lib/rest_connection/rightscale/ec2_ssh_key_internal.rb +2 -2
  23. data/lib/rest_connection/rightscale/executable.rb +4 -4
  24. data/lib/rest_connection/rightscale/instance.rb +5 -3
  25. data/lib/rest_connection/rightscale/instance_type.rb +31 -0
  26. data/lib/rest_connection/rightscale/macro.rb +19 -0
  27. data/lib/rest_connection/rightscale/mc_datacenter.rb +53 -0
  28. data/lib/rest_connection/rightscale/mc_deployment.rb +60 -0
  29. data/lib/rest_connection/rightscale/mc_image.rb +42 -0
  30. data/lib/rest_connection/rightscale/mc_instance.rb +170 -0
  31. data/lib/rest_connection/rightscale/mc_instance_type.rb +47 -0
  32. data/lib/rest_connection/rightscale/mc_multi_cloud_image.rb +58 -0
  33. data/lib/rest_connection/rightscale/mc_multi_cloud_image_setting.rb +46 -0
  34. data/lib/rest_connection/rightscale/mc_security_group.rb +42 -0
  35. data/lib/rest_connection/rightscale/mc_server.rb +250 -6
  36. data/lib/rest_connection/rightscale/mc_server_array.rb +44 -0
  37. data/lib/rest_connection/rightscale/mc_server_template.rb +53 -0
  38. data/lib/rest_connection/rightscale/mc_ssh_key.rb +50 -0
  39. data/lib/rest_connection/rightscale/mc_tag.rb +69 -0
  40. data/lib/rest_connection/rightscale/mc_volume.rb +53 -0
  41. data/lib/rest_connection/rightscale/mc_volume_attachment.rb +48 -0
  42. data/lib/rest_connection/rightscale/mc_volume_snapshot.rb +55 -0
  43. data/lib/rest_connection/rightscale/mc_volume_type.rb +47 -0
  44. data/lib/rest_connection/rightscale/monitoring_metric.rb +35 -0
  45. data/lib/rest_connection/rightscale/multi_cloud_image.rb +16 -2
  46. data/lib/rest_connection/rightscale/multi_cloud_image_cloud_setting_internal.rb +2 -2
  47. data/lib/rest_connection/rightscale/multi_cloud_image_internal.rb +25 -1
  48. data/lib/rest_connection/rightscale/permission.rb +27 -0
  49. data/lib/rest_connection/rightscale/right_script.rb +4 -4
  50. data/lib/rest_connection/rightscale/right_script_internal.rb +4 -4
  51. data/lib/rest_connection/rightscale/rightscale_api_base.rb +69 -20
  52. data/lib/rest_connection/rightscale/rightscale_api_gateway.rb +198 -13
  53. data/lib/rest_connection/rightscale/rightscale_api_internal.rb +3 -3
  54. data/lib/rest_connection/rightscale/rightscale_api_mc_input.rb +32 -0
  55. data/lib/rest_connection/rightscale/rightscale_api_mc_taggable.rb +57 -0
  56. data/lib/rest_connection/rightscale/rightscale_api_resources.rb +32 -2
  57. data/lib/rest_connection/rightscale/rightscale_api_taggable.rb +116 -0
  58. data/lib/rest_connection/rightscale/rs_internal.rb +6 -6
  59. data/lib/rest_connection/rightscale/s3_bucket.rb +36 -0
  60. data/lib/rest_connection/rightscale/server.rb +186 -37
  61. data/lib/rest_connection/rightscale/server_interface.rb +264 -0
  62. data/lib/rest_connection/rightscale/server_internal.rb +3 -3
  63. data/lib/rest_connection/rightscale/server_template.rb +15 -4
  64. data/lib/rest_connection/rightscale/server_template_internal.rb +4 -4
  65. data/lib/rest_connection/rightscale/status.rb +2 -2
  66. data/lib/rest_connection/rightscale/tag.rb +2 -2
  67. data/lib/rest_connection/rightscale/task.rb +47 -0
  68. data/lib/rest_connection/rightscale/user.rb +27 -0
  69. data/lib/rest_connection/ssh_hax.rb +42 -40
  70. data/rest_connection.gemspec +5 -5
  71. data/spec/mcserver_spec.rb +17 -0
  72. data/spec/multi.rb +16 -0
  73. metadata +65 -12
data/README.rdoc CHANGED
@@ -4,7 +4,7 @@
4
4
  "gem install rest_connection"
5
5
 
6
6
  ==== Installing from source
7
- "git clone http://github.com/jeremyd/rest_connection.git"
7
+ "git clone http://github.com/twrodriguez/rest_connection.git"
8
8
  "gem install jeweler rspec"
9
9
  "rake check_dependencies" <- Install any gems listed.
10
10
  "rake install"
@@ -40,8 +40,8 @@ Copy the example from GEMHOME/rest_connection/examples/rest_api_config.yaml.samp
40
40
  my_servers.each { |s| s.wait_for_state("stopped") }
41
41
 
42
42
  === Activate an Ec2ServerArray / Display instances IPs
43
-
44
- my_array = Ec2ServerArray.find(opts[:href])
43
+
44
+ my_array = Ec2ServerArray.find(opts[:href])
45
45
  my_array.active = true
46
46
  my_array.save
47
47
 
data/Rakefile CHANGED
@@ -4,12 +4,13 @@ Jeweler::Tasks.new do |gemspec|
4
4
  gemspec.name = "rest_connection"
5
5
  gemspec.summary = "lib for restful connections to the rightscale api"
6
6
  gemspec.description = "provides rest_connection"
7
- gemspec.email = "jeremy@rubyonlinux.org"
8
- gemspec.homepage = "http://github.com/jeremyd/rest_connection"
9
- gemspec.authors = ["Jeremy Deininger"]
7
+ gemspec.email = ["jeremy@rubyonlinux.org", "tw.rodriguez@gmail.com"]
8
+ gemspec.homepage = "http://github.com/twrodriguez/rest_connection"
9
+ gemspec.authors = ["Jeremy Deininger", "Timothy Rodriguez"]
10
10
  gemspec.add_dependency('activesupport', "=2.3.10")
11
- gemspec.add_dependency('net-ssh')
11
+ gemspec.add_dependency('net-ssh', "=2.1.4")
12
12
  gemspec.add_dependency('json')
13
+ gemspec.add_dependency('highline')
13
14
  end
14
15
  Jeweler::GemcutterTasks.new
15
16
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.23
1
+ 0.1.0
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ rake build
4
+ gem install ../pkg/virtualmonkey*.gem --no-rdoc --no-ri
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ rake build
4
+ gem install ../pkg/virtualmonkey*.gem --no-rdoc --no-ri
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+
3
+ whitespace=""
4
+ for FILE in `git diff-index --name-only HEAD --` ; do
5
+ if test -e $FILE; then
6
+ if [[ -n `grep "\\s\\s*$" $FILE` ]]; then whitespace="$whitespace $FILE"; fi
7
+ # Remove trailing whitespace
8
+ sed -i "s/\\s\\s*$//g" $FILE
9
+ # Remove tabs
10
+ sed -i "s/\t/ /g" $FILE
11
+ # If a file is ruby, check for syntax errors
12
+ if [[ -n `find $FILE -regex ".*\.rb$"` ]]; then
13
+ if [[ "$fail" -eq 0 || -z "$fail" ]]; then
14
+ `ruby -c $FILE 1> /dev/null`; fail=$?
15
+ else
16
+ `ruby -c $FILE 1> /dev/null`
17
+ fi
18
+ fi
19
+ fi
20
+ done
21
+
22
+ # Built-in git checks
23
+ git diff-index --check HEAD --
24
+
25
+ if [[ "$fail" -ne 0 && -n "$fail" ]]; then
26
+ echo "Syntax Errors Found. Aborting commit"
27
+ exit 1
28
+ fi
29
+
30
+ for FILE in $whitespace; do
31
+ echo "Whitespace problem fixed. Please re-add '$FILE' to your commit"
32
+ done
33
+ if [[ -n "$whitespace" ]]; then exit 1; fi
34
+
35
+ # Check that project metadata files exist
36
+ for FILE in "Rakefile" "README.rdoc" "VERSION" ".gitignore" "rest_connection.gemspec"; do
37
+ if test ! -e $FILE; then
38
+ echo "$FILE not present. Aborting commit"
39
+ exit 1
40
+ fi
41
+ done
42
+
43
+ # Check that username and user emails are filled properly
44
+ username=`git config --get user.name`
45
+ useremail=`git config --get user.email`
46
+ emaildomain=`echo $useremail | grep -o "[^@]*$"`
47
+ if [[ "$username" == "" ]]; then
48
+ echo "Please set your git user.name by running 'git config user.name <your github username>'"
49
+ exit 1
50
+ elif [[ "$useremail" == "" ]] || ! host "$emaildomain" &> /dev/null; then
51
+ echo "Please set your git user.email by running 'git config user.email <your github email>'"
52
+ exit 1
53
+ fi
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -19,9 +19,56 @@ require 'json'
19
19
  require 'yaml'
20
20
  require 'cgi'
21
21
  require 'rest_connection/rightscale/rightscale_api_resources'
22
+ require 'rest_connection/patches'
22
23
  require 'logger'
24
+ require 'highline/import'
23
25
 
24
26
  module RestConnection
27
+ AWS_CLOUDS = [{"cloud_id" => 1, "name" => "AWS US-East"},
28
+ {"cloud_id" => 2, "name" => "AWS EU"},
29
+ {"cloud_id" => 3, "name" => "AWS US-West"},
30
+ {"cloud_id" => 4, "name" => "AWS AP-Singapore"},
31
+ {"cloud_id" => 5, "name" => "AWS AP-Tokyo"}]
32
+
33
+ # Check for API 0.1 Access
34
+ def self.api0_1?
35
+ unless class_variable_defined?("@@api0_1")
36
+ begin
37
+ Ec2SshKeyInternal.find_all
38
+ @@api0_1 = true
39
+ rescue
40
+ @@api0_1 = false
41
+ end
42
+ end
43
+ return @@api0_1
44
+ end
45
+
46
+ # Check for API 1.0 Access
47
+ def self.api1_0?
48
+ unless class_variable_defined?("@@api1_0")
49
+ begin
50
+ Ec2SecurityGroup.find_all
51
+ @@api1_0 = true
52
+ rescue
53
+ @@api1_0 = false
54
+ end
55
+ end
56
+ return @@api1_0
57
+ end
58
+
59
+ # Check for API 1.5 Beta Access
60
+ def self.api1_5?
61
+ unless class_variable_defined?("@@api1_5")
62
+ begin
63
+ Cloud.find_all
64
+ @@api1_5 = true
65
+ rescue
66
+ @@api1_5 = false
67
+ end
68
+ end
69
+ return @@api1_5
70
+ end
71
+
25
72
  class Connection
26
73
  # Settings is a hash of options for customizing the connection.
27
74
  # settings.merge! {
@@ -35,9 +82,13 @@ module RestConnection
35
82
  # Settings are loaded from a yaml configuration file in users home directory.
36
83
  # Copy the example config from the gemhome/config/rest_api_config.yaml.sample to ~/.rest_connection/rest_api_config.yaml
37
84
  # OR to /etc/rest_connection/rest_api_config.yaml
85
+ # Here's an example of overriding the settings in the configuration file:
86
+ # Server.connection.settings[:api_url] = "https://my.rightscale.com/api/acct/1234"
38
87
  #
39
88
  def initialize(config_yaml = File.join(File.expand_path("~"), ".rest_connection", "rest_api_config.yaml"))
40
89
  @@logger = nil
90
+ @@user = nil
91
+ @@pass = nil
41
92
  etc_config = File.join("#{File::SEPARATOR}etc", "rest_connection", "rest_api_config.yaml")
42
93
  if File.exists?(config_yaml)
43
94
  @settings = YAML::load(IO.read(config_yaml))
@@ -50,33 +101,41 @@ module RestConnection
50
101
  end
51
102
  @settings[:extension] = ".js"
52
103
  @settings[:api_href] = @settings[:api_url] unless @settings[:api_href]
104
+ unless @settings[:user]
105
+ @@user = ask("Username:") unless @@user
106
+ @settings[:user] = @@user
107
+ end
108
+ unless @settings[:pass]
109
+ @@pass = ask("Password:") { |q| q.echo = false } unless @@pass
110
+ @settings[:pass] = @@pass
111
+ end
53
112
  end
54
113
 
55
114
  # Main HTTP connection loop. Common settings are set here, then we yield(BASE_URI, OPTIONAL_HEADERS) to other methods for each type of HTTP request: GET, PUT, POST, DELETE
56
- #
115
+ #
57
116
  # The block must return a Net::HTTP Request. You have a chance to taylor the request inside the block that you pass by modifying the url and headers.
58
117
  #
59
118
  # rest_connect do |base_uri, headers|
60
119
  # headers.merge! {:my_header => "blah"}
61
120
  # Net::HTTP::Get.new(base_uri, headers)
62
121
  # end
63
- #
122
+ #
64
123
  def rest_connect(&block)
65
124
  uri = URI.parse(@settings[:api_href])
66
125
  http = Net::HTTP.new(uri.host, uri.port)
67
126
  if uri.scheme == 'https'
68
- http.use_ssl = true
127
+ http.use_ssl = true
69
128
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
70
129
  end
71
130
  headers = @settings[:common_headers]
72
- headers.merge!("Cookie" => @cookie) if @cookie
131
+ headers.merge!("Cookie" => @cookie) if @cookie
73
132
  http.start do |http|
74
133
  req = yield(uri, headers)
75
134
  unless @cookie
76
135
  req.basic_auth(@settings[:user], @settings[:pass]) if @settings[:user]
77
136
  end
78
137
  logger("#{req.method}: #{req.path}")
79
- logger("\trequest body: #{req.body}") if req.body
138
+ logger("\trequest body: #{req.body}") if req.body and req.body !~ /password/
80
139
  response, body = http.request(req)
81
140
  handle_response(response)
82
141
  end
@@ -94,7 +153,7 @@ module RestConnection
94
153
  Net::HTTP::Get.new(new_path, headers)
95
154
  end
96
155
  end
97
-
156
+
98
157
  # connection.post(server_url + "/start")
99
158
  #
100
159
  # href = "/api/base_new" if this begins with a slash then the url will be used as absolute path.
@@ -122,7 +181,7 @@ module RestConnection
122
181
  rest_connect do |base_uri, headers|
123
182
  href = "#{base_uri}/#{href}" unless begins_with_slash(href)
124
183
  new_path = URI.escape(href)
125
- req = Net::HTTP::Put.new(new_path, headers)
184
+ req = Net::HTTP::Put.new(new_path, headers)
126
185
  req.set_content_type('application/json')
127
186
  req.body = additional_parameters.to_json
128
187
  req
@@ -146,11 +205,11 @@ module RestConnection
146
205
  end
147
206
 
148
207
  # handle_response
149
- # res = HTTP response
208
+ # res = HTTP response
150
209
  #
151
210
  # decoding and post processing goes here. This is where you may need some customization if you want to handle the response differently (or not at all!). Luckily it's easy to modify based on this handler.
152
211
  def handle_response(res)
153
- if res.code.to_i == 201
212
+ if res.code.to_i == 201 or res.code.to_i == 202
154
213
  return res['Location']
155
214
  elsif [200,203,204,302].detect { |d| d == res.code.to_i }
156
215
  if res.body
@@ -162,7 +221,7 @@ module RestConnection
162
221
  else
163
222
  return res
164
223
  end
165
- else
224
+ else
166
225
  raise "invalid response HTTP code: #{res.code.to_i}, #{res.code}, #{res.body}"
167
226
  end
168
227
  end
@@ -184,7 +243,11 @@ module RestConnection
184
243
  @@logger.info(init_message)
185
244
  end
186
245
 
187
- @@logger.info(message)
246
+ if @settings.nil?
247
+ @@logger.info(message)
248
+ else
249
+ @@logger.info("[API v#{@settings[:common_headers]['X_API_VERSION']} ]" + message)
250
+ end
188
251
  end
189
252
 
190
253
  # used by requestify to build parameters strings
@@ -0,0 +1,106 @@
1
+ # Hash Patches
2
+
3
+ class Hash
4
+ # Merges self with another second, recursively.
5
+ #
6
+ # This code was lovingly stolen from some random gem:
7
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
8
+ #
9
+ # Thanks to whoever made it.
10
+ #
11
+ # Modified to provide same functionality with Arrays
12
+
13
+ def deep_merge(second)
14
+ target = dup
15
+ return target unless second
16
+ second.keys.each do |k|
17
+ if second[k].is_a? Array and self[k].is_a? Array
18
+ target[k] = target[k].deep_merge(second[k])
19
+ next
20
+ elsif second[k].is_a? Hash and self[k].is_a? Hash
21
+ target[k] = target[k].deep_merge(second[k])
22
+ next
23
+ end
24
+ target[k] = second[k]
25
+ end
26
+ target
27
+ end
28
+
29
+ # From: http://www.gemtacular.com/gemdocs/cerberus-0.2.2/doc/classes/Hash.html
30
+ # File lib/cerberus/utils.rb, line 42
31
+ # Modified to provide same functionality with Arrays
32
+
33
+ def deep_merge!(second)
34
+ return nil unless second
35
+ second.each_pair do |k,v|
36
+ if self[k].is_a?(Array) and second[k].is_a?(Array)
37
+ self[k].deep_merge!(second[k])
38
+ elsif self[k].is_a?(Hash) and second[k].is_a?(Hash)
39
+ self[k].deep_merge!(second[k])
40
+ else
41
+ self[k] = second[k]
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Array Patches
48
+
49
+ class Array
50
+ def deep_merge(second)
51
+ target = dup
52
+ return target unless second
53
+ second.each_index do |k|
54
+ if second[k].is_a? Array and self[k].is_a? Array
55
+ target[k] = target[k].deep_merge(second[k])
56
+ next
57
+ elsif second[k].is_a? Hash and self[k].is_a? Hash
58
+ target[k] = target[k].deep_merge(second[k])
59
+ next
60
+ end
61
+ target << second[k] unless target.include?(second[k])
62
+ end
63
+ target
64
+ end
65
+
66
+ def deep_merge!(second)
67
+ return nil unless second
68
+ second.each_index do |k|
69
+ if self[k].is_a?(Array) and second[k].is_a?(Array)
70
+ self[k].deep_merge!(second[k])
71
+ elsif self[k].is_a?(Hash) and second[k].is_a?(Hash)
72
+ self[k].deep_merge!(second[k])
73
+ else
74
+ self << second[k] unless self.include?(second[k])
75
+ end
76
+ end
77
+ end
78
+
79
+ def *(second)
80
+ if second.is_a?(Integer)
81
+ ret = []
82
+ second.times { |i| ret += dup }
83
+ return ret
84
+ elsif second.is_a?(Array)
85
+ ret = []
86
+ each { |x| second.each { |y| ret << [x,y].flatten } }
87
+ return ret
88
+ else
89
+ raise TypeError.new("can't convert #{second.class} into Integer")
90
+ end
91
+ end
92
+
93
+ def **(second)
94
+ if second.is_a?(Integer)
95
+ ret = dup
96
+ (second - 1).times {
97
+ temp = []
98
+ ret.each { |x| each { |y| temp << [x,y].flatten } }
99
+ ret = temp
100
+ }
101
+ return ret
102
+ else
103
+ raise TypeError.new("can't convert #{second.class} into Integer")
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,27 @@
1
+ # This file is part of RestConnection
2
+ #
3
+ # RestConnection is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RestConnection is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ #
17
+ # You must have Beta v1.5 API access to use these internal API calls.
18
+ #
19
+
20
+ #
21
+ # Account Resource requires "admin" role
22
+ #
23
+
24
+ class Account
25
+ include RightScale::Api::Gateway
26
+ extend RightScale::Api::GatewayExtend
27
+ end
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -24,4 +24,4 @@ class AlertSpec
24
24
  end
25
25
 
26
26
 
27
- end
27
+ end
@@ -1,4 +1,4 @@
1
- # This file is part of RestConnection
1
+ # This file is part of RestConnection
2
2
  #
3
3
  # RestConnection is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -31,18 +31,18 @@ class AuditEntry
31
31
  def wait_for_state(state, timeout=900)
32
32
  while(timeout > 0)
33
33
  reload
34
+ return true if state == self.state
34
35
  connection.logger("state is #{self.state}, waiting for #{state}")
35
36
  friendly_url = "https://my.rightscale.com/audit_entries/"
36
37
  friendly_url += self.href.split(/\//).last
37
38
  raise "FATAL error, #{self.summary}\nSee Audit: API:#{self.href}, WWW:<a href='#{friendly_url}'>#{friendly_url}</a>\n" if self.state == 'failed'
38
39
  sleep 30
39
40
  timeout -= 30
40
- return true if state == self.state
41
41
  end
42
42
  raise "FATAL: Timeout waiting for Executable to complete. State was #{self.state}" if timeout <= 0
43
43
  end
44
44
 
45
- def wait_for_completed(legacy=nil)
46
- wait_for_state("completed")
45
+ def wait_for_completed(timeout=900)
46
+ wait_for_state("completed", timeout)
47
47
  end
48
- end
48
+ end