rhc 1.2.7 → 1.3.8
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/bin/rhc +6 -8
- data/bin/rhc-chk +23 -10
- data/features/domain.feature +1 -1
- data/features/lib/rhc_helper.rb +3 -2
- data/features/lib/rhc_helper/api.rb +7 -0
- data/features/lib/rhc_helper/app.rb +8 -10
- data/features/lib/rhc_helper/domain.rb +2 -1
- data/features/lib/rhc_helper/runnable.rb +2 -24
- data/features/sshkey.feature +3 -3
- data/features/step_definitions/cartridge_steps.rb +6 -6
- data/features/step_definitions/client_steps.rb +0 -1
- data/features/step_definitions/sshkey_steps.rb +2 -2
- data/features/support/before_hooks.rb +0 -1
- data/features/support/env.rb +5 -3
- data/lib/rhc-common.rb +1 -1
- data/lib/rhc.rb +9 -8
- data/lib/rhc/auth.rb +3 -0
- data/lib/rhc/auth/basic.rb +54 -0
- data/lib/rhc/cartridge_helpers.rb +11 -5
- data/lib/rhc/cli.rb +4 -2
- data/lib/rhc/command_runner.rb +35 -30
- data/lib/rhc/commands.rb +127 -18
- data/lib/rhc/commands/account.rb +24 -0
- data/lib/rhc/commands/alias.rb +1 -1
- data/lib/rhc/commands/app.rb +210 -209
- data/lib/rhc/commands/apps.rb +22 -0
- data/lib/rhc/commands/base.rb +10 -77
- data/lib/rhc/commands/cartridge.rb +35 -35
- data/lib/rhc/commands/domain.rb +20 -13
- data/lib/rhc/commands/git_clone.rb +30 -0
- data/lib/rhc/commands/{port-forward.rb → port_forward.rb} +3 -3
- data/lib/rhc/commands/server.rb +28 -16
- data/lib/rhc/commands/setup.rb +18 -1
- data/lib/rhc/commands/snapshot.rb +4 -4
- data/lib/rhc/commands/sshkey.rb +4 -18
- data/lib/rhc/commands/tail.rb +32 -9
- data/lib/rhc/config.rb +168 -99
- data/lib/rhc/context_helper.rb +22 -9
- data/lib/rhc/core_ext.rb +41 -1
- data/lib/rhc/exceptions.rb +21 -5
- data/lib/rhc/git_helpers.rb +81 -0
- data/lib/rhc/help_formatter.rb +21 -1
- data/lib/rhc/helpers.rb +222 -87
- data/lib/rhc/output_helpers.rb +94 -110
- data/lib/rhc/rest.rb +15 -198
- data/lib/rhc/rest/api.rb +88 -0
- data/lib/rhc/rest/application.rb +29 -30
- data/lib/rhc/rest/attributes.rb +27 -0
- data/lib/rhc/rest/base.rb +29 -33
- data/lib/rhc/rest/cartridge.rb +42 -20
- data/lib/rhc/rest/client.rb +351 -89
- data/lib/rhc/rest/domain.rb +7 -13
- data/lib/rhc/rest/gear_group.rb +1 -1
- data/lib/rhc/rest/key.rb +7 -2
- data/lib/rhc/rest/mock.rb +609 -0
- data/lib/rhc/rest/user.rb +6 -2
- data/lib/rhc/{ssh_key_helpers.rb → ssh_helpers.rb} +58 -28
- data/lib/rhc/{targz.rb → tar_gz.rb} +0 -0
- data/lib/rhc/usage_templates/command_help.erb +4 -1
- data/lib/rhc/usage_templates/help.erb +24 -11
- data/lib/rhc/usage_templates/options_help.erb +14 -0
- data/lib/rhc/wizard.rb +283 -213
- data/spec/keys/example.pem +23 -0
- data/spec/keys/example_private.pem +27 -0
- data/spec/keys/server.pem +19 -0
- data/spec/rest_spec_helper.rb +3 -371
- data/spec/rhc/auth_spec.rb +226 -0
- data/spec/rhc/cli_spec.rb +41 -14
- data/spec/rhc/command_spec.rb +44 -15
- data/spec/rhc/commands/account_spec.rb +41 -0
- data/spec/rhc/commands/alias_spec.rb +16 -15
- data/spec/rhc/commands/app_spec.rb +115 -92
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/cartridge_spec.rb +134 -112
- data/spec/rhc/commands/domain_spec.rb +31 -86
- data/spec/rhc/commands/git_clone_spec.rb +56 -0
- data/spec/rhc/commands/{port-forward_spec.rb → port_forward_spec.rb} +27 -32
- data/spec/rhc/commands/server_spec.rb +28 -3
- data/spec/rhc/commands/setup_spec.rb +29 -11
- data/spec/rhc/commands/snapshot_spec.rb +4 -3
- data/spec/rhc/commands/sshkey_spec.rb +24 -56
- data/spec/rhc/commands/tail_spec.rb +26 -9
- data/spec/rhc/commands/threaddump_spec.rb +12 -11
- data/spec/rhc/config_spec.rb +211 -164
- data/spec/rhc/context_spec.rb +2 -0
- data/spec/rhc/helpers_spec.rb +242 -46
- data/spec/rhc/rest_application_spec.rb +42 -28
- data/spec/rhc/rest_client_spec.rb +110 -93
- data/spec/rhc/rest_spec.rb +220 -131
- data/spec/rhc/targz_spec.rb +1 -1
- data/spec/rhc/wizard_spec.rb +435 -624
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +140 -6
- data/spec/wizard_spec_helper.rb +326 -0
- metadata +163 -143
- data/lib/rhc/client.rb +0 -17
- data/lib/rhc/git_helper.rb +0 -59
data/lib/rhc/context_helper.rb
CHANGED
@@ -1,30 +1,43 @@
|
|
1
|
-
require 'rhc/
|
1
|
+
require 'rhc/git_helpers'
|
2
2
|
|
3
3
|
module RHC
|
4
|
+
#
|
5
|
+
# Methods in this module should not attempt to read from the options hash
|
6
|
+
# in a recursive manner (server_context can't read options.server).
|
7
|
+
#
|
4
8
|
module ContextHelpers
|
5
9
|
include RHC::GitHelpers
|
6
10
|
|
11
|
+
def server_context
|
12
|
+
ENV['LIBRA_SERVER'] || (!options.clean && config['libra_server']) || "openshift.redhat.com"
|
13
|
+
end
|
14
|
+
|
7
15
|
def app_context
|
8
16
|
debug "Getting app context"
|
9
17
|
|
18
|
+
name = git_config_get "rhc.app-name"
|
19
|
+
return name if name.present?
|
20
|
+
|
10
21
|
uuid = git_config_get "rhc.app-uuid"
|
11
22
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
if uuid.present?
|
24
|
+
# proof of concept - we shouldn't be traversing
|
25
|
+
# the broker should expose apis for getting the application via a uuid
|
26
|
+
rest_client.domains.each do |rest_domain|
|
27
|
+
rest_domain.applications.each do |rest_app|
|
28
|
+
return rest_app.name if rest_app.uuid == uuid
|
29
|
+
end
|
17
30
|
end
|
18
|
-
end
|
19
31
|
|
20
|
-
|
32
|
+
debug "Couldn't find app with UUID == #{uuid}"
|
33
|
+
end
|
21
34
|
nil
|
22
35
|
end
|
23
36
|
|
24
37
|
def namespace_context
|
25
38
|
# right now we don't have any logic since we only support one domain
|
26
39
|
# TODO: add domain lookup based on uuid
|
27
|
-
domain = rest_client.domains
|
40
|
+
domain = rest_client.domains.first
|
28
41
|
raise RHC::DomainNotFoundException, "No domains configured for this user. You may create one using 'rhc domain create'." if domain.nil?
|
29
42
|
|
30
43
|
domain.id
|
data/lib/rhc/core_ext.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'rhc/json'
|
3
3
|
require 'open-uri'
|
4
4
|
require 'highline'
|
5
|
+
require 'httpclient'
|
5
6
|
|
6
7
|
class Object
|
7
8
|
def present?
|
@@ -11,6 +12,10 @@ class Object
|
|
11
12
|
respond_to?(:empty?) ? empty? : !self
|
12
13
|
end
|
13
14
|
|
15
|
+
def presence
|
16
|
+
present? ? self : nil
|
17
|
+
end
|
18
|
+
|
14
19
|
# Avoid a conflict if to_json is already defined
|
15
20
|
unless Object.new.respond_to? :to_json
|
16
21
|
def to_json(options=nil)
|
@@ -34,6 +39,16 @@ class String
|
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
42
|
+
unless HTTP::Message.method_defined? :ok?
|
43
|
+
#:nocov:
|
44
|
+
class HTTP::Message
|
45
|
+
def ok?
|
46
|
+
HTTP::Status.successful?(status)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
#:nocov:
|
50
|
+
end
|
51
|
+
|
37
52
|
#
|
38
53
|
# Allow http => https redirection, see
|
39
54
|
# http://bugs.ruby-lang.org/issues/859 to 1.8.7 for rough
|
@@ -49,6 +64,32 @@ module OpenURI
|
|
49
64
|
end
|
50
65
|
end
|
51
66
|
|
67
|
+
class Hash
|
68
|
+
def stringify_keys!
|
69
|
+
keys.each do |key|
|
70
|
+
v = delete(key)
|
71
|
+
if v.is_a? Hash
|
72
|
+
v.stringify_keys!
|
73
|
+
elsif v.is_a? Array
|
74
|
+
v.each{ |value| value.stringify_keys! if value.is_a? Hash }
|
75
|
+
end
|
76
|
+
self[(key.to_s rescue key) || key] = v
|
77
|
+
end
|
78
|
+
self
|
79
|
+
end
|
80
|
+
def slice!(*args)
|
81
|
+
s = []
|
82
|
+
args.inject([]) do |a, k|
|
83
|
+
s << [k, delete(k)] if has_key?(k)
|
84
|
+
end
|
85
|
+
s
|
86
|
+
end
|
87
|
+
def reverse_merge!(other_hash)
|
88
|
+
# right wins if there is no left
|
89
|
+
merge!( other_hash ){|key,left,right| left }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
52
93
|
# Some versions of highline get in an infinite loop when trying to wrap.
|
53
94
|
# Fixes BZ 866530.
|
54
95
|
class HighLine
|
@@ -122,5 +163,4 @@ class HighLine
|
|
122
163
|
|
123
164
|
return wrapped_text.join("\n")
|
124
165
|
end
|
125
|
-
|
126
166
|
end
|
data/lib/rhc/exceptions.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
module RHC
|
2
2
|
class Exception < StandardError
|
3
3
|
attr_reader :code
|
4
|
-
def initialize(message=nil, code=
|
4
|
+
def initialize(message=nil, code=1)
|
5
5
|
super(message)
|
6
6
|
@code = code
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
class ConfirmationError < Exception
|
11
|
+
def initialize(message="This action requires the --confirm option (or entering 'yes' at a prompt) to run.", code=1)
|
12
|
+
super(message, code)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
class DomainNotFoundException < Exception
|
11
17
|
def initialize(message="Domain not found")
|
12
18
|
super message, 127
|
@@ -37,14 +43,15 @@ module RHC
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
|
-
# Makes sense to use its own exit code since this is different from a
|
41
|
-
# resource error
|
42
46
|
class GitException < Exception
|
43
47
|
def initialize(message="Git returned an error")
|
44
48
|
super message, 216
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
52
|
+
class GitPermissionDenied < GitException; end
|
53
|
+
class GitDirectoryExists < GitException; end
|
54
|
+
|
48
55
|
class DeprecatedError < RuntimeError; end
|
49
56
|
|
50
57
|
class KeyFileNotExistentException < Exception
|
@@ -97,13 +104,22 @@ module RHC
|
|
97
104
|
|
98
105
|
class MissingScalingValueException < Exception
|
99
106
|
def initialize(message="Must provide either a min or max value for scaling")
|
100
|
-
super message
|
107
|
+
super message
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
104
111
|
class CartridgeNotScalableException < Exception
|
105
112
|
def initialize(message="Cartridge is not scalable")
|
106
|
-
super message
|
113
|
+
super message
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class ConnectionFailed < Exception
|
118
|
+
end
|
119
|
+
|
120
|
+
class SSHConnectionRefused < ConnectionFailed
|
121
|
+
def initialize(host, user)
|
122
|
+
super "The server #{host} refused a connection with user #{user}. The application may be unavailable.", 1
|
107
123
|
end
|
108
124
|
end
|
109
125
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'open4'
|
2
|
+
|
3
|
+
module RHC
|
4
|
+
module GitHelpers
|
5
|
+
def git_version
|
6
|
+
@git_version ||= `git --version 2>&1`.strip #:nocov:
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_git?
|
10
|
+
@has_git ||= begin
|
11
|
+
@git_version = nil
|
12
|
+
git_version
|
13
|
+
$?.success?
|
14
|
+
rescue
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def git_clone_application(app)
|
20
|
+
repo_dir = options.repo || app.name
|
21
|
+
|
22
|
+
debug "Pulling new repo down"
|
23
|
+
git_clone_repo(app.git_url, repo_dir)
|
24
|
+
|
25
|
+
debug "Configuring git repo"
|
26
|
+
Dir.chdir(repo_dir) do |dir|
|
27
|
+
git_config_set "rhc.app-uuid", app.uuid
|
28
|
+
git_config_set "rhc.app-name", app.name
|
29
|
+
git_config_set "rhc.domain-name", app.domain_id
|
30
|
+
end
|
31
|
+
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# :nocov: These all call external binaries so test them in cucumber
|
36
|
+
def git_config_get(key)
|
37
|
+
config_get_cmd = "git config --get #{key}"
|
38
|
+
debug "Running #{config_get_cmd}"
|
39
|
+
uuid = %x[#{config_get_cmd}].strip
|
40
|
+
debug "UUID = '#{uuid}'"
|
41
|
+
uuid = nil if $?.exitstatus != 0 or uuid.empty?
|
42
|
+
|
43
|
+
uuid
|
44
|
+
end
|
45
|
+
|
46
|
+
def git_config_set(key, value)
|
47
|
+
unset_cmd = "git config --unset-all #{key}"
|
48
|
+
config_cmd = "git config --add #{key} #{value}"
|
49
|
+
debug "Adding #{key} = #{value} to git config"
|
50
|
+
commands = [unset_cmd, config_cmd]
|
51
|
+
commands.each do |cmd|
|
52
|
+
debug "Running #{cmd} 2>&1"
|
53
|
+
output = %x[#{cmd} 2>&1]
|
54
|
+
raise RHC::GitException, "Error while adding config values to git - #{output}" unless output.empty?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# :nocov:
|
58
|
+
|
59
|
+
def git_clone_repo(git_url, repo_dir)
|
60
|
+
# quote the repo to avoid input injection risk
|
61
|
+
destination = (repo_dir ? " \"#{repo_dir}\"" : "")
|
62
|
+
cmd = "git clone #{git_url}#{destination}"
|
63
|
+
debug "Running #{cmd}"
|
64
|
+
|
65
|
+
status, stdout, stderr = run_with_tee(cmd)
|
66
|
+
|
67
|
+
if status != 0
|
68
|
+
case stderr
|
69
|
+
when /fatal: destination path '[^']*' already exists and is not an empty directory./
|
70
|
+
raise RHC::GitDirectoryExists, "The directory you are cloning into already exists."
|
71
|
+
when /^Permission denied \(.*?publickey.*?\).$/
|
72
|
+
raise RHC::GitPermissionDenied, "You don't have permission to access this repository. Check that your SSH public keys are correct."
|
73
|
+
else
|
74
|
+
raise RHC::GitException, "Unable to clone your repository. Called Git with: #{cmd}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
success "Your application code is now in '#{repo_dir}'"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/rhc/help_formatter.rb
CHANGED
@@ -8,6 +8,26 @@ module RHC
|
|
8
8
|
def render_command_syntax command
|
9
9
|
template(:command_syntax_help).result command.get_binding
|
10
10
|
end
|
11
|
+
def render_options runner
|
12
|
+
template(:options_help).result runner.get_binding
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class CommandHelpBindings
|
17
|
+
def initialize(command, instance_commands, runner)
|
18
|
+
@command = command
|
19
|
+
@actions = instance_commands.collect do |command_name, command_class|
|
20
|
+
next if command_class.summary.nil?
|
21
|
+
m = /^#{command.name} ([^ ]+)/.match(command_name)
|
22
|
+
# if we have a match and it is not an alias then we can use it
|
23
|
+
m and command_name == command_class.name ? {:name => m[1], :summary => command_class.summary || ""} : nil
|
24
|
+
end
|
25
|
+
@actions.compact!
|
26
|
+
@global_options = runner.options
|
27
|
+
@runner = runner
|
28
|
+
end
|
29
|
+
def program(*args)
|
30
|
+
@runner.program *args
|
31
|
+
end
|
11
32
|
end
|
12
|
-
# TODO: class ManPageHelpFormatter
|
13
33
|
end
|
data/lib/rhc/helpers.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'commander/user_interaction'
|
2
2
|
require 'rhc/version'
|
3
3
|
require 'rhc/config'
|
4
|
-
require 'rhc/commands'
|
5
4
|
require 'rhc/output_helpers'
|
6
5
|
require 'rbconfig'
|
7
6
|
|
@@ -47,12 +46,14 @@ module RHC
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def date(s)
|
50
|
-
now =
|
49
|
+
now = Date.today
|
51
50
|
d = datetime_rfc3339(s)
|
52
51
|
if now.year == d.year
|
53
52
|
return d.strftime('%l:%M %p').strip if now.yday == d.yday
|
53
|
+
d.strftime('%b %d %l:%M %p')
|
54
|
+
else
|
55
|
+
d.strftime('%b %d, %Y %l:%M %p')
|
54
56
|
end
|
55
|
-
d.strftime('%b %d %l:%M %p')
|
56
57
|
rescue ArgumentError
|
57
58
|
"Unknown date"
|
58
59
|
end
|
@@ -70,38 +71,86 @@ module RHC
|
|
70
71
|
"rhc/#{RHC::VERSION::STRING} (ruby #{RUBY_VERSION}; #{RUBY_PLATFORM})#{" (API #{RHC::Rest::API_VERSION})" rescue ''}"
|
71
72
|
end
|
72
73
|
|
73
|
-
def get(uri, opts=nil, *args)
|
74
|
-
opts = {'User-Agent' => user_agent}.merge(opts || {})
|
75
|
-
RestClient.get(uri, opts, *args)
|
76
|
-
end
|
77
|
-
|
78
74
|
#
|
79
75
|
# Global config
|
80
76
|
#
|
77
|
+
global_option '-l', '--rhlogin LOGIN', "OpenShift login"
|
78
|
+
global_option '-p', '--password PASSWORD', "OpenShift password"
|
79
|
+
global_option '-d', '--debug', "Turn on debugging", :hide => true
|
81
80
|
|
82
|
-
global_option '
|
83
|
-
global_option '-
|
84
|
-
global_option '-d', '--debug', "Turn on debugging"
|
81
|
+
global_option '--server NAME', String, 'An OpenShift server hostname (default: openshift.redhat.com)'
|
82
|
+
global_option '-k', '--insecure', "Allow insecure SSL connections. Potential security risk.", :hide => true
|
85
83
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
$rest_timeout = value
|
84
|
+
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(', ')}") }
|
85
|
+
global_option '--ssl-version VERSION', SSLVersion, "The version of SSL to use", :hide => true do |value|
|
86
|
+
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
|
90
87
|
end
|
91
|
-
global_option '--
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
global_option '--ssl-ca-file FILE', "An SSL certificate CA file (may contain multiple certs)", :hide => true do |value|
|
89
|
+
debug certificate_file(value)
|
90
|
+
end
|
91
|
+
global_option '--ssl-client-cert-file FILE', "An SSL x509 client certificate file", :hide => true do |value|
|
92
|
+
debug certificate_file(value)
|
93
|
+
end
|
94
|
+
|
95
|
+
global_option('--timeout SECONDS', Integer, 'The timeout for operations') do |value|
|
96
|
+
raise RHC::Exception, "Timeout must be a positive integer" unless value > 0
|
97
|
+
end
|
98
|
+
global_option '--noprompt', "Suppress all interactive operations command", :hide => true do
|
99
|
+
$terminal.page_at = nil
|
100
|
+
end
|
101
|
+
global_option '--config FILE', "Path of a different config file", :hide => true
|
102
|
+
global_option '--clean', "Ignore any saved configuration options", :hide => true
|
103
|
+
global_option '--mock', "Run in mock mode", :hide => true do
|
104
|
+
#:nocov:
|
105
|
+
require 'rhc/rest/mock'
|
106
|
+
RHC::Rest::Mock.start
|
107
|
+
#:nocov:
|
95
108
|
end
|
96
109
|
|
97
110
|
def openshift_server
|
98
|
-
|
111
|
+
to_host((options.server rescue nil) || ENV['LIBRA_SERVER'] || "openshift.redhat.com")
|
112
|
+
end
|
113
|
+
def openshift_online_server?
|
114
|
+
openshift_server =~ /openshift.redhat.com$/i
|
99
115
|
end
|
100
116
|
def openshift_url
|
101
117
|
"https://#{openshift_server}"
|
102
118
|
end
|
103
|
-
|
104
|
-
|
119
|
+
|
120
|
+
def to_host(s)
|
121
|
+
s =~ %r(^http(?:s)?://) ? URI(s).host : s
|
122
|
+
end
|
123
|
+
def to_uri(s)
|
124
|
+
URI(s =~ %r(^http(?:s)?://) ? s : "https://#{s}")
|
125
|
+
end
|
126
|
+
def openshift_rest_endpoint
|
127
|
+
uri = to_uri((options.server rescue nil) || ENV['LIBRA_SERVER'] || "openshift.redhat.com")
|
128
|
+
uri.path = '/broker/rest/api' if uri.path.blank? || uri.path == '/'
|
129
|
+
uri
|
130
|
+
end
|
131
|
+
|
132
|
+
def client_from_options(opts)
|
133
|
+
RHC::Rest::Client.new({
|
134
|
+
:url => openshift_rest_endpoint.to_s,
|
135
|
+
:debug => options.debug,
|
136
|
+
:timeout => options.timeout,
|
137
|
+
}.merge!(ssl_options).merge!(opts))
|
138
|
+
end
|
139
|
+
|
140
|
+
def ssl_options
|
141
|
+
{
|
142
|
+
:ssl_version => options.ssl_version,
|
143
|
+
:client_cert => certificate_file(options.ssl_client_cert),
|
144
|
+
:ca_file => options.ssl_ca_file && File.expand_path(options.ssl_ca_file),
|
145
|
+
:verify_mode => options.insecure ? OpenSSL::SSL::VERIFY_NONE : nil,
|
146
|
+
}.delete_if{ |k,v| v.nil? }
|
147
|
+
end
|
148
|
+
|
149
|
+
def certificate_file(file)
|
150
|
+
file && OpenSSL::X509::Certificate.new(IO.read(File.expand_path(file)))
|
151
|
+
rescue => e
|
152
|
+
debug e
|
153
|
+
raise OptionParser::InvalidOption.new(nil, "The certificate '#{file}' cannot be loaded: #{e.message} (#{e.class})")
|
105
154
|
end
|
106
155
|
|
107
156
|
#
|
@@ -111,6 +160,9 @@ module RHC
|
|
111
160
|
def debug(msg)
|
112
161
|
$stderr.puts "DEBUG: #{msg}" if debug?
|
113
162
|
end
|
163
|
+
def debug?
|
164
|
+
false
|
165
|
+
end
|
114
166
|
|
115
167
|
def deprecated_command(correct,short = false)
|
116
168
|
deprecated("This command is deprecated. Please use '#{correct}' instead.",short)
|
@@ -123,25 +175,61 @@ module RHC
|
|
123
175
|
def deprecated(msg,short = false)
|
124
176
|
HighLine::use_color = false if windows? # handle deprecated commands that does not start through highline
|
125
177
|
|
126
|
-
info = " For porting and testing purposes you may switch this %s to %s by setting the DISABLE_DEPRECATED environment variable to %d. It is not recommended to do so in a production environment as this option
|
127
|
-
|
178
|
+
info = " For porting and testing purposes you may switch this %s to %s by setting the DISABLE_DEPRECATED environment variable to %d. It is not recommended to do so in a production environment as this option will be removed in a future release."
|
128
179
|
msg << info unless short
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
180
|
+
|
181
|
+
raise DeprecatedError.new(msg % ['an error','a warning',0]) if disable_deprecated?
|
182
|
+
|
183
|
+
warn "Warning: #{msg}\n" % ['a warning','an error',1]
|
134
184
|
end
|
135
185
|
|
186
|
+
@@indent = 0
|
187
|
+
@@last_line_open = false
|
136
188
|
def say(msg, *args)
|
137
|
-
if Hash[*args][:stderr]
|
138
|
-
|
139
|
-
|
140
|
-
|
189
|
+
output = if Hash[*args][:stderr]
|
190
|
+
$stderr
|
191
|
+
else
|
192
|
+
separate_blocks
|
193
|
+
$terminal.instance_variable_get(:@output)
|
194
|
+
end
|
195
|
+
|
196
|
+
Array(msg).each do |statement|
|
197
|
+
statement = statement.to_str
|
198
|
+
next unless statement.present?
|
199
|
+
|
200
|
+
template = ERB.new(statement, nil, "%")
|
201
|
+
statement = template.result(binding)
|
202
|
+
|
203
|
+
statement = $terminal.wrap(statement) unless $terminal.instance_variable_get(:@wrap_at).nil?
|
204
|
+
statement = $terminal.send(:page_print, statement) unless $terminal.instance_variable_get(:@page_at).nil?
|
205
|
+
|
206
|
+
output.print(' ' * @@indent * INDENT) unless @@last_line_open
|
207
|
+
|
208
|
+
@@last_line_open =
|
209
|
+
if statement[-1, 1] == " " or statement[-1, 1] == "\t"
|
210
|
+
output.print(statement)
|
211
|
+
output.flush
|
212
|
+
else
|
213
|
+
output.puts(statement)
|
214
|
+
end
|
141
215
|
end
|
216
|
+
|
142
217
|
msg
|
143
218
|
end
|
144
219
|
|
220
|
+
[:ask, :agree].each do |sym|
|
221
|
+
define_method(sym) do |*args, &block|
|
222
|
+
separate_blocks
|
223
|
+
super(*args, &block)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def confirm_action(question)
|
228
|
+
return if options.confirm
|
229
|
+
return if !options.noprompt && paragraph{ agree("#{question} (yes|no): ") }
|
230
|
+
raise RHC::ConfirmationError
|
231
|
+
end
|
232
|
+
|
145
233
|
def success(msg, *args)
|
146
234
|
say color(msg, :green), *args
|
147
235
|
end
|
@@ -166,6 +254,14 @@ module RHC
|
|
166
254
|
count == 1 ? "#{count} #{s}" : "#{count} #{s}s"
|
167
255
|
end
|
168
256
|
|
257
|
+
## report a result (true/false) and return result
|
258
|
+
## if the result is false, msg is displayed
|
259
|
+
def report_result(result, msg, fatal = true)
|
260
|
+
result ? $terminal.instance_variable_get(:@output).print('.') : error(msg)
|
261
|
+
# ignore the result if non-fatal
|
262
|
+
fatal ? result : true
|
263
|
+
end
|
264
|
+
|
169
265
|
# given an array of arrays "items", construct an array of strings that can
|
170
266
|
# be used to print in tabular form.
|
171
267
|
def table(items, opts={}, &block)
|
@@ -174,7 +270,7 @@ module RHC
|
|
174
270
|
items.each do |item|
|
175
271
|
item.each_with_index do |s, i|
|
176
272
|
item[i] = s.to_s
|
177
|
-
widths[i] = [widths[i] || 0,
|
273
|
+
widths[i] = [widths[i] || 0, item[i].length].max
|
178
274
|
end
|
179
275
|
end
|
180
276
|
align = opts[:align] || []
|
@@ -186,7 +282,7 @@ module RHC
|
|
186
282
|
items.unshift(opts[:header])
|
187
283
|
end
|
188
284
|
items.map do |item|
|
189
|
-
item.each_with_index.map{ |s,i| s.send((align[i] == :right ? :rjust : :ljust), widths[i], ' ') }.join(join).
|
285
|
+
item.each_with_index.map{ |s,i| s.send((align[i] == :right ? :rjust : :ljust), widths[i], ' ') }.join(join).rstrip
|
190
286
|
end
|
191
287
|
end
|
192
288
|
|
@@ -212,29 +308,63 @@ module RHC
|
|
212
308
|
:current_scale => "Current",
|
213
309
|
:scales_from => "Minimum",
|
214
310
|
:scales_to => "Maximum",
|
311
|
+
:gear_sizes => "Allowed Gear Sizes",
|
312
|
+
:consumed_gears => "Gears Used",
|
313
|
+
:max_gears => "Gears Allowed",
|
314
|
+
:gear_info => "Gears",
|
315
|
+
:plan_id => "Plan",
|
215
316
|
:url => "URL",
|
216
|
-
:
|
217
|
-
:
|
317
|
+
:ssh_string => "SSH",
|
318
|
+
:connection_info => "Connection URL",
|
319
|
+
:gear_profile => "Gear Size",
|
320
|
+
:visible_to_ssh? => 'Available',
|
218
321
|
})
|
219
322
|
|
220
323
|
headings[value]
|
221
324
|
end
|
222
325
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
326
|
+
class StringTee < StringIO
|
327
|
+
attr_reader :tee
|
328
|
+
def initialize(other)
|
329
|
+
@tee = other
|
330
|
+
super()
|
331
|
+
end
|
332
|
+
def <<(buf)
|
333
|
+
tee << buf
|
334
|
+
super
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
#def tee(&block)
|
339
|
+
# original = [$stdout, $stderr]
|
340
|
+
# $stdout, $stderr = (tees = original.map{ |io| StringTee.new(io) })
|
341
|
+
# yield
|
342
|
+
#ensure
|
343
|
+
# $stdout, $stderr = original
|
344
|
+
# tees.each(&:close_write).map(&:string)
|
345
|
+
#end
|
346
|
+
|
347
|
+
def header(str,opts = {}, &block)
|
348
|
+
str = underline(str)
|
349
|
+
str = str.map{ |s| color(s, opts[:color]) } if opts[:color]
|
350
|
+
say str
|
227
351
|
if block_given?
|
228
|
-
|
229
|
-
yield
|
230
|
-
@indent -= 1
|
352
|
+
indent &block
|
231
353
|
end
|
232
354
|
end
|
233
355
|
|
356
|
+
def underline(s)
|
357
|
+
[s, "-"*s.length]
|
358
|
+
end
|
359
|
+
|
234
360
|
INDENT = 2
|
235
|
-
def indent(
|
236
|
-
|
237
|
-
|
361
|
+
def indent(&block)
|
362
|
+
@@indent += 1
|
363
|
+
begin
|
364
|
+
yield
|
365
|
+
ensure
|
366
|
+
@@indent -= 1
|
367
|
+
end
|
238
368
|
end
|
239
369
|
|
240
370
|
##
|
@@ -265,37 +395,21 @@ module RHC
|
|
265
395
|
# top - top margin specified in lines
|
266
396
|
# bottom - bottom margin specified in line
|
267
397
|
#
|
268
|
-
@@
|
398
|
+
@@margin = nil
|
269
399
|
def section(params={}, &block)
|
270
|
-
top = params[:top]
|
271
|
-
|
272
|
-
bottom = params[:bottom]
|
273
|
-
bottom = 0 if bottom.nil?
|
274
|
-
|
275
|
-
# add more newlines if top is greater than the last section's bottom margin
|
276
|
-
top_margin = @@section_bottom_last
|
277
|
-
|
278
|
-
# negitive previous bottoms indicate that an untracked newline was
|
279
|
-
# printed and so we do our best to negate it since we can't remove it
|
280
|
-
if top_margin < 0
|
281
|
-
top += top_margin
|
282
|
-
top_margin = 0
|
283
|
-
end
|
400
|
+
top = params[:top] || 0
|
401
|
+
bottom = params[:bottom] || 0
|
284
402
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
403
|
+
# the first section cannot take a newline
|
404
|
+
top = 0 unless @@margin
|
405
|
+
@@margin = [top, @@margin || 0].max
|
289
406
|
|
290
|
-
block.call
|
407
|
+
value = block.call
|
291
408
|
|
292
|
-
|
293
|
-
|
294
|
-
say "\n"
|
295
|
-
bottom_margin += 1
|
296
|
-
end
|
409
|
+
say "\n" if @@last_line_open
|
410
|
+
@@margin = [bottom, @@margin].max
|
297
411
|
|
298
|
-
|
412
|
+
value
|
299
413
|
end
|
300
414
|
|
301
415
|
##
|
@@ -314,7 +428,7 @@ module RHC
|
|
314
428
|
# to distinguish the final results of a command from other output
|
315
429
|
#
|
316
430
|
def results(&block)
|
317
|
-
|
431
|
+
section(:top => 1, :bottom => 0) do
|
318
432
|
say "RESULT:"
|
319
433
|
yield
|
320
434
|
end
|
@@ -326,14 +440,9 @@ module RHC
|
|
326
440
|
def unix? ; !jruby? && !windows? end
|
327
441
|
def mac? ; RbConfig::CONFIG['host_os'] =~ /^darwin/ end
|
328
442
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
Name: <%= key.name %>
|
333
|
-
Type: <%= key.type %>
|
334
|
-
Fingerprint: <%= key.fingerprint %>
|
335
|
-
|
336
|
-
FORMAT
|
443
|
+
def system_path(path)
|
444
|
+
return path.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if File.const_defined?('ALT_SEPARATOR') and File::ALT_SEPARATOR.present?
|
445
|
+
path
|
337
446
|
end
|
338
447
|
|
339
448
|
#
|
@@ -346,15 +455,41 @@ Fingerprint: <%= key.fingerprint %>
|
|
346
455
|
dns.getresources(host, Resolv::DNS::Resource::IN::A).any?
|
347
456
|
# :nocov:
|
348
457
|
end
|
349
|
-
|
458
|
+
|
350
459
|
def hosts_file_contains?(host)
|
351
460
|
# :nocov:
|
352
461
|
resolver = Resolv::Hosts.new
|
353
|
-
|
354
|
-
|
355
|
-
rescue Resolv::ResolvError
|
356
|
-
end
|
462
|
+
resolver.getaddress host
|
463
|
+
rescue Resolv::ResolvError
|
357
464
|
# :nocov:
|
358
465
|
end
|
466
|
+
|
467
|
+
# Run a command and export its output to the user. Output is not capturable
|
468
|
+
# on all platforms.
|
469
|
+
def run_with_tee(cmd)
|
470
|
+
status, stdout, stderr = nil
|
471
|
+
|
472
|
+
if windows?
|
473
|
+
#:nocov: TODO: Test block
|
474
|
+
system(cmd)
|
475
|
+
status = $?.exitstatus
|
476
|
+
#:nocov:
|
477
|
+
else
|
478
|
+
stdout, stderr = [$stdout, $stderr].map{ |t| StringTee.new(t) }
|
479
|
+
status = Open4.spawn(cmd, 'stdout' => stdout, 'stderr' => stderr, 'quiet' => true)
|
480
|
+
stdout, stderr = [stdout, stderr].map(&:string)
|
481
|
+
end
|
482
|
+
|
483
|
+
[status, stdout, stderr]
|
484
|
+
end
|
485
|
+
|
486
|
+
private
|
487
|
+
|
488
|
+
def separate_blocks
|
489
|
+
if (@@margin ||= 0) > 0 && !@@last_line_open
|
490
|
+
$terminal.instance_variable_get(:@output).print "\n" * @@margin
|
491
|
+
@@margin = 0
|
492
|
+
end
|
493
|
+
end
|
359
494
|
end
|
360
495
|
end
|