startapp 0.1.6
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.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/autocomplete/rhc_bash +1672 -0
- data/bin/app +37 -0
- data/conf/express.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +191 -0
- data/features/deployments_feature.rb +129 -0
- data/features/domains_feature.rb +58 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +166 -0
- data/lib/rhc/auth/basic.rb +64 -0
- data/lib/rhc/auth/token.rb +102 -0
- data/lib/rhc/auth/token_store.rb +53 -0
- data/lib/rhc/auth.rb +5 -0
- data/lib/rhc/autocomplete.rb +66 -0
- data/lib/rhc/autocomplete_templates/bash.erb +39 -0
- data/lib/rhc/cartridge_helpers.rb +118 -0
- data/lib/rhc/cli.rb +40 -0
- data/lib/rhc/command_runner.rb +185 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +726 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +115 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +329 -0
- data/lib/rhc/commands/clone.rb +66 -0
- data/lib/rhc/commands/configure.rb +20 -0
- data/lib/rhc/commands/create.rb +100 -0
- data/lib/rhc/commands/delete.rb +19 -0
- data/lib/rhc/commands/deploy.rb +32 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +172 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/force_stop.rb +17 -0
- data/lib/rhc/commands/git_clone.rb +34 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/logs.rb +21 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/reload.rb +17 -0
- data/lib/rhc/commands/restart.rb +17 -0
- data/lib/rhc/commands/scp.rb +54 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/show.rb +43 -0
- data/lib/rhc/commands/snapshot.rb +137 -0
- data/lib/rhc/commands/ssh.rb +51 -0
- data/lib/rhc/commands/sshkey.rb +97 -0
- data/lib/rhc/commands/start.rb +17 -0
- data/lib/rhc/commands/stop.rb +17 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands/tidy.rb +17 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +321 -0
- data/lib/rhc/context_helper.rb +121 -0
- data/lib/rhc/core_ext.rb +202 -0
- data/lib/rhc/coverage_helper.rb +33 -0
- data/lib/rhc/deployment_helpers.rb +111 -0
- data/lib/rhc/exceptions.rb +256 -0
- data/lib/rhc/git_helpers.rb +106 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +481 -0
- data/lib/rhc/highline_extensions.rb +479 -0
- data/lib/rhc/json.rb +51 -0
- data/lib/rhc/output_helpers.rb +260 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/alias.rb +42 -0
- data/lib/rhc/rest/api.rb +87 -0
- data/lib/rhc/rest/application.rb +348 -0
- data/lib/rhc/rest/attributes.rb +36 -0
- data/lib/rhc/rest/authorization.rb +8 -0
- data/lib/rhc/rest/base.rb +79 -0
- data/lib/rhc/rest/cartridge.rb +162 -0
- data/lib/rhc/rest/client.rb +650 -0
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/domain.rb +98 -0
- data/lib/rhc/rest/environment_variable.rb +15 -0
- data/lib/rhc/rest/gear_group.rb +16 -0
- data/lib/rhc/rest/httpclient.rb +145 -0
- data/lib/rhc/rest/key.rb +44 -0
- data/lib/rhc/rest/membership.rb +105 -0
- data/lib/rhc/rest/mock.rb +1042 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/scp_helpers.rb +27 -0
- data/lib/rhc/ssh_helpers.rb +380 -0
- data/lib/rhc/tar_gz.rb +51 -0
- data/lib/rhc/usage_templates/command_help.erb +51 -0
- data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
- data/lib/rhc/usage_templates/help.erb +61 -0
- data/lib/rhc/usage_templates/missing_help.erb +1 -0
- data/lib/rhc/usage_templates/options_help.erb +12 -0
- data/lib/rhc/vendor/okjson.rb +600 -0
- data/lib/rhc/vendor/parseconfig.rb +178 -0
- data/lib/rhc/vendor/sshkey.rb +253 -0
- data/lib/rhc/vendor/zliby.rb +628 -0
- data/lib/rhc/version.rb +5 -0
- data/lib/rhc/wizard.rb +637 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +82 -0
- data/spec/direct_execution_helper.rb +339 -0
- 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 +31 -0
- data/spec/rhc/assets/cert.crt +22 -0
- data/spec/rhc/assets/cert_key_rsa +27 -0
- data/spec/rhc/assets/empty.txt +0 -0
- data/spec/rhc/assets/env_vars.txt +7 -0
- data/spec/rhc/assets/env_vars_2.txt +1 -0
- data/spec/rhc/assets/foo.txt +1 -0
- data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
- data/spec/rhc/assets/targz_sample.tar.gz +0 -0
- data/spec/rhc/auth_spec.rb +442 -0
- data/spec/rhc/cli_spec.rb +186 -0
- data/spec/rhc/command_spec.rb +435 -0
- data/spec/rhc/commands/account_spec.rb +42 -0
- data/spec/rhc/commands/alias_spec.rb +333 -0
- data/spec/rhc/commands/app_spec.rb +777 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +157 -0
- data/spec/rhc/commands/cartridge_spec.rb +665 -0
- data/spec/rhc/commands/clone_spec.rb +41 -0
- data/spec/rhc/commands/deployment_spec.rb +327 -0
- data/spec/rhc/commands/domain_spec.rb +401 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +102 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +247 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/scp_spec.rb +77 -0
- data/spec/rhc/commands/server_spec.rb +69 -0
- data/spec/rhc/commands/setup_spec.rb +118 -0
- data/spec/rhc/commands/snapshot_spec.rb +179 -0
- data/spec/rhc/commands/ssh_spec.rb +163 -0
- data/spec/rhc/commands/sshkey_spec.rb +188 -0
- data/spec/rhc/commands/tail_spec.rb +81 -0
- data/spec/rhc/commands/threaddump_spec.rb +84 -0
- data/spec/rhc/config_spec.rb +407 -0
- data/spec/rhc/helpers_spec.rb +531 -0
- data/spec/rhc/highline_extensions_spec.rb +314 -0
- data/spec/rhc/json_spec.rb +30 -0
- data/spec/rhc/rest_application_spec.rb +258 -0
- data/spec/rhc/rest_client_spec.rb +752 -0
- data/spec/rhc/rest_spec.rb +740 -0
- data/spec/rhc/targz_spec.rb +55 -0
- data/spec/rhc/wizard_spec.rb +756 -0
- data/spec/spec_helper.rb +575 -0
- data/spec/wizard_spec_helper.rb +330 -0
- metadata +469 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module RHC::Auth
|
|
2
|
+
class Basic
|
|
3
|
+
def initialize(*args)
|
|
4
|
+
if args[0].is_a?(String) or args.length > 1
|
|
5
|
+
@username, @password = args
|
|
6
|
+
else
|
|
7
|
+
@options = args[0] || Commander::Command::Options.new
|
|
8
|
+
@username = options[:rhlogin]
|
|
9
|
+
@password = options[:password]
|
|
10
|
+
@no_interactive = options[:noprompt]
|
|
11
|
+
end
|
|
12
|
+
@skip_interactive = !@password.nil?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_request(request)
|
|
16
|
+
request[:user] ||=
|
|
17
|
+
lambda{ username || (request[:lazy_auth] != true && ask_username) || nil }
|
|
18
|
+
request[:password] ||=
|
|
19
|
+
lambda{ password || (username? && request[:lazy_auth] != true && ask_password) || nil }
|
|
20
|
+
request
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def retry_auth?(response, client)
|
|
24
|
+
if response.status == 401
|
|
25
|
+
credentials_rejected
|
|
26
|
+
else
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def can_authenticate?
|
|
32
|
+
username? and not (password.nil? and @skip_interactive and @no_interactive)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_reader :username
|
|
36
|
+
|
|
37
|
+
protected
|
|
38
|
+
include RHC::Helpers
|
|
39
|
+
attr_reader :options, :password
|
|
40
|
+
|
|
41
|
+
def credentials_rejected
|
|
42
|
+
error "Username or password is not correct" if username? && password
|
|
43
|
+
unless @skip_interactive or @no_interactive
|
|
44
|
+
ask_username unless username?
|
|
45
|
+
ask_password
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ask_username
|
|
51
|
+
@username = ask("Login to #{openshift_server}: ") unless @no_interactive
|
|
52
|
+
end
|
|
53
|
+
def ask_password
|
|
54
|
+
@password = ask("Password: ") { |q|
|
|
55
|
+
q.echo = '*'
|
|
56
|
+
q.whitespace = :chomp
|
|
57
|
+
} unless @no_interactive
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def username?
|
|
61
|
+
username.present?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module RHC::Auth
|
|
2
|
+
class Token
|
|
3
|
+
def initialize(opt, auth=nil, store=nil)
|
|
4
|
+
if opt.is_a?(String)
|
|
5
|
+
@token = opt
|
|
6
|
+
else
|
|
7
|
+
@options = opt || Commander::Command::Options.new
|
|
8
|
+
@token = options[:token]
|
|
9
|
+
@no_interactive = options[:noprompt]
|
|
10
|
+
@allows_tokens = options[:use_authorization_tokens]
|
|
11
|
+
end
|
|
12
|
+
@auth = auth
|
|
13
|
+
@store = store
|
|
14
|
+
read_token
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_request(request)
|
|
18
|
+
if token
|
|
19
|
+
debug "Using token authentication"
|
|
20
|
+
(request[:headers] ||= {})['authorization'] = "Bearer #{token}"
|
|
21
|
+
elsif auth and (!@allows_tokens or @can_get_token == false)
|
|
22
|
+
debug "Bypassing token auth"
|
|
23
|
+
auth.to_request(request)
|
|
24
|
+
end
|
|
25
|
+
request
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def retry_auth?(response, client)
|
|
29
|
+
if response.status == 401
|
|
30
|
+
token_rejected(response, client)
|
|
31
|
+
else
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def username
|
|
37
|
+
auth && auth.respond_to?(:username) && auth.username || options[:username]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def save(token)
|
|
41
|
+
store.put(username, openshift_server, token) if store
|
|
42
|
+
@token = token
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def can_authenticate?
|
|
46
|
+
token || auth && auth.can_authenticate?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
protected
|
|
50
|
+
include RHC::Helpers
|
|
51
|
+
attr_reader :options, :token, :auth, :store
|
|
52
|
+
|
|
53
|
+
def token_rejected(response, client)
|
|
54
|
+
has_token = !!token
|
|
55
|
+
@token = nil
|
|
56
|
+
|
|
57
|
+
unless auth && auth.can_authenticate?
|
|
58
|
+
if has_token
|
|
59
|
+
raise RHC::Rest::TokenExpiredOrInvalid, "Your authorization token is expired or invalid."
|
|
60
|
+
end
|
|
61
|
+
debug "Cannot authenticate via token or password, exiting"
|
|
62
|
+
return false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if has_token
|
|
66
|
+
if cannot_retry?
|
|
67
|
+
raise RHC::Rest::TokenExpiredOrInvalid, "Your authorization token is expired or invalid."
|
|
68
|
+
end
|
|
69
|
+
if not client.supports_sessions?
|
|
70
|
+
raise RHC::Rest::AuthorizationsNotSupported
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
@can_get_token = client.supports_sessions? && @allows_tokens
|
|
75
|
+
|
|
76
|
+
if has_token
|
|
77
|
+
warn "Your authorization token has expired. Please sign in now to continue on #{openshift_server}."
|
|
78
|
+
elsif @can_get_token
|
|
79
|
+
info "Please sign in to start a new session to #{openshift_server}."
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return auth.retry_auth?(response, client) unless @can_get_token
|
|
83
|
+
|
|
84
|
+
debug "Creating a new authorization token"
|
|
85
|
+
if auth_token = client.new_session(:auth => auth)
|
|
86
|
+
@fetch_once = true
|
|
87
|
+
save(auth_token.token)
|
|
88
|
+
true
|
|
89
|
+
else
|
|
90
|
+
auth.retry_auth?(response, client)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def read_token
|
|
95
|
+
@token ||= store.get(username, openshift_server) if store
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def cannot_retry?
|
|
99
|
+
!@fetch_once && @no_interactive
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module RHC::Auth
|
|
4
|
+
class TokenStore
|
|
5
|
+
def initialize(dir)
|
|
6
|
+
@dir = dir
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def get(login, server)
|
|
10
|
+
self[key(login,server)]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def put(login, server, token)
|
|
14
|
+
self[key(login,server)] = token
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def clear
|
|
18
|
+
Dir[File.join(@dir, "token_*")].
|
|
19
|
+
each{ |f| File.delete(f) unless File.directory?(f) }.
|
|
20
|
+
present?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def path(key)
|
|
25
|
+
File.join(@dir, filename(key))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def filename(key)
|
|
29
|
+
"token_#{Base64.encode64(Digest::MD5.digest(key)).gsub(/[^\w\@]/,'')}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def []=(key, value)
|
|
33
|
+
file = path(key)
|
|
34
|
+
FileUtils.mkdir_p File.dirname(file)
|
|
35
|
+
File.open(file, 'w'){ |f| f.write(value) }
|
|
36
|
+
File.chmod(0600, file)
|
|
37
|
+
value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def [](key)
|
|
41
|
+
s = IO.read(path(key)).presence
|
|
42
|
+
s = s.strip.gsub(/[\n\r\t]/,'') if s
|
|
43
|
+
s
|
|
44
|
+
rescue Errno::ENOENT
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def key(login, server)
|
|
49
|
+
"#{login || ''}@#{server}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/rhc/auth.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module RHC
|
|
2
|
+
class AutoComplete
|
|
3
|
+
attr_reader :runner
|
|
4
|
+
|
|
5
|
+
def initialize(runner=::Commander::Runner.instance, shell='bash')
|
|
6
|
+
@runner, @shell = runner, shell
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
@s ||= template.result AutoCompleteBindings.new(self).get_binding
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def template
|
|
16
|
+
@template ||= ERB.new(File.read(File.join(File.dirname(__FILE__), 'autocomplete_templates', "#{@shell}.erb")), nil, '-')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class AutoCompleteBindings
|
|
21
|
+
attr_reader :commands, :top_level_commands, :global_options
|
|
22
|
+
|
|
23
|
+
def initialize(data)
|
|
24
|
+
@commands = {}
|
|
25
|
+
@top_level_commands = []
|
|
26
|
+
|
|
27
|
+
data.runner.commands.each_pair do |name, cmd|
|
|
28
|
+
next if cmd.summary.nil?
|
|
29
|
+
next if cmd.deprecated(name)
|
|
30
|
+
|
|
31
|
+
if cmd.root?
|
|
32
|
+
if cmd.name == name
|
|
33
|
+
@top_level_commands << name
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
@top_level_commands << name if name == cmd.name
|
|
37
|
+
commands = name.split ' '
|
|
38
|
+
action = commands.pop
|
|
39
|
+
id = commands.join(' ')
|
|
40
|
+
v = @commands[id] || {:actions => [], :switches => []}
|
|
41
|
+
v[:actions] << action unless id == '' && name != cmd.name
|
|
42
|
+
@commands[id] = v
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
v = @commands[name.to_s] || {:actions => [], :switches => []}
|
|
46
|
+
v[:switches].concat(cmd.options.map do |o|
|
|
47
|
+
if o[:switches]
|
|
48
|
+
s = o[:switches][-1].split(' ')[0]
|
|
49
|
+
if m = /--\[no-\](.+)/.match(s)
|
|
50
|
+
s = ["--#{m[1]}", "--no-#{m[1]}"]
|
|
51
|
+
else
|
|
52
|
+
s
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end.flatten.compact.sort)
|
|
56
|
+
@commands[name.to_s] = v
|
|
57
|
+
end
|
|
58
|
+
@commands.delete('')
|
|
59
|
+
@commands = @commands.to_a.sort{ |a,b| a[0] <=> b[0] }
|
|
60
|
+
|
|
61
|
+
@top_level_commands.sort!
|
|
62
|
+
|
|
63
|
+
@global_options = data.runner.options.map{ |o| o[:switches][-1].split(' ')[0] }.sort
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This is the bash auto completion script for the rhc command
|
|
3
|
+
#
|
|
4
|
+
_app()
|
|
5
|
+
{
|
|
6
|
+
local cur opts prev
|
|
7
|
+
COMPREPLY=()
|
|
8
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
9
|
+
if [ $COMP_CWORD -eq 1 ]; then
|
|
10
|
+
if [[ "$cur" == -* ]]; then
|
|
11
|
+
opts=<%= "\"%s\"" % global_options.join(' ') %>
|
|
12
|
+
elif [ -z $cur ]; then
|
|
13
|
+
opts=<%= "\"%s\"" % top_level_commands.join(' ') %>
|
|
14
|
+
else
|
|
15
|
+
opts=<%= "\"%s\"" % commands.map{ |c| c.first }.delete_if{ |s| s.include? ' ' }.sort.join(' ') %>
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
prev="${COMP_WORDS[@]:0:COMP_CWORD}"
|
|
19
|
+
SAVE_IFS=$IFS
|
|
20
|
+
IFS=" "
|
|
21
|
+
case "${prev[*]}" in
|
|
22
|
+
<%- for name, data in commands %>
|
|
23
|
+
<%= "\"app %s\")" % name %>
|
|
24
|
+
if [[ "$cur" == -* ]]; then
|
|
25
|
+
opts=<%= "\"%s\"" % data[:switches].join(" ") %>
|
|
26
|
+
else
|
|
27
|
+
opts=<%= "\"%s\"" % data[:actions].join(" ") %>
|
|
28
|
+
fi
|
|
29
|
+
;;
|
|
30
|
+
<%- end %>
|
|
31
|
+
esac
|
|
32
|
+
IFS=$SAVE_IFS
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
36
|
+
return 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
complete -o default -F _app app
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module RHC
|
|
2
|
+
module CartridgeHelpers
|
|
3
|
+
|
|
4
|
+
protected
|
|
5
|
+
def check_cartridges(names, opts={}, &block)
|
|
6
|
+
cartridge_names = Array(names).map{ |s| s.strip if s && s.length > 0 }.compact
|
|
7
|
+
from = opts[:from] || all_cartridges
|
|
8
|
+
|
|
9
|
+
cartridge_names.map do |name|
|
|
10
|
+
next from.find{ |c| c.url.present? && c.url.downcase == name} || use_cart(RHC::Rest::Cartridge.for_url(name), name) if name =~ %r(\Ahttps?://)i
|
|
11
|
+
|
|
12
|
+
name = name.downcase
|
|
13
|
+
from.find{ |c| c.name.downcase == name } ||
|
|
14
|
+
begin
|
|
15
|
+
carts = from.select{ |c| match_cart(c, name) }
|
|
16
|
+
if carts.empty?
|
|
17
|
+
paragraph { list_cartridges(from) }
|
|
18
|
+
raise RHC::CartridgeNotFoundException, "There are no cartridges that match '#{name}'."
|
|
19
|
+
elsif carts.length == 1
|
|
20
|
+
use_cart(carts.first, name)
|
|
21
|
+
else
|
|
22
|
+
carts.sort!.instance_variable_set(:@for, name)
|
|
23
|
+
carts
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end.tap do |carts|
|
|
27
|
+
yield carts if block_given?
|
|
28
|
+
end.each do |carts|
|
|
29
|
+
if carts.is_a? Array
|
|
30
|
+
name = carts.instance_variable_get(:@for)
|
|
31
|
+
paragraph { list_cartridges(carts) }
|
|
32
|
+
raise RHC::MultipleCartridgesException, "There are multiple cartridges matching '#{name}'. Please provide the short name of the correct cart."
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def use_cart(cart, for_cartridge_name)
|
|
38
|
+
if cart.name.blank? and cart.custom?
|
|
39
|
+
info "The cartridge '#{cart.url}' will be downloaded and installed"
|
|
40
|
+
else
|
|
41
|
+
info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} for '#{for_cartridge_name}'"
|
|
42
|
+
end
|
|
43
|
+
cart
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def match_cart(cart, search)
|
|
47
|
+
search = search.to_s.downcase.gsub(/[_\-\s]/,' ')
|
|
48
|
+
[
|
|
49
|
+
cart.name,
|
|
50
|
+
(cart.tags || []).join(' '),
|
|
51
|
+
].compact.any?{ |s| s.present? && s.downcase.gsub(/[_\-\s]/,' ').include?(search) } ||
|
|
52
|
+
search.length > 2 && [
|
|
53
|
+
cart.description
|
|
54
|
+
].compact.any?{ |s| s.present? && !s.downcase.match(/\b#{Regexp.escape(search)}\b/).nil? }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def web_carts_only
|
|
58
|
+
lambda{ |cart|
|
|
59
|
+
next cart unless cart.is_a? Array
|
|
60
|
+
name = cart.instance_variable_get(:@for)
|
|
61
|
+
matching = cart.select{ |c| not c.only_in_existing? }
|
|
62
|
+
if matching.size == 1
|
|
63
|
+
use_cart(matching.first, name)
|
|
64
|
+
else
|
|
65
|
+
matching.instance_variable_set(:@for, name)
|
|
66
|
+
matching
|
|
67
|
+
end
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def other_carts_only
|
|
72
|
+
lambda{ |cart|
|
|
73
|
+
next cart unless cart.is_a? Array
|
|
74
|
+
name = cart.instance_variable_get(:@for)
|
|
75
|
+
matching = cart.select{ |c| not c.only_in_new? }
|
|
76
|
+
if matching.size == 1
|
|
77
|
+
use_cart(matching.first, name)
|
|
78
|
+
else
|
|
79
|
+
matching.instance_variable_set(:@for, name)
|
|
80
|
+
matching
|
|
81
|
+
end
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def standalone_cartridges
|
|
86
|
+
@standalone_cartridges ||= all_cartridges.select{ |c| c.type == 'standalone' }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def not_standalone_cartridges
|
|
90
|
+
@not_standalone_cartridges ||= all_cartridges.select{ |c| c.type != 'standalone' }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def all_cartridges
|
|
94
|
+
@all_cartridges = rest_client.cartridges
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def list_cartridges(cartridges)
|
|
98
|
+
carts = cartridges.map{ |c| [c.name, c.display_name || ''] }.sort{ |a,b| a[1].downcase <=> b[1].downcase }
|
|
99
|
+
carts.unshift ['==========', '=========']
|
|
100
|
+
carts.unshift ['Short Name', 'Full name']
|
|
101
|
+
say table(carts)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def filter_jenkins_cartridges(tag)
|
|
105
|
+
cartridges = all_cartridges.select { |c| (c.tags || []).include?(tag) && c.name =~ /\Ajenkins/i }.sort
|
|
106
|
+
raise RHC::JenkinsNotInstalledOnServer if cartridges.empty?
|
|
107
|
+
cartridges
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def jenkins_cartridges
|
|
111
|
+
@jenkins_cartridges ||= filter_jenkins_cartridges('ci')
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def jenkins_client_cartridges
|
|
115
|
+
@jenkins_client_cartridges ||= filter_jenkins_cartridges('ci_builder')
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/rhc/cli.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'rhc'
|
|
2
|
+
require 'rhc/commands'
|
|
3
|
+
|
|
4
|
+
module RHC
|
|
5
|
+
#
|
|
6
|
+
# Run and execute a command line session with the RHC tools.
|
|
7
|
+
#
|
|
8
|
+
# You can invoke the CLI with:
|
|
9
|
+
# bundle exec ruby -e 'require "rhc/cli"; RHC::CLI.start(ARGV);' -- <arguments>
|
|
10
|
+
#
|
|
11
|
+
# from the gem directory.
|
|
12
|
+
#
|
|
13
|
+
module CLI
|
|
14
|
+
extend Commander::Delegates
|
|
15
|
+
|
|
16
|
+
def self.set_terminal
|
|
17
|
+
$terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first rescue 80 if $stdout.tty?
|
|
18
|
+
$terminal.wrap_at = nil if $terminal.wrap_at == 0
|
|
19
|
+
#$terminal.page_at = :auto if $stdin.tty? and $stdout.tty?
|
|
20
|
+
# FIXME: ANSI terminals are not default on windows but we may just be
|
|
21
|
+
# hitting a bug in highline if windows does support another method.
|
|
22
|
+
# This is a safe fix for now but needs more research.
|
|
23
|
+
HighLine::use_color = false if RHC::Helpers.windows? or not $stdout.tty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.start(args)
|
|
27
|
+
runner = RHC::CommandRunner.new(args)
|
|
28
|
+
Commander::Runner.instance_variable_set :@singleton, runner
|
|
29
|
+
|
|
30
|
+
program :name, 'app'
|
|
31
|
+
program :description, 'Command line interface for StartApp.'
|
|
32
|
+
program :version, RHC::VERSION::STRING
|
|
33
|
+
program :help_formatter, RHC::HelpFormatter
|
|
34
|
+
program :int_message, " Interrupted\n"
|
|
35
|
+
|
|
36
|
+
RHC::Commands.load.to_commander
|
|
37
|
+
run! || 0
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
module RHC
|
|
2
|
+
class CommandRunner < Commander::Runner
|
|
3
|
+
# regex fix from git - match on word boundries
|
|
4
|
+
def valid_command_names_from *args
|
|
5
|
+
arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
|
|
6
|
+
commands.keys.find_all { |name| name if /^#{name}\b/.match arg_string }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
if Commander::VERSION == '4.0.3'
|
|
10
|
+
#:nocov:
|
|
11
|
+
def program(*args)
|
|
12
|
+
Array(super).first
|
|
13
|
+
end
|
|
14
|
+
#:nocov:
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def options_parse_trace
|
|
18
|
+
if @args.include?("--trace")
|
|
19
|
+
@args.delete "--trace"
|
|
20
|
+
return true
|
|
21
|
+
end
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def options_parse_debug
|
|
26
|
+
if @args.include?("-d") or @args.include?("--debug")
|
|
27
|
+
@args.delete "-d"
|
|
28
|
+
@args.delete "--debug"
|
|
29
|
+
return true
|
|
30
|
+
end
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def options_parse_version
|
|
35
|
+
if @args.include? "--version"
|
|
36
|
+
say version
|
|
37
|
+
exit 0
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
HELP_OPTIONS = ['--help', '-h']
|
|
42
|
+
|
|
43
|
+
def options_parse_help
|
|
44
|
+
if (@args & HELP_OPTIONS).present?
|
|
45
|
+
args = (@args -= HELP_OPTIONS)
|
|
46
|
+
args.shift if args.first == 'help' && !command_exists?(args.join(' '))
|
|
47
|
+
exit run_help(args)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# override so we can do our own error handling
|
|
52
|
+
def run!
|
|
53
|
+
trace = false
|
|
54
|
+
require_program :version, :description
|
|
55
|
+
|
|
56
|
+
global_option('-h', '--help', 'Help on any command', :hide => true)
|
|
57
|
+
global_option('--version', 'Display version information', :hide => true)
|
|
58
|
+
|
|
59
|
+
# special case --debug so all commands can output relevant info on it
|
|
60
|
+
$terminal.debug = options_parse_debug
|
|
61
|
+
|
|
62
|
+
# special case --trace because we need to use it in the runner
|
|
63
|
+
trace = options_parse_trace
|
|
64
|
+
|
|
65
|
+
# special case --version so it is processed before an invalid command
|
|
66
|
+
options_parse_version
|
|
67
|
+
|
|
68
|
+
# help is a special branch prior to command execution
|
|
69
|
+
options_parse_help
|
|
70
|
+
|
|
71
|
+
unless trace
|
|
72
|
+
begin
|
|
73
|
+
run_active_command
|
|
74
|
+
rescue InvalidCommandError => e
|
|
75
|
+
run_help(provided_arguments)
|
|
76
|
+
rescue \
|
|
77
|
+
OptionParser::InvalidOption => e
|
|
78
|
+
RHC::Helpers.error e.message
|
|
79
|
+
1
|
|
80
|
+
rescue \
|
|
81
|
+
ArgumentError,
|
|
82
|
+
OptionParser::ParseError => e
|
|
83
|
+
|
|
84
|
+
help_bindings = CommandHelpBindings.new(active_command, commands, self)
|
|
85
|
+
usage = RHC::HelpFormatter.new(self).render_command_syntax(help_bindings)
|
|
86
|
+
message = case e
|
|
87
|
+
when OptionParser::AmbiguousOption
|
|
88
|
+
"The option #{e.args.join(' ')} is ambiguous. You will need to specify the entire option."
|
|
89
|
+
else
|
|
90
|
+
e.message
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
RHC::Helpers.error message
|
|
94
|
+
say "#{usage}"
|
|
95
|
+
1
|
|
96
|
+
rescue RHC::Exception, RHC::Rest::Exception => e
|
|
97
|
+
RHC::Helpers.error e.message
|
|
98
|
+
e.code.nil? ? 128 : [1, (e.code || 1).to_i].max
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
run_active_command
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def provided_arguments
|
|
106
|
+
@args[0, @args.find_index { |arg| arg != '--' and arg.start_with?('-') } || @args.length]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def global_option(*args, &block)
|
|
110
|
+
opts = args.pop if Hash === args.last
|
|
111
|
+
super(*args, &block).tap do |options|
|
|
112
|
+
options.last.merge!(opts) if opts
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def create_default_commands
|
|
117
|
+
command 'help options' do |c|
|
|
118
|
+
c.description = "Display all global options and information about configuration"
|
|
119
|
+
c.when_called do |args, options|
|
|
120
|
+
say help_formatter.render_options self
|
|
121
|
+
0
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
command :help do |c|
|
|
125
|
+
c.syntax = '<command>'
|
|
126
|
+
c.description = 'Display global or <command> help documentation.'
|
|
127
|
+
c.when_called(&method(:run_help))
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def run_help(args=[], options=nil)
|
|
132
|
+
args.delete_if{ |a| a.start_with? '-' }
|
|
133
|
+
unless args[0] == 'commands'
|
|
134
|
+
variations = (1..args.length).reverse_each.map{ |n| args[0,n].join('-') }
|
|
135
|
+
cmd = variations.find{ |cmd| command_exists?(cmd) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if args.empty?
|
|
139
|
+
say help_formatter.render
|
|
140
|
+
0
|
|
141
|
+
else
|
|
142
|
+
if cmd.nil?
|
|
143
|
+
matches = (variations || ['']).inject(nil) do |candidates, term|
|
|
144
|
+
term = term.downcase
|
|
145
|
+
keys = commands.keys.map(&:downcase)
|
|
146
|
+
prefix, keys = keys.partition{ |n| n.start_with? term }
|
|
147
|
+
inline, keys = keys.partition{ |n| n.include? term }
|
|
148
|
+
break [term, prefix, inline] unless prefix.empty? && inline.empty?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
unless matches
|
|
152
|
+
RHC::Helpers.error "The command '#{program :name} #{provided_arguments.join(' ')}' is not recognized.\n"
|
|
153
|
+
say "See '#{program :name} help' for a list of valid commands."
|
|
154
|
+
return 1
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
candidates = (matches[1] + matches[2]).map{ |n| commands[n] }.uniq.sort_by{ |c| c.name }
|
|
158
|
+
if candidates.length == 1
|
|
159
|
+
cmd = candidates.first.name
|
|
160
|
+
else
|
|
161
|
+
RHC::Helpers.pager
|
|
162
|
+
RHC::Helpers.say matches[0] != '' ? "Showing commands matching '#{matches[0]}'" : "Showing all commands"
|
|
163
|
+
candidates.reverse.each do |command|
|
|
164
|
+
RHC::Helpers.paragraph do
|
|
165
|
+
aliases = (commands.map{ |(k,v)| k if command == v }.compact - [command.name]).map{ |s| "'#{s}'"}
|
|
166
|
+
aliases[0] = "(also #{aliases[0]}" if aliases[0]
|
|
167
|
+
aliases[-1] << ')' if aliases[0]
|
|
168
|
+
|
|
169
|
+
RHC::Helpers.header [RHC::Helpers.color(command.name, :cyan), *aliases.join(', ')]
|
|
170
|
+
say command.description || command.summary
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
return 1
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
RHC::Helpers.pager
|
|
178
|
+
command = command(cmd)
|
|
179
|
+
help_bindings = CommandHelpBindings.new command, commands, self
|
|
180
|
+
say help_formatter.render_command help_bindings
|
|
181
|
+
0
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module RHC::Commands
|
|
2
|
+
class Account < Base
|
|
3
|
+
suppress_wizard
|
|
4
|
+
|
|
5
|
+
summary "Display details about your StartApp account"
|
|
6
|
+
description <<-DESC
|
|
7
|
+
Shows who you are logged in to the server as and the capabilities
|
|
8
|
+
available to you on this server.
|
|
9
|
+
|
|
10
|
+
To access more details about your account please visit the website.
|
|
11
|
+
DESC
|
|
12
|
+
def run
|
|
13
|
+
user = rest_client.user
|
|
14
|
+
|
|
15
|
+
say format_table \
|
|
16
|
+
["Login #{user.login} on #{openshift_server}"],
|
|
17
|
+
get_properties(user, :id, :plan_id, :consumed_gears, :max_gears, :max_domains).
|
|
18
|
+
concat(get_properties(user.capabilities, :gear_sizes)).
|
|
19
|
+
push(['SSL Certificates:', user.capabilities.private_ssl_certificates ? 'yes' : 'no']),
|
|
20
|
+
:delete => true
|
|
21
|
+
|
|
22
|
+
0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|