rhc 1.27.4 → 1.28.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,8 +29,22 @@ class RHC::Commands::Base
29
29
  # should be through this call pattern.
30
30
  def rest_client(opts={})
31
31
  @rest_client ||= begin
32
- auth = RHC::Auth::Basic.new(options)
33
- auth = RHC::Auth::Token.new(options, auth, token_store) if (options.use_authorization_tokens || options.token) && !(options.rhlogin && options.password)
32
+ core_auth = if (options.ssl_client_cert_file && options.ssl_client_key_file)
33
+ RHC::Auth::X509.new(options)
34
+ else
35
+ RHC::Auth::Basic.new(options)
36
+ end
37
+
38
+ # Specifying a username and password on the CLI trumps token
39
+ # authentication.
40
+ auth = if options.rhlogin && options.password
41
+ RHC::Auth::Basic.new(options)
42
+ elsif (options.use_authorization_tokens || options.token)
43
+ RHC::Auth::Token.new(options, core_auth, token_store)
44
+ else
45
+ core_auth
46
+ end
47
+
34
48
  debug "Authenticating with #{auth.class}"
35
49
  client_from_options(:auth => auth)
36
50
  end
@@ -93,7 +93,7 @@ module RHC::Commands
93
93
  confirm_action "Are you sure you wish to remove the environment variable(s) above from application '#{rest_app.name}'?"
94
94
  say 'Removing environment variable(s) ... '
95
95
  rest_app.unset_environment_variables(env)
96
- success 'removed'
96
+ success 'done'
97
97
 
98
98
  0
99
99
  end
@@ -109,6 +109,7 @@ module RHC::Commands
109
109
  takes_application :argument => true
110
110
  option ["--table"], "Format the output list as a table"
111
111
  option ["--quotes"], "Format the output list with double quotes for env var values"
112
+ alias_action :"envs", :root_command => true
112
113
  def list(app)
113
114
  rest_app = find_app
114
115
  rest_env_vars = rest_app.environment_variables
@@ -0,0 +1,27 @@
1
+ require 'rhc/commands/base'
2
+
3
+ module RHC::Commands
4
+ class Region < Base
5
+ summary "Display the regions and zones available on the OpenShift server"
6
+ default_action :list
7
+
8
+ summary "List the regions and zones available on the OpenShift server"
9
+ alias_action :"regions", :root_command => true
10
+ def list
11
+ regions = rest_client.regions
12
+
13
+ raise RHC::NoRegionConfiguredException if regions.empty?
14
+
15
+ paragraph{ say "Server #{options.server}" }
16
+
17
+ regions.sort.each do |region|
18
+ display_region(region)
19
+ end
20
+
21
+ paragraph{ say "To create an app in a specific region use 'rhc create-app <name> <cartridge> --region <region>'." }
22
+
23
+ 0
24
+ end
25
+
26
+ end
27
+ end
@@ -46,7 +46,6 @@ module RHC::Commands
46
46
  say "Connected to #{openshift_server}"
47
47
 
48
48
  if openshift_online_server?
49
- #status = decode_json(get("#{openshift_url}/app/status/status.json").body)
50
49
  status = rest_client.request(:method => :get, :url => "#{openshift_url}/app/status/status.json", :lazy_auth => true){ |res| decode_json(res.content) }
51
50
  open = status['open']
52
51
 
@@ -55,9 +54,9 @@ module RHC::Commands
55
54
  open.each do |i|
56
55
  i = i['issue']
57
56
  say color("%-3s %s" % ["##{i['id']}", i['title']], :bold)
58
- items = i['updates'].map{ |u| [u['description'], date(u['created_at'])] }
59
- items.unshift ['Opened', date(i['created_at'])]
60
- table(items, :align => [nil,:right], :join => ' ').each{ |s| say " #{s}" }
57
+ items = i['updates'].map{ |u| [date(u['created_at']), u['description']] }
58
+ items.unshift [date(i['created_at']), 'Opened']
59
+ say table(items, :align => [nil,:left], :join => ' ')
61
60
  end
62
61
  say "\n"
63
62
  warn pluralize(open.length, "open issue")
@@ -76,7 +75,7 @@ module RHC::Commands
76
75
  When adding a new server users can optionally provide a 'nickname'
77
76
  that will allow to easily switch between servers.
78
77
  DESC
79
- syntax "<hostname> [<nickname>] [--rhlogin LOGIN] [--[no-]use-authorization-tokens] [--[no-]insecure]"
78
+ syntax "<hostname> [<nickname>] [--rhlogin LOGIN] [--[no-]use-authorization-tokens] [--[no-]insecure] [--use] [--skip-wizard] [--timeout SECONDS] [--ssl-ca-file FILE] [--ssl-client-cert-file FILE] [--ssl-version VERSION]"
80
79
  argument :hostname, "Hostname of the server you are adding", ["--server HOSTNAME"]
81
80
  argument :nickname, "Optionally provide a nickname to the server you are adding (e.g. 'development', 'production', 'online')", ["--nickname NICKNAME"], :optional => true
82
81
  option ["-l", "--rhlogin LOGIN"], "Change the default OpenShift login used on this server"
@@ -84,10 +83,14 @@ module RHC::Commands
84
83
  option ["--[no-]insecure"], "If true, certificate errors will be ignored"
85
84
  option ["--use"], "If provided, the server being added will be set as default (same as 'rhc server use')"
86
85
  option ["--skip-wizard"], "If provided, the wizard will be skipped and a session token will not be estabilished"
86
+ option ["--timeout SECONDS"], "The default timeout for operations on this server", :type => Integer
87
+ option ["--ssl-ca-file FILE"], "An SSL certificate CA file (may contain multiple certs) to be used on this server", :type => CertificateFile, :optional => true
88
+ option ["--ssl-client-cert-file FILE"], "An SSL x509 client certificate file to be used on this server", :type => CertificateFile, :optional => true
89
+ option ["--ssl-version VERSION"], "The version of SSL to use to be used on this server", :type => SSLVersion, :optional => true
87
90
  def add(hostname, nickname)
88
91
  raise ArgumentError, "The --use and --skip-wizard options cannot be used together." if options.use && options.skip_wizard
89
92
 
90
- attrs = [:login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_ca_file]
93
+ attrs = [:login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_client_key_file, :ssl_ca_file]
91
94
 
92
95
  server = server_configs.add(hostname,
93
96
  attrs.inject({:nickname => nickname}){ |h, (k, v)| h[k] = options[k == :login ? :rhlogin : k]; h })
@@ -132,7 +135,7 @@ module RHC::Commands
132
135
  def use(server)
133
136
  server = server_configs.find(server)
134
137
 
135
- attrs = [:login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_ca_file]
138
+ attrs = [:login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_client_key_file, :ssl_ca_file]
136
139
 
137
140
  if wizard_to_server(server.hostname, true, attrs.inject({}){ |h, (k, v)| h[k] = server.send(k); h })
138
141
  paragraph { success "Now using '#{server.hostname}'" }
@@ -162,7 +165,7 @@ module RHC::Commands
162
165
  end
163
166
 
164
167
  summary "Update server attributes"
165
- syntax "<server> [--hostname HOSTNAME] [--nickname NICKNAME] [--rhlogin LOGIN] [--[no-]use-authorization-tokens] [--[no-]insecure]"
168
+ syntax "<server> [--hostname HOSTNAME] [--nickname NICKNAME] [--rhlogin LOGIN] [--[no-]use-authorization-tokens] [--[no-]insecure] [--use] [--skip-wizard] [--timeout SECONDS] [--ssl-ca-file FILE] [--ssl-client-cert-file FILE] [--ssl-version VERSION]"
166
169
  argument :server, "Server hostname or nickname to be configured", ["--server SERVER"]
167
170
  option ["--hostname HOSTNAME"], "Change the hostname of this server"
168
171
  option ["--nickname NICKNAME"], "Change the nickname of this server"
@@ -171,12 +174,16 @@ module RHC::Commands
171
174
  option ["--[no-]insecure"], "If true, certificate errors will be ignored"
172
175
  option ["--use"], "If provided, the server being configured will be set as default (same as 'rhc server use')"
173
176
  option ["--skip-wizard"], "If provided, the wizard will be skipped and a session token will not be estabilished"
177
+ option ["--timeout SECONDS"], "The default timeout for operations on this server", :type => Integer
178
+ option ["--ssl-ca-file FILE"], "An SSL certificate CA file (may contain multiple certs) to be used on this server", :type => CertificateFile, :optional => true
179
+ option ["--ssl-client-cert-file FILE"], "An SSL x509 client certificate file to be used on this server", :type => CertificateFile, :optional => true
180
+ option ["--ssl-version VERSION"], "The version of SSL to use to be used on this server", :type => SSLVersion, :optional => true
174
181
  def configure(server)
175
182
  raise ArgumentError, "The --use and --skip-wizard options cannot be used together." if options.use && options.skip_wizard
176
183
 
177
184
  server = server_configs.find(server)
178
185
 
179
- attrs = [:hostname, :nickname, :login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_ca_file].inject({}){ |h, (k, v)| v = options[k == :login ? :rhlogin : k]; h[k] = (v.nil? ? server.send(k) : v); h }
186
+ attrs = [:hostname, :nickname, :login, :use_authorization_tokens, :insecure, :timeout, :ssl_version, :ssl_client_cert_file, :ssl_client_key_file, :ssl_ca_file].inject({}){ |h, (k, v)| v = options[k == :login ? :rhlogin : k]; h[k] = (v.nil? ? server.send(k) : v); h }
180
187
 
181
188
  raise RHC::ServerNicknameExistsException.new(options.nickname) if options.nickname &&
182
189
  server_configs.nickname_exists?(options.nickname) &&
@@ -213,10 +220,12 @@ module RHC::Commands
213
220
  options['server'] = hostname
214
221
  options['rhlogin'] = args[:login] if args[:login]
215
222
  options['use_authorization_tokens'] = args[:use_authorization_tokens] unless args[:use_authorization_tokens].nil?
223
+ options['create_token'] = args[:use_authorization_tokens] unless args[:use_authorization_tokens].nil?
216
224
  options['insecure'] = args[:insecure] unless args[:insecure].nil?
217
- options['timeout'] = args[:timeout] unless args[:timeout].nil?
225
+ options['timeout'] = args[:timeout]
218
226
  options['ssl_version'] = args[:ssl_version]
219
227
  options['ssl_client_cert_file'] = args[:ssl_client_cert_file]
228
+ options['ssl_client_key_file'] = args[:ssl_client_key_file]
220
229
  options['ssl_ca_file'] = args[:ssl_ca_file]
221
230
  RHC::ServerWizard.new(config, options, server_configs, set_default).run
222
231
  end
@@ -22,6 +22,7 @@ module RHC::Commands
22
22
 
23
23
  summary 'Display all the SSH keys for your account'
24
24
  syntax ''
25
+ alias_action :"sshkeys", :root_command => true
25
26
  def list
26
27
  keys = rest_client.sshkeys.each{ |key| paragraph{ display_key(key) } }
27
28
 
@@ -61,6 +61,7 @@ module RHC
61
61
  :insecure => [nil, :boolean, "If true, certificate errors will be ignored.\nWARNING: This may allow others to eavesdrop on your communication with OpenShift."],
62
62
  :ssl_version => [nil, nil, 'The SSL protocol version to use when connecting to this server'],
63
63
  :ssl_client_cert_file => [nil, :path_to_file, 'A client certificate file for use with your server'],
64
+ :ssl_client_key_file => [nil, :path_to_file, 'The corresponding key for the client certificate'],
64
65
  :ssl_ca_file => [nil, :path_to_file, 'A file containing CA one or more certificates'],
65
66
  }
66
67
 
@@ -83,17 +83,7 @@ module RHC
83
83
  end
84
84
 
85
85
  def find_membership_container(opts={})
86
- domain, app =
87
- if options.target.present?
88
- options.target.split(/\//)
89
- elsif options.namespace || options.app
90
- if options.app =~ /\//
91
- options.app.split(/\//)
92
- else
93
- [options.namespace || namespace_context, options.app]
94
- end
95
- end
96
-
86
+ domain, app = discover_domain_and_app
97
87
  if options.team_id.present?
98
88
  rest_client.find_team_by_id(options.team_id)
99
89
  elsif options.team_name.present?
@@ -161,5 +151,17 @@ module RHC
161
151
 
162
152
  domain.name
163
153
  end
154
+
155
+ def discover_domain_and_app
156
+ if options.target.present?
157
+ options.target.split(/\//)
158
+ elsif options.namespace || options.app
159
+ if options.app =~ /\//
160
+ options.app.split(/\//)
161
+ else
162
+ [options.namespace || namespace_context, options.app]
163
+ end
164
+ end
165
+ end
164
166
  end
165
167
  end
@@ -109,6 +109,18 @@ module RHC
109
109
  end
110
110
  end
111
111
 
112
+ class RegionsAndZonesNotSupportedException < Exception
113
+ def initialize(message="Server does not support regions and zones")
114
+ super message, 168
115
+ end
116
+ end
117
+
118
+ class NoRegionConfiguredException < Exception
119
+ def initialize(message="Server doesn't have any regions or zones configured")
120
+ super message, 169
121
+ end
122
+ end
123
+
112
124
  class GitPermissionDenied < GitException; end
113
125
  class GitDirectoryExists < GitException; end
114
126
 
@@ -117,7 +117,7 @@ module RHC
117
117
  global_option '--raw', "Do not format the output from the requested operations.", :hide => true
118
118
  global_option '--always-prefix', "Include the gear prefix on all output from the server.", :hide => true
119
119
 
120
- OptionParser.accept(SSLVersion = Class.new){ |s| OpenSSL::SSL::SSLContext::METHODS.find{ |m| m.to_s.downcase == s.downcase } or raise OptionParser::InvalidOption.new(nil, "The provided SSL version '#{s}' is not valid. Supported values: #{OpenSSL::SSL::SSLContext::METHODS.map(&:to_s).map(&:downcase).join(', ')}") }
120
+ OptionParser.accept(SSLVersion = Class.new){ |s| parse_ssl_version(s) }
121
121
  global_option '--ssl-version VERSION', SSLVersion, "The version of SSL to use", :hide => true do |value|
122
122
  raise RHC::Exception, "You are using an older version of the httpclient gem which prevents the use of --ssl-version. Please run 'gem update httpclient' to install a newer version (2.2.6 or newer)." unless HTTPClient::SSLConfig.method_defined? :ssl_version
123
123
  end
@@ -127,6 +127,9 @@ module RHC
127
127
  global_option '--ssl-client-cert-file FILE', "An SSL x509 client certificate file", :hide => true do |value|
128
128
  debug certificate_file(value)
129
129
  end
130
+ global_option '--ssl-client-key-file FILE', "An RSA client certificate key", :hide => true do |value|
131
+ debug certificate_key(value)
132
+ end
130
133
 
131
134
  global_option('--timeout SECONDS', Integer, 'The timeout for operations') do |value|
132
135
  raise RHC::Exception, "Timeout must be a positive integer" unless value > 0
@@ -150,6 +153,8 @@ module RHC
150
153
  raise OptionParser::InvalidOption.new(nil, "The provided role '#{s}' is not valid. Supported values: #{ROLES.keys.join(', ')}")
151
154
  end
152
155
 
156
+ OptionParser.accept(CertificateFile = Class.new) {|s| certificate_file(s); s}
157
+
153
158
  def role_name(s)
154
159
  ROLES[s.downcase]
155
160
  end
@@ -196,7 +201,6 @@ module RHC
196
201
  def ssl_options
197
202
  {
198
203
  :ssl_version => options.ssl_version,
199
- :client_cert => certificate_file(options.ssl_client_cert),
200
204
  :ca_file => options.ssl_ca_file && File.expand_path(options.ssl_ca_file),
201
205
  :verify_mode => options.insecure ? OpenSSL::SSL::VERIFY_NONE : nil,
202
206
  }.delete_if{ |k,v| v.nil? }
@@ -209,6 +213,16 @@ module RHC
209
213
  raise OptionParser::InvalidOption.new(nil, "The certificate '#{file}' cannot be loaded: #{e.message} (#{e.class})")
210
214
  end
211
215
 
216
+ def certificate_key(file)
217
+ file && OpenSSL::PKey::RSA.new(IO.read(File.expand_path(file)))
218
+ rescue => e
219
+ debug e
220
+ raise OptionParser::InvalidOption.new(nil, "The key '#{file}' cannot be loaded: #{e.message} (#{e.class})")
221
+ end
222
+
223
+ def parse_ssl_version(version)
224
+ OpenSSL::SSL::SSLContext::METHODS.find{ |m| m.to_s.downcase == version.to_s.downcase } or raise OptionParser::InvalidOption.new(nil, "The provided SSL version '#{version}' is not valid. Supported values: #{OpenSSL::SSL::SSLContext::METHODS.map(&:to_s).map(&:downcase).join(', ')}") unless version.nil?
225
+ end
212
226
 
213
227
  #
214
228
  # Output helpers
@@ -281,10 +295,14 @@ module RHC
281
295
 
282
296
  # OVERRIDE: Replaces default commander behavior
283
297
  def color(item, *args)
284
- if item.is_a? Array
285
- item.map{ |i| $terminal.color(i, *args) }
286
- else
287
- $terminal.color(item, *args)
298
+ unless (options.raw rescue false)
299
+ if item.is_a? Array
300
+ item.map{ |i| $terminal.color(i, *args) }
301
+ else
302
+ $terminal.color(item, *args)
303
+ end
304
+ else
305
+ item
288
306
  end
289
307
  end
290
308
 
@@ -341,7 +359,8 @@ module RHC
341
359
  :use_authorization_tokens => 'Use Auth Tokens',
342
360
  :ssl_ca_file => 'SSL Cert CA File',
343
361
  :ssl_version => 'SSL Version',
344
- :ssl_client_cert_file => 'SSL x509 Client Cert File'
362
+ :ssl_client_cert_file => 'SSL x509 Client Cert File',
363
+ :zones => 'Available Zones'
345
364
  })
346
365
 
347
366
  headings[value]
@@ -30,6 +30,7 @@ module RHC
30
30
  :creation_time,
31
31
  (:id if ids),
32
32
  (:allowed_gear_sizes unless domain.allowed_gear_sizes.nil?),
33
+ (:suffix unless domain.suffix.nil? || openshift_online_server?),
33
34
  :compact_members
34
35
  ),
35
36
  :delete => true
@@ -96,6 +97,26 @@ module RHC
96
97
  end
97
98
  end
98
99
 
100
+ def display_region(region)
101
+ paragraph do
102
+ header ["Region '#{region.name}'", "(uuid: #{region.uuid})", ("(default)" if region.default?)], {:color => (:green if region.default?)} do
103
+ section(:bottom => 1) do
104
+ say format_table \
105
+ nil,
106
+ get_properties(
107
+ region,
108
+ :description,
109
+ :zones
110
+ ),
111
+ {
112
+ :delete => true,
113
+ :color => (:green if region.default?)
114
+ }
115
+ end
116
+ end
117
+ end
118
+ end
119
+
99
120
  def format_cart_header(cart)
100
121
  [
101
122
  cart.name,
@@ -18,6 +18,7 @@ module RHC
18
18
  autoload :GearGroup, 'rhc/rest/gear_group'
19
19
  autoload :Key, 'rhc/rest/key'
20
20
  autoload :Membership, 'rhc/rest/membership'
21
+ autoload :Region, 'rhc/rest/region'
21
22
  autoload :Team, 'rhc/rest/team'
22
23
  autoload :User, 'rhc/rest/user'
23
24
 
@@ -10,6 +10,7 @@ module RHC
10
10
  :scalable, :health_check_path, :embedded, :gear_count,
11
11
  :ssh_url, :building_app, :cartridges, :initial_git_url,
12
12
  :auto_deploy, :deployment_branch, :deployment_type, :keep_deployments, :deployments
13
+ :region
13
14
  alias_method :domain_name, :domain_id
14
15
 
15
16
  # Query helper to say consistent with cartridge
@@ -100,6 +101,10 @@ module RHC
100
101
  gear['ssh_url'] or raise NoPerGearOperations
101
102
  end
102
103
 
104
+ def region
105
+ gears.first['region'] rescue nil
106
+ end
107
+
103
108
  def tidy
104
109
  debug "Starting application #{name}"
105
110
  rest_method 'TIDY', :event => "tidy"
@@ -59,6 +59,15 @@ module RHC
59
59
  @cartridges ||= api.rest_method("LIST_CARTRIDGES", nil, :lazy_auth => true)
60
60
  end
61
61
 
62
+ def regions
63
+ debug "Getting all regions and zones available"
64
+ if supports_regions_and_zones?
65
+ @regions ||= api.rest_method("LIST_REGIONS")
66
+ else
67
+ raise RHC::RegionsAndZonesNotSupportedException
68
+ end
69
+ end
70
+
62
71
  def user
63
72
  debug "Getting user info"
64
73
  @user ||= api.rest_method "GET_USER"
@@ -156,6 +165,12 @@ module RHC
156
165
  request(:url => link_show_application_by_domain_name(domain, application, "gear_groups"), :method => "GET", :payload => options)
157
166
  end
158
167
 
168
+ def find_application_gear_groups_endpoints(domain, application, options={})
169
+ precheck_domain_id(domain)
170
+ precheck_application_id(application)
171
+ request(:url => link_show_application_by_domain_name(domain, application, "gear_groups"), :method => "GET", :payload => options.merge(:include => 'endpoints'))
172
+ end
173
+
159
174
  def find_application_aliases(domain, application, options={})
160
175
  precheck_domain_id(domain)
161
176
  precheck_application_id(application)
@@ -266,6 +281,10 @@ module RHC
266
281
  api.supports? 'ADD_AUTHORIZATION'
267
282
  end
268
283
 
284
+ def supports_regions_and_zones?
285
+ api.supports? :LIST_REGIONS
286
+ end
287
+
269
288
  def authorizations
270
289
  raise AuthorizationsNotSupported unless supports_sessions?
271
290
  api.rest_method 'LIST_AUTHORIZATIONS'
@@ -640,6 +659,8 @@ module RHC
640
659
  RHC::Rest::Membership::Member.new(data, self)
641
660
  when 'members'
642
661
  data.map{ |json| RHC::Rest::Membership::Member.new(json, self) }
662
+ when 'regions'
663
+ data.map{ |json| Region.new(json, self) }
643
664
  else
644
665
  data
645
666
  end
@@ -6,7 +6,8 @@ module RHC
6
6
  define_attr :id, # Domain name for API version < 1.6, domain unique id otherwise
7
7
  :name, # Available from API version 1.6 onwards
8
8
  :allowed_gear_sizes, # Available from API version 1.3 onwards on compatible servers
9
- :creation_time # Available from API version 1.3 onwards on compatible servers
9
+ :creation_time, # Available from API version 1.3 onwards on compatible servers
10
+ :suffix
10
11
 
11
12
  def id
12
13
  id_and_name.first
@@ -143,6 +143,8 @@ module RHC::Rest
143
143
 
144
144
  def owner
145
145
  members.find(&:owner?)
146
+ rescue RHC::MembersNotSupported
147
+ nil
146
148
  end
147
149
  end
148
150
  end