rhc 0.68.5
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.
- data/LICENSE +21 -0
- data/README +41 -0
- data/Rakefile +32 -0
- data/bin/rhc-create-app +389 -0
- data/bin/rhc-create-domain +177 -0
- data/bin/rhc-ctl-app +157 -0
- data/bin/rhc-snapshot +119 -0
- data/bin/rhc-user-info +151 -0
- data/conf/express.conf +8 -0
- data/lib/rhc-common.rb +318 -0
- metadata +107 -0
data/bin/rhc-user-info
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright 2011 Red Hat, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person
|
5
|
+
# obtaining a copy of this software and associated documentation files
|
6
|
+
# (the "Software"), to deal in the Software without restriction,
|
7
|
+
# including without limitation the rights to use, copy, modify, merge,
|
8
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
9
|
+
# and to permit persons to whom the Software is furnished to do so,
|
10
|
+
# subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
19
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
20
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "uri"
|
26
|
+
require "net/http"
|
27
|
+
require "net/https"
|
28
|
+
require "getoptlong"
|
29
|
+
require "json"
|
30
|
+
require 'parseconfig'
|
31
|
+
require 'fileutils'
|
32
|
+
require 'rhc-common'
|
33
|
+
|
34
|
+
#
|
35
|
+
# print help
|
36
|
+
#
|
37
|
+
def p_usage
|
38
|
+
rhlogin = get_var('default_rhlogin') ? "Default: #{get_var('default_rhlogin')}" : "(required)"
|
39
|
+
puts <<USAGE
|
40
|
+
|
41
|
+
Usage: rhc-user-info
|
42
|
+
Display information about a user
|
43
|
+
|
44
|
+
-l|--rhlogin rhlogin RHCloud rhlogin (#{rhlogin})
|
45
|
+
-p|--password password RHLogin password (optional, will prompt)
|
46
|
+
-a|--apps List applications for rhlogin
|
47
|
+
-i|--info Show user info
|
48
|
+
-d|--debug Print Debug info
|
49
|
+
-h|--help Show Usage info
|
50
|
+
|
51
|
+
USAGE
|
52
|
+
exit 255
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_email(email)
|
56
|
+
if email =~ /([^@]+)@([a-zA-Z0-9\.])+\.([a-zA-Z]{2,3})/
|
57
|
+
if $1 =~ /[^a-zA-Z0-9\.]/
|
58
|
+
return false
|
59
|
+
else
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
else
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
opts = GetoptLong.new(
|
69
|
+
["--debug", "-d", GetoptLong::NO_ARGUMENT],
|
70
|
+
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
71
|
+
["--apps", "-a", GetoptLong::NO_ARGUMENT],
|
72
|
+
["--info", "-i", GetoptLong::NO_ARGUMENT],
|
73
|
+
["--rhlogin", "-l", GetoptLong::REQUIRED_ARGUMENT],
|
74
|
+
["--password", "-p", GetoptLong::OPTIONAL_ARGUMENT]
|
75
|
+
)
|
76
|
+
opt = {}
|
77
|
+
opts.each do |o, a|
|
78
|
+
opt[o[2..-1]] = a.to_s
|
79
|
+
end
|
80
|
+
rescue Exception => e
|
81
|
+
#puts e.message
|
82
|
+
p_usage
|
83
|
+
end
|
84
|
+
|
85
|
+
# Pull in configs from files
|
86
|
+
libra_server = get_var('libra_server')
|
87
|
+
debug = get_var('debug') == 'false' ? nil : get_var('debug')
|
88
|
+
libra_domain = get_var('libra_domain')
|
89
|
+
|
90
|
+
libra_kfile = "#{ENV['HOME']}/.ssh/libra_id_rsa"
|
91
|
+
libra_kpfile = "#{ENV['HOME']}/.ssh/libra_id_rsa.pub"
|
92
|
+
|
93
|
+
if opt["help"]
|
94
|
+
p_usage
|
95
|
+
end
|
96
|
+
|
97
|
+
if opt["debug"]
|
98
|
+
debug = true
|
99
|
+
end
|
100
|
+
|
101
|
+
opt["rhlogin"] = get_var('default_rhlogin') unless opt["rhlogin"]
|
102
|
+
|
103
|
+
if !RHC::check_rhlogin(opt['rhlogin'])
|
104
|
+
p_usage
|
105
|
+
end
|
106
|
+
|
107
|
+
password = opt['password']
|
108
|
+
if !password
|
109
|
+
password = RHC::get_password
|
110
|
+
end
|
111
|
+
|
112
|
+
opt['apps'] = true if not opt['info'] and not opt['apps']
|
113
|
+
user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, @http, debug, true)
|
114
|
+
|
115
|
+
if user_info['user_info']['uuid'] != get_var(opt["rhlogin"])
|
116
|
+
puts "!! WARNING !! WARNING !! WARNING !! WARNING !!"
|
117
|
+
puts
|
118
|
+
puts "The UUID in your #{@conf_name} file is either missing or incorrect."
|
119
|
+
puts "Please ensure the below line exists in #{@local_config_path}"
|
120
|
+
puts
|
121
|
+
puts "#{opt['rhlogin']}=#{user_info['user_info']['uuid']}"
|
122
|
+
puts
|
123
|
+
puts "!! WARNING !! WARNING !! WARNING !! WARNING !!"
|
124
|
+
end
|
125
|
+
|
126
|
+
if opt['info']
|
127
|
+
puts "User Info"
|
128
|
+
puts "========="
|
129
|
+
puts "Namespace: #{user_info['user_info']['namespace']}"
|
130
|
+
puts " UUID: #{user_info['user_info']['uuid']}"
|
131
|
+
puts " RHLogin: #{user_info['user_info']['rhlogin']}"
|
132
|
+
puts " ssh_key: #{user_info['user_info']['ssh_key']}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if opt['apps']
|
136
|
+
puts "\n\n" if opt['info']
|
137
|
+
|
138
|
+
puts "Application Info"
|
139
|
+
puts "================"
|
140
|
+
user_info['app_info'].each do |key, val|
|
141
|
+
puts key
|
142
|
+
puts " Framework: #{val['framework']}"
|
143
|
+
puts " Creation: #{val['creation_time']}"
|
144
|
+
puts " Git URL: ssh://#{user_info['user_info']['uuid']}@#{key}-#{user_info['user_info']['namespace']}.#{libra_domain}/~/git/#{key}.git/"
|
145
|
+
puts " Public URL: http://#{key}-#{user_info['user_info']['namespace']}.#{libra_domain}/"
|
146
|
+
puts ""
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
exit 0
|
data/conf/express.conf
ADDED
data/lib/rhc-common.rb
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
# Copyright 2010 Red Hat, Inc.
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'fileutils'
|
25
|
+
require 'getoptlong'
|
26
|
+
require 'json'
|
27
|
+
require 'net/http'
|
28
|
+
require 'net/https'
|
29
|
+
require 'parseconfig'
|
30
|
+
require 'resolv'
|
31
|
+
require 'uri'
|
32
|
+
|
33
|
+
|
34
|
+
module RHC
|
35
|
+
|
36
|
+
Maxdlen = 16
|
37
|
+
Maxretries = 10
|
38
|
+
Defaultdelay = 2
|
39
|
+
|
40
|
+
TYPES = {
|
41
|
+
'php-5.3.2' => :php,
|
42
|
+
'rack-1.1.0' => :rack,
|
43
|
+
'wsgi-3.2.1' => :wsgi
|
44
|
+
}
|
45
|
+
|
46
|
+
def self.delay(time, adj=Defaultdelay)
|
47
|
+
(time*=adj).to_int
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.get_cartridge_types(sep=', ')
|
51
|
+
i = 1
|
52
|
+
type_keys = ''
|
53
|
+
TYPES.each_key do |key|
|
54
|
+
type_keys += key
|
55
|
+
if i < TYPES.size
|
56
|
+
type_keys += sep
|
57
|
+
end
|
58
|
+
i += 1
|
59
|
+
end
|
60
|
+
type_keys
|
61
|
+
end
|
62
|
+
|
63
|
+
# Invalid chars (") ($) (^) (<) (>) (|) (%) (/) (;) (:) (,) (\) (*) (=) (~)
|
64
|
+
def self.check_rhlogin(rhlogin)
|
65
|
+
if rhlogin
|
66
|
+
if rhlogin.length < 6
|
67
|
+
puts 'RHLogin must be at least 6 characters'
|
68
|
+
return false
|
69
|
+
elsif rhlogin =~ /["\$\^<>\|%\/;:,\\\*=~]/
|
70
|
+
puts 'RHLogin may not contain any of these characters: (\") ($) (^) (<) (>) (|) (%) (/) (;) (:) (,) (\) (*) (=) (~)'
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
else
|
74
|
+
puts "RHLogin is required"
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.check_app(app)
|
81
|
+
check_field(app, 'application', Maxdlen)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.check_namespace(namespace)
|
85
|
+
check_field(namespace, 'namespace', Maxdlen)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.check_field(field, type, max=0)
|
89
|
+
if field
|
90
|
+
if field =~ /[^0-9a-zA-Z]/
|
91
|
+
puts "#{type} contains non-alphanumeric characters!"
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
if max != 0 && field.length > Maxdlen
|
95
|
+
puts "maximum #{type} size is #{Maxdlen} characters"
|
96
|
+
return false
|
97
|
+
end
|
98
|
+
else
|
99
|
+
puts "#{type} is required"
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.get_cartridge(type)
|
106
|
+
if type
|
107
|
+
if !(RHC::TYPES.has_key?(type))
|
108
|
+
puts 'type must be ' << get_cartridge_types(' or ')
|
109
|
+
else
|
110
|
+
return RHC::TYPES[type]
|
111
|
+
end
|
112
|
+
else
|
113
|
+
puts "Type is required"
|
114
|
+
end
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.print_post_data(h, debug)
|
119
|
+
if (debug)
|
120
|
+
puts 'DEBUG: Submitting form:'
|
121
|
+
h.each do |k,v|
|
122
|
+
if k.to_s != 'password'
|
123
|
+
puts "#{k.to_s}: #{v.to_s}"
|
124
|
+
else
|
125
|
+
print 'password: '
|
126
|
+
for i in (1..v.length)
|
127
|
+
print 'X'
|
128
|
+
end
|
129
|
+
puts ''
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.get_user_info(libra_server, rhlogin, password, net_http, debug, print_result)
|
136
|
+
|
137
|
+
puts "Contacting https://#{libra_server}"
|
138
|
+
data = {'rhlogin' => rhlogin}
|
139
|
+
if debug
|
140
|
+
data['debug'] = "true"
|
141
|
+
end
|
142
|
+
print_post_data(data, debug)
|
143
|
+
json_data = JSON.generate(data)
|
144
|
+
|
145
|
+
url = URI.parse("https://#{libra_server}/app/broker/userinfo")
|
146
|
+
response = http_post(net_http, url, json_data, password)
|
147
|
+
|
148
|
+
unless response.code == '200'
|
149
|
+
if response.code == '404'
|
150
|
+
puts "A user with rhlogin '#{rhlogin}' does not have a registered domain. Be sure to run rhc-create-domain before using the other rhc tools."
|
151
|
+
exit 99
|
152
|
+
elsif response.code == '401'
|
153
|
+
puts "Invalid user credentials"
|
154
|
+
exit 97
|
155
|
+
else
|
156
|
+
print_response_err(response, debug)
|
157
|
+
end
|
158
|
+
exit 254
|
159
|
+
end
|
160
|
+
if print_result
|
161
|
+
print_response_success(response, debug)
|
162
|
+
end
|
163
|
+
begin
|
164
|
+
json_resp = JSON.parse(response.body)
|
165
|
+
user_info = JSON.parse(json_resp['result'].to_s)
|
166
|
+
rescue JSON::ParserError
|
167
|
+
exit 254
|
168
|
+
end
|
169
|
+
user_info
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.get_password
|
173
|
+
password = nil
|
174
|
+
begin
|
175
|
+
print "Password: "
|
176
|
+
system "stty -echo"
|
177
|
+
password = gets.chomp
|
178
|
+
ensure
|
179
|
+
system "stty echo"
|
180
|
+
end
|
181
|
+
puts "\n"
|
182
|
+
password
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.http_post(http, url, json_data, password)
|
186
|
+
req = http::Post.new(url.path)
|
187
|
+
|
188
|
+
req.set_form_data({'json_data' => json_data, 'password' => password})
|
189
|
+
http = http.new(url.host, url.port)
|
190
|
+
http.open_timeout = 10
|
191
|
+
if url.scheme == "https"
|
192
|
+
http.use_ssl = true
|
193
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
194
|
+
end
|
195
|
+
begin
|
196
|
+
response = http.start {|http| http.request(req)}
|
197
|
+
if response.code == '404' && response.content_type == 'text/html'
|
198
|
+
# TODO probably want to remove this at some point
|
199
|
+
puts "!!!! WARNING !!!! WARNING !!!! WARNING !!!!"
|
200
|
+
puts "RHCloud server not found. You might want to try updating your rhc client tools."
|
201
|
+
exit 218
|
202
|
+
end
|
203
|
+
response
|
204
|
+
rescue Exception => e
|
205
|
+
puts "There was a problem communicating with the server. Response message: #{e.message}"
|
206
|
+
puts "If you were disconnected it is possible the operation finished without being able to report success."
|
207
|
+
puts "You can use rhc-user-info and rhc-ctl-app to learn about the status of your user and application(s)."
|
208
|
+
exit 219
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.print_response_err(response, debug)
|
213
|
+
puts "Problem reported from server. Response code was #{response.code}."
|
214
|
+
if (!debug)
|
215
|
+
puts "Re-run with -d for more information."
|
216
|
+
end
|
217
|
+
exit_code = 254
|
218
|
+
if response.content_type == 'application/json'
|
219
|
+
exit_code = print_json_body(response, debug)
|
220
|
+
elsif debug
|
221
|
+
puts "HTTP response from server is #{response.body}"
|
222
|
+
end
|
223
|
+
exit exit_code.nil? ? 666 : exit_code
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.print_response_success(response, debug, always_print_result=false)
|
227
|
+
if debug
|
228
|
+
puts "Response from server:"
|
229
|
+
print_json_body(response, debug)
|
230
|
+
elsif always_print_result
|
231
|
+
print_json_body(response, debug)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.print_json_body(response, debug)
|
236
|
+
json_resp = JSON.parse(response.body);
|
237
|
+
exit_code = json_resp['exit_code']
|
238
|
+
if debug
|
239
|
+
if json_resp['debug']
|
240
|
+
puts ''
|
241
|
+
puts 'DEBUG:'
|
242
|
+
puts json_resp['debug']
|
243
|
+
puts ''
|
244
|
+
puts "Exit Code: #{exit_code}"
|
245
|
+
if (json_resp.length > 3)
|
246
|
+
json_resp.each do |k,v|
|
247
|
+
if (k != 'results' && k != 'debug' && k != 'exit_code')
|
248
|
+
puts "#{k.to_s}: #{v.to_s}"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
if json_resp['result']
|
255
|
+
puts ''
|
256
|
+
puts 'RESULT:'
|
257
|
+
puts json_resp['result']
|
258
|
+
puts ''
|
259
|
+
end
|
260
|
+
exit_code
|
261
|
+
end
|
262
|
+
|
263
|
+
#
|
264
|
+
# Check if host exists
|
265
|
+
#
|
266
|
+
def self.hostexist?(host)
|
267
|
+
dns = Resolv::DNS.new
|
268
|
+
resp = dns.getresources(host, Resolv::DNS::Resource::IN::A)
|
269
|
+
return resp.any?
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
#
|
275
|
+
# Config paths... /etc/openshift/express.conf or $GEM/conf/express.conf -> ~/.openshift/express.conf
|
276
|
+
#
|
277
|
+
# semi-private: Just in case we rename again :)
|
278
|
+
@conf_name = 'express.conf'
|
279
|
+
_linux_cfg = '/etc/openshift/' + @conf_name
|
280
|
+
_gem_cfg = File.join(File.expand_path(File.dirname(__FILE__) + "/../conf"), @conf_name)
|
281
|
+
_home_conf = File.expand_path('~/.openshift')
|
282
|
+
@local_config_path = File.join(_home_conf, @conf_name)
|
283
|
+
@config_path = File.exists?(_linux_cfg) ? _linux_cfg : _gem_cfg
|
284
|
+
|
285
|
+
FileUtils.mkdir_p _home_conf unless File.directory?(_home_conf)
|
286
|
+
if !File.exists?(File.expand_path(@local_config_path)) && File.exists?("#{ENV['HOME']}/.li/libra.conf")
|
287
|
+
print "Moving old-style config file..."
|
288
|
+
FileUtils.cp "#{ENV['HOME']}/.li/libra.conf", File.expand_path(@local_config_path)
|
289
|
+
FileUtils.mv "#{ENV['HOME']}/.li/libra.conf", "#{ENV['HOME']}/.li/libra.conf.deprecated"
|
290
|
+
puts " Done."
|
291
|
+
end
|
292
|
+
|
293
|
+
FileUtils.touch File.expand_path(@local_config_path)
|
294
|
+
|
295
|
+
begin
|
296
|
+
@global_config = ParseConfig.new(@config_path)
|
297
|
+
@local_config = ParseConfig.new(File.expand_path(@local_config_path))
|
298
|
+
rescue Errno::EACCES => e
|
299
|
+
puts "Could not open config file: #{e.message}"
|
300
|
+
exit 253
|
301
|
+
end
|
302
|
+
|
303
|
+
#
|
304
|
+
# Check for proxy environment
|
305
|
+
#
|
306
|
+
if ENV['http_proxy']
|
307
|
+
host, port = ENV['http_proxy'].split(':')
|
308
|
+
@http = Net::HTTP::Proxy(host, port)
|
309
|
+
else
|
310
|
+
@http = Net::HTTP
|
311
|
+
end
|
312
|
+
|
313
|
+
#
|
314
|
+
# Check for local var in ~/.li/libra.conf use it, else use $GEM/../conf/libra.conf
|
315
|
+
#
|
316
|
+
def get_var(var)
|
317
|
+
@local_config.get_value(var) ? @local_config.get_value(var) : @global_config.get_value(var)
|
318
|
+
end
|