rhc 0.97.17 → 0.98.16

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 (93) hide show
  1. data/bin/rhc +9 -9
  2. data/bin/rhc-port-forward +3 -0
  3. data/bin/rhc-sshkey +3 -0
  4. data/features/README.md +163 -35
  5. data/features/application.feature +24 -0
  6. data/features/cartridge.feature +24 -0
  7. data/features/client.feature +11 -0
  8. data/features/domain.feature +29 -0
  9. data/features/lib/rhc_helper.rb +14 -0
  10. data/features/lib/rhc_helper/app.rb +2 -8
  11. data/features/lib/rhc_helper/cartridge.rb +1 -7
  12. data/features/lib/rhc_helper/commandify.rb +37 -6
  13. data/features/lib/rhc_helper/domain.rb +5 -1
  14. data/features/lib/rhc_helper/httpify.rb +124 -60
  15. data/features/lib/rhc_helper/loggable.rb +0 -2
  16. data/features/lib/rhc_helper/persistable.rb +12 -2
  17. data/features/lib/rhc_helper/sshkey.rb +29 -0
  18. data/features/multiple_cartridge.feature +17 -0
  19. data/features/sshkey.feature +58 -0
  20. data/features/step_definitions/application_steps.rb +60 -52
  21. data/features/step_definitions/cartridge_steps.rb +22 -24
  22. data/features/step_definitions/client_steps.rb +9 -2
  23. data/features/step_definitions/domain_steps.rb +4 -0
  24. data/features/step_definitions/sshkey_steps.rb +56 -0
  25. data/features/support/assumptions.rb +37 -0
  26. data/features/support/before_hooks.rb +25 -0
  27. data/features/support/env.rb +84 -39
  28. data/features/support/key1 +27 -0
  29. data/features/support/key1.pub +1 -0
  30. data/features/support/key2 +27 -0
  31. data/features/support/key2.pub +1 -0
  32. data/features/support/key3.pub +1 -0
  33. data/features/support/ssh.sh +2 -0
  34. data/features/verify.feature +18 -159
  35. data/lib/rhc-common.rb +8 -21
  36. data/lib/rhc.rb +9 -5
  37. data/lib/rhc/autocomplete.rb +68 -0
  38. data/lib/rhc/autocomplete_templates/rhc.erb +33 -0
  39. data/lib/rhc/cli.rb +9 -6
  40. data/lib/rhc/command_runner.rb +108 -0
  41. data/lib/rhc/commands.rb +66 -132
  42. data/lib/rhc/commands/base.rb +95 -24
  43. data/lib/rhc/commands/domain.rb +33 -50
  44. data/lib/rhc/commands/port-forward.rb +81 -0
  45. data/lib/rhc/commands/setup.rb +1 -1
  46. data/lib/rhc/commands/sshkey.rb +95 -0
  47. data/lib/rhc/config.rb +108 -103
  48. data/lib/rhc/context_helper.rb +19 -0
  49. data/lib/rhc/coverage_helper.rb +1 -1
  50. data/lib/rhc/exceptions.rb +55 -4
  51. data/lib/rhc/help_formatter.rb +2 -3
  52. data/lib/rhc/helpers.rb +31 -23
  53. data/lib/{rhc-rest.rb → rhc/rest.rb} +95 -23
  54. data/lib/{rhc-rest → rhc/rest}/application.rb +1 -1
  55. data/lib/{rhc-rest → rhc/rest}/cartridge.rb +1 -1
  56. data/lib/{rhc-rest → rhc/rest}/client.rb +40 -9
  57. data/lib/{rhc-rest → rhc/rest}/domain.rb +1 -1
  58. data/lib/{rhc-rest → rhc/rest}/key.rb +11 -1
  59. data/lib/{rhc-rest → rhc/rest}/user.rb +1 -1
  60. data/lib/rhc/ssh_key_helpers.rb +10 -1
  61. data/lib/rhc/targz.rb +7 -8
  62. data/lib/rhc/usage_templates/command_help.erb +7 -6
  63. data/lib/rhc/usage_templates/help.erb +6 -9
  64. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  65. data/lib/rhc/version.rb +2 -2
  66. data/lib/rhc/wizard.rb +4 -9
  67. data/spec/coverage_helper.rb +2 -2
  68. data/spec/rest_spec_helper.rb +66 -16
  69. data/spec/rhc/cli_spec.rb +16 -5
  70. data/spec/rhc/command_spec.rb +61 -6
  71. data/spec/rhc/commands/domain_spec.rb +50 -27
  72. data/spec/rhc/commands/port-forward_spec.rb +133 -0
  73. data/spec/rhc/commands/setup_spec.rb +2 -2
  74. data/spec/rhc/commands/sshkey_spec.rb +141 -0
  75. data/spec/rhc/common_spec.rb +1 -1
  76. data/spec/rhc/config_spec.rb +6 -4
  77. data/spec/rhc/helpers_spec.rb +0 -21
  78. data/spec/rhc/rest_application_spec.rb +7 -7
  79. data/spec/rhc/rest_client_spec.rb +87 -24
  80. data/spec/rhc/rest_spec.rb +36 -36
  81. data/spec/rhc/wizard_spec.rb +3 -3
  82. data/spec/spec.opts +1 -0
  83. data/spec/spec_helper.rb +3 -3
  84. metadata +61 -31
  85. data/lib/rhc-rest/exceptions/exceptions.rb +0 -75
  86. data/test/functional/application_test.rb +0 -71
  87. data/test/functional/domain_test.rb +0 -123
  88. data/test/functional/test_credentials.rb +0 -5
  89. data/test/sample-usage.rb +0 -122
  90. data/test/support/server.rb +0 -14
  91. data/test/support/testcase.rb +0 -3
  92. data/test/test_helper.rb +0 -4
  93. data/test/unit/command_test.rb +0 -19
@@ -1,5 +1,5 @@
1
1
  require 'dnsruby'
2
- require 'rhc-rest'
2
+ require 'rhc/rest'
3
3
 
4
4
  module RHCHelper
5
5
  #
@@ -38,6 +38,10 @@ module RHCHelper
38
38
  end
39
39
  end
40
40
 
41
+ def self.create
42
+ rhc_domain_create
43
+ end
44
+
41
45
  def self.delete
42
46
  rhc_domain_delete
43
47
  $namespace = nil
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'uri'
2
3
  require 'net/https'
3
4
  require 'ostruct'
@@ -21,62 +22,125 @@ module RHCHelper
21
22
  return http.start
22
23
  end
23
24
 
24
- def http_get(url, timeout=30)
25
- uri = URI.parse(url)
26
- http = http_instance(uri, timeout)
27
- request = Net::HTTP::Get.new(uri.request_uri)
28
- http.request(request)
29
- end
30
-
31
- def http_head(url, host=nil, follow_redirects=true)
32
- uri = URI.parse(url)
33
- http = http_instance(uri)
34
- request = Net::HTTP::Head.new(uri.request_uri)
35
- request["Host"] = host if host
36
- response = http.request(request)
37
-
38
- if follow_redirects and response.is_a?(Net::HTTPRedirection)
39
- return http_head(response.header['location'])
40
- else
41
- return response
25
+ def do_http(options)
26
+ # Generate the URL if it doesn't exist
27
+ options[:url] ||= "http%s://%s" % [options[:use_https] ? 's' : '', hostname]
28
+
29
+ # Set some default options
30
+ http_defaults = {
31
+ :method => :head,
32
+ :host => nil,
33
+ :expected => Net::HTTPSuccess,
34
+ :sleep => 5,
35
+ :timeout => 1200,
36
+ :http_timeout => 30,
37
+ :follow_redirects => true,
38
+ :redirects => 0,
39
+ :max_redirects => 10
40
+ }
41
+ options = http_defaults.merge(options)
42
+
43
+ # Parse the URI
44
+ uri = URI.parse(options[:url])
45
+ # Start with a nil response
46
+ response = nil
47
+
48
+ # Set some headers
49
+ headers = {}
50
+ headers['Host'] = host if options[:host]
51
+
52
+ # Keep retrying, and let Ruby handle the timeout
53
+ start = Time.now
54
+
55
+ # Helper function to log message and sleep
56
+ def my_sleep(start,uri,e,options)
57
+ err_str = "Connection inacessible for %s (%s) - %.2f seconds"
58
+ logger.info(err_str % [uri,e.class,Time.now - start])
59
+ logger.info "Sleeping for %d seconds, retrying" % options[:sleep]
60
+ sleep options[:sleep]
42
61
  end
43
- end
44
62
 
45
- def is_inaccessible?(max_retries=120)
46
- max_retries.times do |i|
47
- begin
48
- if http_head("http://#{hostname}").is_a? Net::HTTPServerError
49
- return true
50
- else
51
- logger.info("Connection still accessible / retry #{i} / #{hostname}")
52
- sleep 1
63
+ begin
64
+ timeout(options[:timeout]) do
65
+ loop do
66
+ # Send the HTTP request
67
+ response = begin
68
+ http = http_instance(uri,options[:http_timeout])
69
+ logger.debug "Requesting: #{uri}"
70
+ http.send_request(
71
+ options[:method].to_s.upcase, # Allow options to be a symbol
72
+ uri.request_uri, nil, headers
73
+ )
74
+ rescue Exception => e
75
+ # Pass these up so we can check them
76
+ return e
77
+ end
78
+ logger.debug "Received: %s" % response
79
+
80
+ case response
81
+ # Catch any response if we're expecting it
82
+ when options[:expected]
83
+ break
84
+ # Retry these responses
85
+ when Net::HTTPServiceUnavailable, SocketError
86
+ my_sleep(start,uri,response,options)
87
+ else
88
+ # Some other response
89
+ break
90
+ end
53
91
  end
54
- rescue
55
- return true
56
92
  end
93
+ rescue Timeout::Error => e
94
+ puts "Did not receive an acceptable response in %d seconds" % options[:timeout]
57
95
  end
58
- return false
96
+
97
+ # Test to see if we should follow redirect
98
+ if options[:follow_redirects] && response.is_a?(Net::HTTPRedirection) && !(response.is_a?(options[:expected]))
99
+ logger.debug "Response was a redirect, we will attempt to follow"
100
+ logger.debug "We've been redirected #{options[:redirects]} times"
101
+ if options[:redirects] < options[:max_redirects]
102
+ options[:redirects] += 1
103
+ response = do_http(options.merge({
104
+ :url => response.header['location']
105
+ }))
106
+ else
107
+ logger.debug "Too many redirects"
108
+ end
109
+ end
110
+
111
+ return response
59
112
  end
60
113
 
61
- def is_accessible?(use_https=false, max_retries=120, host=nil)
62
- prefix = use_https ? "https://" : "http://"
63
- url = prefix + hostname
114
+ def is_inaccessible?
115
+ check_response({
116
+ :expected => Net::HTTPServiceUnavailable
117
+ })
118
+ end
64
119
 
65
- max_retries.times do |i|
66
- begin
67
- if http_head(url, host).is_a? Net::HTTPSuccess
68
- return true
69
- else
70
- logger.info("Connection still inaccessible / retry #{i} / #{url}")
71
- sleep 1
72
- end
73
- rescue SocketError
74
- logger.info("Connection still inaccessible / retry #{i} / #{url}")
75
- sleep 1
120
+ def is_accessible?(options = {})
121
+ check_response(options.merge({
122
+ :expected => Net::HTTPSuccess
123
+ }))
124
+ end
125
+
126
+ def doesnt_exist?
127
+ check_response({
128
+ :expected => SocketError,
129
+ }) do |response|
130
+ return !(response.is_a?(Net::HTTPSuccess))
76
131
  end
77
- end
132
+ end
78
133
 
79
- return false
134
+ def check_response(options)
135
+ response = do_http(options)
136
+
137
+ if block_given?
138
+ # Use the custom check for this response
139
+ yield response
140
+ else
141
+ # Compare the response against :expected or Net::HTTPSuccess
142
+ response.is_a?(options[:expected] || Net::HTTPSuccess)
143
+ end
80
144
  end
81
145
 
82
146
  def connect(use_https=false, max_retries=30)
@@ -86,22 +150,22 @@ module RHCHelper
86
150
  logger.info("Connecting to #{url}")
87
151
  beginning_time = Time.now
88
152
 
89
- max_retries.times do |i|
90
- response = http_get(url, 1)
153
+ response = do_http({
154
+ :method => :get,
155
+ :url => url,
156
+ :http_timeout => 1
157
+ })
91
158
 
92
- if response.is_a? Net::HTTPSuccess
93
- @response_code = response.code
94
- @response_time = Time.now - beginning_time
95
- logger.info("Connection result = #{@response_code} / #{url}")
96
- logger.info("Connection response time = #{@response_time} / #{url}")
97
- return response.body
98
- else
99
- logger.info("Connection failed / retry #{i} / #{url}")
100
- sleep 1
101
- end
159
+ if response.is_a? Net::HTTPSuccess
160
+ @response_code = response.code
161
+ @response_time = Time.now - beginning_time
162
+ logger.info("Connection result = #{@response_code} / #{url}")
163
+ logger.info("Connection response time = #{@response_time} / #{url}")
164
+ return response.body
165
+ else
166
+ logger.info("Connection failed / #{url}")
167
+ return nil
102
168
  end
103
-
104
- return nil
105
169
  end
106
170
  end
107
171
  end
@@ -2,8 +2,6 @@ require 'logger'
2
2
 
3
3
  module RHCHelper
4
4
  module Loggable
5
- PASSWORD_REGEX = / -p [^\s]* /
6
-
7
5
  def logger
8
6
  Loggable.logger
9
7
  end
@@ -6,8 +6,18 @@ module RHCHelper
6
6
  include ActiveSupport::JSON
7
7
  include Loggable
8
8
 
9
- def find_on_fs
10
- Dir.glob("#{RHCHelper::TEMP_DIR}/*.json").collect {|f| App.from_file(f)}
9
+ def find_on_fs(options = {})
10
+ # Find all apps
11
+ apps = Dir.glob("#{RHCHelper::TEMP_DIR}/*.json").collect {|f| App.from_file(f)}
12
+
13
+ if options.empty?
14
+ apps.first
15
+ else
16
+ apps.find do |app|
17
+ # Loop through all options and then run AND on all of the results
18
+ options.map{|name,expected| app.send(name) == expected }.inject(&:&)
19
+ end
20
+ end
11
21
  end
12
22
 
13
23
  def from_file(filename)
@@ -0,0 +1,29 @@
1
+ require "rhc/rest"
2
+
3
+ module RHCHelper
4
+
5
+ class Sshkey
6
+ extend Runnable
7
+ extend Commandify
8
+
9
+ class << self
10
+ attr_accessor :sshkey_output, :exitcode
11
+ end
12
+
13
+ def self.list(*args)
14
+ rhc_sshkey_list args
15
+ end
16
+
17
+ def self.show(*args)
18
+ rhc_sshkey_show args
19
+ end
20
+
21
+ def self.add(*args)
22
+ rhc_sshkey_add args
23
+ end
24
+
25
+ def self.remove(*args)
26
+ rhc_sshkey_remove args
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ @multiple_cartridge @domain_required
2
+ Feature: Multiple Cartridge Tests
3
+
4
+ @init
5
+ Scenario: Supporting Cartridge Added
6
+ Given an existing or new php-5.3 application with an embedded mysql-5.1 cartridge
7
+ When the phpmyadmin-3.4 cartridge is added
8
+ Then the phpmyadmin-3.4 cartridge should be running
9
+
10
+ Scenario: Conflicting Cartridge Fails
11
+ Then adding the postgresql-8.5 cartridge should fail
12
+
13
+ Scenario: Cartridge Removed
14
+ When the phpmyadmin-3.4 cartridge is removed
15
+ When the mysql-5.1 cartridge is removed
16
+ Then the phpmyadmin-3.4 cartridge should be removed
17
+ Then the mysql-5.1 cartridge should be removed
@@ -0,0 +1,58 @@
1
+ @sshkey @client_tools_required
2
+ Feature: SSH key Management
3
+ As an OpenShift user, I want to manage SSH keys with 'rhc sshkey' commands.
4
+
5
+ @sshkey_list
6
+ Scenario: SSH key is listed
7
+ Given the SSH key "key1" already exists
8
+ When 'rhc sshkey list' is run
9
+ Then the output includes the key information
10
+
11
+ @sshkey_show
12
+ Scenario: SSH key is shown individually
13
+ Given the SSH key "key1" already exists
14
+ When 'rhc sshkey show "key1"' is run
15
+ Then the output includes the key information for "key1"
16
+
17
+ @sshkey_show
18
+ Scenario: Requested SSH key does not exist
19
+ Given the SSH key "key2" does not exist
20
+ When 'rhc sshkey show "key2"' command is run
21
+ Then the command exits with status code 118
22
+
23
+ @sshkey_add
24
+ Scenario: SSH key is added successfully
25
+ Given the SSH key "key1" does not exist
26
+ When a new SSH key "features/support/key1.pub" is added as "key1"
27
+ Then the key "key1" should exist
28
+ And the command exits with status code 0
29
+
30
+ @sshkey_add
31
+ Scenario: invalid SSH key is added
32
+ Given the SSH key "key1" does not exist
33
+ When a new SSH key "features/support/key3.pub" is added as "key3"
34
+ Then the command exits with status code 128
35
+
36
+ @sshkey_add
37
+ Scenario: SSH key with the same name already exists
38
+ Given the SSH key "key1" already exists
39
+ When a new SSH key "features/support/key2.pub" is added as "key1"
40
+ Then the command exits with status code 128
41
+
42
+ @sshkey_add
43
+ Scenario: SSH key with the identical content already exists
44
+ Given an SSH key "key2" with the same content as "key1" exists
45
+ And the SSH key "key1" does not exist
46
+ When 'rhc sshkey add "key1" "features/support/key1.pub"' is run
47
+ Then the command exits with status code 128
48
+
49
+ @sshkey_remove
50
+ Scenario: SSH key is deleted successfully
51
+ When 'rhc sshkey remove "key1"' is run
52
+ Then the SSH key "key1" is deleted
53
+
54
+ @sshkey_remove
55
+ Scenario: SSH key requested to be deleted does not exist
56
+ Given the SSH key "key1" does not exist
57
+ When 'rhc sshkey remove "key1"' is run
58
+ Then the command exits with status code 118
@@ -3,75 +3,78 @@ require 'rhc/config'
3
3
 
4
4
  include RHCHelper
5
5
 
6
- Given /^an existing (.+) application with an embedded (.*) cartridge$/ do |type, embed|
7
- App.find_on_fs.each do |app|
8
- if app.type == type and app.embed.include?(embed)
9
- @app = app
10
- break
11
- end
6
+ # This can transform any application cartridge requirements into an array
7
+ Transform /^application with(.*)$/ do |embed_type|
8
+ case embed_type.strip
9
+ when /^out an embedded cartridge/
10
+ []
11
+ when /^an embedded (.*) cartridge$/
12
+ [$1]
13
+ when /^embedded (.*) and (.*) cartridges$/
14
+ [$1,$2]
12
15
  end
13
-
14
- @app.should_not be_nil, 'No existing applications w/cartridges found. Check the creation scenarios for failures.'
15
16
  end
16
17
 
17
- Given /^an existing (.+) application with embedded (.*) and (.*) cartridges$/ do |type, embed_1, embed_2|
18
- App.find_on_fs.each do |app|
19
- if app.type == type and app.embed.include?(embed_1) and app.embed.include?(embed_2)
20
- @app = app
21
- break
22
- end
23
- end
18
+ # Use the transformed array so we can reuse this step for all combinations
19
+ Given /^an existing (or new )?(.+) (application with.*)$/ do |create,type, embeds|
20
+ options = { :type => type }
21
+ options[:embed] = embeds if embeds
24
22
 
25
- @app.should_not be_nil, 'No existing applications w/cartridges found. Check the creation scenarios for failures.'
26
- end
23
+ @app = App.find_on_fs(options)
27
24
 
28
- Given /^an existing (.+) application( without an embedded cartridge)?$/ do |type, ignore|
29
- App.find_on_fs.each do |app|
30
- if app.type == type and app.embed.empty?
31
- @app = app
32
- break
25
+ if create && @app.nil?
26
+ step "a #{type} application is created"
27
+ embeds.each do |embed|
28
+ step "the #{embed} cartridge is added"
33
29
  end
34
30
  end
35
31
 
36
- @app.should_not be_nil, 'No existing applications found. Check the creation scenarios for failures.'
32
+ @app.should_not be_nil, "No existing %s applications %sfound. Check the creation scenarios for failures." % [
33
+ type,
34
+ embeds ? '' : "w/ [#{embeds.join(',')}]"
35
+ ]
36
+ end
37
+
38
+ # Mark this step as pending so we make sure to explicitly require apps without embeds
39
+ Given /^an existing (or new )?(.+) application$/ do |create,type|
40
+ pending
37
41
  end
38
42
 
39
43
  When /^(\d+) (.+) applications are created$/ do |app_count, type|
44
+ old_app = @app
40
45
  @apps = app_count.to_i.times.collect do
41
- app = App.create_unique(type)
42
- app.rhc_app_create
43
- app
46
+ Then "a #{type} application is created"
47
+ @app
44
48
  end
49
+ @app = old_app
45
50
  end
46
51
 
47
- When /^the application is stopped$/ do
48
- @app.rhc_app_stop
49
- end
50
-
51
- When /^the application is started$/ do
52
- @app.rhc_app_start
52
+ When /^a (.+) application is created$/ do |type|
53
+ @app = App.create_unique(type)
54
+ @app.rhc_app_create
53
55
  end
54
56
 
55
- When /^the application is restarted$/ do
56
- @app.rhc_app_restart
57
- end
58
-
59
- When /^the application is destroyed$/ do
60
- @app.rhc_app_destroy
61
- end
57
+ When /^the application is (\w+)$/ do |command|
58
+ # Do any pre-check setup we may need
59
+ case command
60
+ when 'snapshot'
61
+ @snapshot = File.join(RHCHelper::TEMP_DIR, "snapshot.tar.gz")
62
+ @app.snapshot = @snapshot
63
+ end
62
64
 
63
- When /^the application is snapshot$/ do
64
- @snapshot = File.join(RHCHelper::TEMP_DIR, "snapshot.tar.gz")
65
- @app.snapshot = @snapshot
66
- @app.rhc_app_snapshot_save
67
- end
65
+ # Set up aliases for any irregular commands
66
+ aliases = {
67
+ :stopped => :stop,
68
+ :shown => :show,
69
+ :tidied => :tidy,
70
+ :snapshot => :snapshot_save
71
+ }
68
72
 
69
- When /^the application is shown$/ do
70
- @app.rhc_app_show
71
- end
73
+ # Use an alias if it exists, or just remove 'ed' (like from started)
74
+ cmd = aliases[command.to_sym] || command.gsub(/ed$/,'').to_sym
72
75
 
73
- When /^the application is tidied$/ do
74
- @app.rhc_app_tidy
76
+ # Send the specified command to the application
77
+ @app.send("rhc_app_#{cmd}")
75
78
  end
76
79
 
77
80
  Then /^the snapshot should be found$/ do
@@ -80,20 +83,25 @@ Then /^the snapshot should be found$/ do
80
83
  end
81
84
 
82
85
  Then /^the applications should be accessible?$/ do
86
+ old_app = @app
83
87
  @apps.each do |app|
84
- app.is_accessible?.should be_true
85
- app.is_accessible?(true).should be_true
88
+ Then "the application should be accessible"
86
89
  end
90
+ @app = old_app
87
91
  end
88
92
 
89
93
  Then /^the application should be accessible$/ do
90
94
  @app.is_accessible?.should be_true
91
- @app.is_accessible?(true).should be_true, "Application was not accessible and should be"
95
+ @app.is_accessible?({:use_https => true}).should be_true, "Application was not accessible and should be"
92
96
  end
93
97
 
94
98
  Then /^the application should not be accessible$/ do
95
99
  @app.is_inaccessible?.should be_true, "Application was still accessible when it shouldn't be"
96
100
  end
97
101
 
102
+ Then /^the application should not exist$/ do
103
+ @app.doesnt_exist?.should be_true, "Application still exists when it shouldn't"
104
+ end
105
+
98
106
  Then /^it should succeed$/ do
99
107
  end