rhc 0.97.17 → 0.98.16

Sign up to get free protection for your applications and to get access to all the features.
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