startapp 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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,34 @@
|
|
1
|
+
require 'rhc/commands/base'
|
2
|
+
require 'rhc/git_helpers'
|
3
|
+
|
4
|
+
module RHC::Commands
|
5
|
+
class GitClone < Base
|
6
|
+
summary "Clone and configure an application's repository locally"
|
7
|
+
description "This is a convenience wrapper for 'git clone' with the added",
|
8
|
+
"benefit of adding configuration data such as the application's",
|
9
|
+
"UUID to the local repository. It also automatically",
|
10
|
+
"figures out the Git url from the application name so you don't",
|
11
|
+
"have to look it up."
|
12
|
+
syntax "<app> [--namespace NAME]"
|
13
|
+
takes_application :argument => true
|
14
|
+
option ["-r", "--repo dir"], "Path to the Git repository (defaults to ./$app_name)"
|
15
|
+
alias_action 'app git-clone', :deprecated => true, :root_command => true
|
16
|
+
# TODO: Implement default values for arguments once ffranz has added context arguments
|
17
|
+
# argument :directory, "The name of a new directory to clone into", [], :default => nil
|
18
|
+
def run(app_name)
|
19
|
+
if has_git?
|
20
|
+
rest_app = find_app
|
21
|
+
dir = git_clone_application(rest_app)
|
22
|
+
success "Your application Git repository has been cloned to '#{system_path(dir)}'"
|
23
|
+
|
24
|
+
0
|
25
|
+
else
|
26
|
+
error "You do not have git installed. In order to fully interact with StartApp you will need to install and configure a git client."
|
27
|
+
2
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
include RHC::GitHelpers
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module RHC::Commands
|
2
|
+
class Logout < Base
|
3
|
+
suppress_wizard
|
4
|
+
|
5
|
+
summary "End the current session"
|
6
|
+
description <<-DESC
|
7
|
+
Logout ends your current session on the server and then removes
|
8
|
+
all of the local session files. If you are using multiple
|
9
|
+
servers and configurations this will remove all of your local
|
10
|
+
session files.
|
11
|
+
|
12
|
+
The --all option will terminate all authorizations on your
|
13
|
+
account. Any previously generated authorizations will be
|
14
|
+
deleted and external tools that integrate with your account
|
15
|
+
will no longer be able to log in.
|
16
|
+
DESC
|
17
|
+
option '--all', "Remove all authorizations on your account."
|
18
|
+
alias_action 'account logout', :root_command => true
|
19
|
+
def run
|
20
|
+
if options.all
|
21
|
+
rest_client.user # force authentication
|
22
|
+
say "Deleting all authorizations associated with your account ... "
|
23
|
+
begin
|
24
|
+
rest_client.delete_authorizations
|
25
|
+
success "done"
|
26
|
+
rescue RHC::Rest::AuthorizationsNotSupported
|
27
|
+
info "not supported"
|
28
|
+
end
|
29
|
+
elsif token_for_user
|
30
|
+
options.noprompt = true
|
31
|
+
say "Ending session on server ... "
|
32
|
+
begin
|
33
|
+
rest_client.delete_authorization(token_for_user)
|
34
|
+
success "deleted"
|
35
|
+
rescue RHC::Rest::AuthorizationsNotSupported
|
36
|
+
info "not supported"
|
37
|
+
rescue RHC::Rest::TokenExpiredOrInvalid
|
38
|
+
info "already closed"
|
39
|
+
rescue => e
|
40
|
+
debug_error(e)
|
41
|
+
warn e.message
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
0
|
46
|
+
ensure
|
47
|
+
token_store.clear
|
48
|
+
success "All local sessions removed."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rhc/commands/tail'
|
2
|
+
|
3
|
+
module RHC::Commands
|
4
|
+
class Logs < Tail
|
5
|
+
|
6
|
+
summary "Tail the logs of an application"
|
7
|
+
syntax "<application>"
|
8
|
+
takes_application :argument => true
|
9
|
+
option ["-o", "--opts options"], "Options to pass to the server-side (linux based) tail command (applicable to tail command only) (-f is implicit. See the linux tail man page full list of options.) (Ex: --opts '-n 100')"
|
10
|
+
option ["-f", "--files files"], "File glob relative to app (default <application_name>/logs/*) (optional)"
|
11
|
+
option ["-g", "--gear ID"], "Tail only a specific gear"
|
12
|
+
#option ["-c", "--cartridge name"], "Tail only a specific cartridge"
|
13
|
+
alias_action :"app tail", :root_command => true, :deprecated => true
|
14
|
+
def run(app_name)
|
15
|
+
super app_name
|
16
|
+
|
17
|
+
0
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'rhc/commands/base'
|
2
|
+
|
3
|
+
module RHC::Commands
|
4
|
+
class Member < Base
|
5
|
+
summary "Manage membership on domains"
|
6
|
+
syntax "<action>"
|
7
|
+
description <<-DESC
|
8
|
+
Teams of developers can collaborate on applications by adding people to
|
9
|
+
domains as members: each member has a role (admin, editor, or viewer),
|
10
|
+
and those roles determine what the user can do with the domain and the
|
11
|
+
applications contained within.
|
12
|
+
|
13
|
+
Roles:
|
14
|
+
|
15
|
+
view - able to see information about the domain and its apps, but not make any changes
|
16
|
+
edit - create, update, and delete applications, and has Git and SSH access
|
17
|
+
admin - can update membership of a domain
|
18
|
+
|
19
|
+
The default role granted to members when added is 'edit' - use the '--role'
|
20
|
+
argument to use another. When adding and removing members, you can use their
|
21
|
+
'login' value (typically their email or a short unique name for them) or their
|
22
|
+
'id'. Both login and ID are visible via the 'rhc account' command.
|
23
|
+
|
24
|
+
To see existing members of a domain or application, use:
|
25
|
+
|
26
|
+
rhc members -n <domain_name> [-a <app_name>]
|
27
|
+
|
28
|
+
To change the role for a user, simply call the add-member command with the new role. You
|
29
|
+
cannot change the role of the owner.
|
30
|
+
DESC
|
31
|
+
syntax "<action>"
|
32
|
+
default_action :help
|
33
|
+
|
34
|
+
summary "List members of a domain or application"
|
35
|
+
syntax "<domain_or_app_name> [-n DOMAIN_NAME] [-a APP_NAME]"
|
36
|
+
description <<-DESC
|
37
|
+
Show the existing members of a domain or application - you can pass the name
|
38
|
+
of your domain with '-n', the name of your application with '-a', or combine
|
39
|
+
them in the first argument to the command like:
|
40
|
+
|
41
|
+
rhc members <domain_name>/[<app_name>]
|
42
|
+
|
43
|
+
The owner is always listed first. To see the unique ID of members, pass
|
44
|
+
'--ids'.
|
45
|
+
DESC
|
46
|
+
option ['--ids'], "Display the IDs of each member", :optional => true
|
47
|
+
takes_application_or_domain :argument => true
|
48
|
+
alias_action :members, :root_command => true
|
49
|
+
def list(path)
|
50
|
+
target = find_app_or_domain(path)
|
51
|
+
members = target.members.sort_by{ |m| [m.owner? ? 0 : 1, m.role_weight, m.name] }
|
52
|
+
show_name = members.any?{ |m| m.name && m.name != m.login }
|
53
|
+
members.map! do |m|
|
54
|
+
[
|
55
|
+
((m.name || "") if show_name),
|
56
|
+
m.login || "",
|
57
|
+
m.owner? ? "#{m.role} (owner)" : m.role,
|
58
|
+
(m.id if options.ids)
|
59
|
+
].compact
|
60
|
+
end
|
61
|
+
say table(members, :header => [('Name' if show_name), 'Login', 'Role', ("ID" if options.ids)].compact)
|
62
|
+
|
63
|
+
0
|
64
|
+
end
|
65
|
+
|
66
|
+
summary "Add or update a member on a domain"
|
67
|
+
syntax "<login> [<login>...] [-n DOMAIN_NAME] [--role view|edit|admin] [--ids]"
|
68
|
+
description <<-DESC
|
69
|
+
Adds or updates members on a domain by passing one or more login
|
70
|
+
or ids for other people on OpenShift. The login and ID values for each
|
71
|
+
account are displayed in 'rhc account'. To change the role for a user, simply
|
72
|
+
call the add-member command with the new role. You cannot change the role of
|
73
|
+
the owner.
|
74
|
+
|
75
|
+
Roles
|
76
|
+
view - able to see information about the domain and its apps,
|
77
|
+
but not make any changes
|
78
|
+
edit - create, update, and delete applications, and has Git
|
79
|
+
and SSH access
|
80
|
+
admin - can update membership of a domain
|
81
|
+
|
82
|
+
The default role granted to members when added is 'edit' - use the '--role'
|
83
|
+
argument for 'view' or 'admin'.
|
84
|
+
|
85
|
+
Examples
|
86
|
+
rhc add-member sally joe -n mydomain
|
87
|
+
Gives the accounts with logins 'sally' and 'joe' edit access on mydomain
|
88
|
+
|
89
|
+
rhc add-member bob@example.com --role admin -n mydomain
|
90
|
+
Gives the account with login 'bob@example.com' admin access on mydomain
|
91
|
+
|
92
|
+
DESC
|
93
|
+
takes_domain
|
94
|
+
option ['--ids'], "Treat the arguments as a list of IDs", :optional => true
|
95
|
+
option ['-r', '--role ROLE'], "The role to give to each member - view, edit, or admin (default 'edit')", :type => Role, :optional => true
|
96
|
+
argument :members, "A list of members logins to add. Pass --ids to treat this as a list of IDs.", [], :type => :list
|
97
|
+
def add(members)
|
98
|
+
target = find_domain
|
99
|
+
role = options.role || 'edit'
|
100
|
+
raise ArgumentError, 'You must pass one or more logins or ids to this command' unless members.present?
|
101
|
+
say "Adding #{pluralize(members.length, role_name(role))} to #{target.class.model_name.downcase} ... "
|
102
|
+
target.update_members(changes_for(members, role))
|
103
|
+
success "done"
|
104
|
+
|
105
|
+
0
|
106
|
+
end
|
107
|
+
|
108
|
+
summary "Remove a member from a domain"
|
109
|
+
syntax "<login> [<login>...] [-n DOMAIN_NAME] [--ids]"
|
110
|
+
description <<-DESC
|
111
|
+
Remove members on a domain by passing one or more login or ids for each
|
112
|
+
member you wish to remove. View the list of existing members with
|
113
|
+
'rhc members <domain_name>'.
|
114
|
+
|
115
|
+
Pass '--all' to remove all but the owner from the domain.
|
116
|
+
DESC
|
117
|
+
takes_domain
|
118
|
+
option ['--ids'], "Treat the arguments as a list of IDs"
|
119
|
+
option ['--all'], "Remove all members from this domain."
|
120
|
+
argument :members, "Member logins to remove from the domain. Pass --ids to treat this as a list of IDs.", [], :type => :list
|
121
|
+
def remove(members)
|
122
|
+
target = find_domain
|
123
|
+
|
124
|
+
if options.all
|
125
|
+
say "Removing all members from #{target.class.model_name.downcase} ... "
|
126
|
+
target.delete_members
|
127
|
+
success "done"
|
128
|
+
|
129
|
+
else
|
130
|
+
raise ArgumentError, 'You must pass one or more logins or ids to this command' unless members.present?
|
131
|
+
say "Removing #{pluralize(members.length, 'member')} from #{target.class.model_name.downcase} ... "
|
132
|
+
target.update_members(changes_for(members, 'none'))
|
133
|
+
success "done"
|
134
|
+
end
|
135
|
+
|
136
|
+
0
|
137
|
+
end
|
138
|
+
|
139
|
+
protected
|
140
|
+
def changes_for(members, role)
|
141
|
+
members.map do |m|
|
142
|
+
h = {:role => role}
|
143
|
+
h[options.ids ? :id : :login] = m
|
144
|
+
h
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module RHC::Commands
|
4
|
+
class ForwardingSpec
|
5
|
+
include RHC::Helpers
|
6
|
+
include Enumerable
|
7
|
+
# class to represent how SSH port forwarding should be performed
|
8
|
+
attr_accessor :port_from
|
9
|
+
attr_reader :remote_host, :port_to, :host_from, :service
|
10
|
+
attr_writer :bound
|
11
|
+
|
12
|
+
def initialize(service, remote_host, port_to, port_from = nil)
|
13
|
+
@service = service
|
14
|
+
@remote_host = remote_host
|
15
|
+
@port_to = port_to
|
16
|
+
@host_from = '127.0.0.1'
|
17
|
+
@port_from = port_from || port_to # match ports if possible
|
18
|
+
@bound = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_cmd_arg
|
22
|
+
# string to be used in a direct SSH command
|
23
|
+
"-L #{port_from}:#{remote_host}:#{port_to}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_fwd_args
|
27
|
+
# array of arguments to be passed to Net::SSH::Service::Forward#local
|
28
|
+
[port_from.to_i, remote_host, port_to.to_i]
|
29
|
+
end
|
30
|
+
|
31
|
+
def bound?
|
32
|
+
@bound
|
33
|
+
end
|
34
|
+
|
35
|
+
# :nocov: These are for sorting. No need to test for coverage.
|
36
|
+
def <=>(other)
|
37
|
+
if bound? && !other.bound?
|
38
|
+
-1
|
39
|
+
elsif !bound? && other.bound?
|
40
|
+
1
|
41
|
+
else
|
42
|
+
order_by_attrs(other, :service, :remote_host, :port_from)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def order_by_attrs(other, *attrs)
|
47
|
+
# compare self and "other" by examining their "attrs" in order
|
48
|
+
# attrs should be an array of symbols to which self and "other"
|
49
|
+
# respond when sent.
|
50
|
+
while attribute = attrs.shift do
|
51
|
+
if self.send(attribute) != other.send(attribute)
|
52
|
+
return self.send(attribute) <=> other.send(attribute)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
0
|
56
|
+
end
|
57
|
+
# :nocov:
|
58
|
+
|
59
|
+
private :order_by_attrs
|
60
|
+
end
|
61
|
+
|
62
|
+
class PortForward < Base
|
63
|
+
|
64
|
+
UP_TO_256 = /25[0-5]|2[0-4][0-9]|[01]?(?:[0-9][0-9]?)/
|
65
|
+
UP_TO_65535 = /6553[0-5]|655[0-2][0-9]|65[0-4][0-9][0-9]|6[0-4][0-9][0-9][0-9]|[0-5]?(?:[0-9][0-9]{0,3})/
|
66
|
+
# 'host' part is a bit lax; we rely on 'rhc-list-ports' to hand us a reasonable output
|
67
|
+
# about the host information, be it numeric or FQDN in IPv4 or IPv6.
|
68
|
+
HOST_AND_PORT = /(.+):(#{UP_TO_65535})\b/
|
69
|
+
|
70
|
+
summary "Forward remote ports to the workstation"
|
71
|
+
syntax "<application>"
|
72
|
+
takes_application :argument => true
|
73
|
+
option ["-g", "--gear ID"], "Gear ID you are port forwarding to (optional)"
|
74
|
+
def run(app)
|
75
|
+
rest_app = find_app
|
76
|
+
ssh_uri = URI.parse(options.gear ? rest_app.gear_ssh_url(options.gear) : rest_app.ssh_url)
|
77
|
+
|
78
|
+
say "Using #{ssh_uri}..." if options.debug
|
79
|
+
|
80
|
+
forwarding_specs = []
|
81
|
+
|
82
|
+
begin
|
83
|
+
say "Checking available ports ... "
|
84
|
+
|
85
|
+
Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
|
86
|
+
# If a specific gear is targeted, do not include remote (e.g. database) ports
|
87
|
+
list_ports_cmd = "rhc-list-ports#{options.gear ? ' --exclude-remote' : ''}"
|
88
|
+
ssh.exec! list_ports_cmd do |channel, stream, data|
|
89
|
+
if stream == :stderr
|
90
|
+
data.each_line do |line|
|
91
|
+
line.chomp!
|
92
|
+
# FIXME: This is really brittle; there must be a better way
|
93
|
+
# for the server to tell us that permission (what permission?)
|
94
|
+
# is denied.
|
95
|
+
raise RHC::PermissionDeniedException.new "Permission denied." if line =~ /permission denied/i
|
96
|
+
# ...and also which services are available for the application
|
97
|
+
# for us to forward ports for.
|
98
|
+
if line =~ /\A\s*(\S+) -> #{HOST_AND_PORT}\z/
|
99
|
+
debug fs = ForwardingSpec.new($1, $2, $3.to_i)
|
100
|
+
forwarding_specs << fs
|
101
|
+
else
|
102
|
+
debug line
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if forwarding_specs.length == 0
|
110
|
+
# check if the gears have been stopped
|
111
|
+
if rest_app.gear_groups.all?{ |gg| gg.gears.all?{ |g| g["state"] == "stopped" } }
|
112
|
+
warn "none"
|
113
|
+
error "The application is stopped. Please restart the application and try again."
|
114
|
+
return 1
|
115
|
+
else
|
116
|
+
warn "none"
|
117
|
+
raise RHC::NoPortsToForwardException.new "There are no available ports to forward for this application. Your application may be stopped or idled."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
success "done"
|
122
|
+
|
123
|
+
begin
|
124
|
+
Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
|
125
|
+
say "Forwarding ports ..."
|
126
|
+
forwarding_specs.each do |fs|
|
127
|
+
given_up = nil
|
128
|
+
while !fs.bound? && !given_up
|
129
|
+
begin
|
130
|
+
args = fs.to_fwd_args
|
131
|
+
debug args.inspect
|
132
|
+
ssh.forward.local(*args)
|
133
|
+
fs.bound = true
|
134
|
+
rescue Errno::EADDRINUSE, Errno::EACCES => e
|
135
|
+
warn "#{e} while forwarding port #{fs.port_from}. Trying local port #{fs.port_from+1}"
|
136
|
+
fs.port_from += 1
|
137
|
+
rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
|
138
|
+
given_up = true
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
bound_ports = forwarding_specs.select(&:bound?)
|
144
|
+
if bound_ports.length > 0
|
145
|
+
paragraph{ say "To connect to a service running on OpenShift, use the Local address" }
|
146
|
+
paragraph do
|
147
|
+
say table(
|
148
|
+
bound_ports.map do |fs|
|
149
|
+
[fs.service, "#{fs.host_from}:#{fs.port_from}", " => ", "#{fs.remote_host}:#{fs.port_to.to_s}"]
|
150
|
+
end,
|
151
|
+
:header => ["Service", "Local", " ", "OpenShift"]
|
152
|
+
)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# for failed port forwarding attempts
|
157
|
+
failed_port_forwards = forwarding_specs.select { |fs| !fs.bound? }
|
158
|
+
if failed_port_forwards.length > 0
|
159
|
+
ssh_cmd_arg = failed_port_forwards.map { |fs| fs.to_cmd_arg }.join(" ")
|
160
|
+
ssh_cmd = "ssh -N #{ssh_cmd_arg} #{ssh_uri.user}@#{ssh_uri.host}"
|
161
|
+
warn "Error forwarding some port(s). You can try to forward manually by running:\n#{ssh_cmd}"
|
162
|
+
else
|
163
|
+
say "Press CTRL-C to terminate port forwarding"
|
164
|
+
end
|
165
|
+
|
166
|
+
unless forwarding_specs.any?(&:bound?)
|
167
|
+
warn "No ports have been bound"
|
168
|
+
return
|
169
|
+
end
|
170
|
+
ssh.loop { true }
|
171
|
+
end
|
172
|
+
rescue Interrupt
|
173
|
+
say " Ending port forward"
|
174
|
+
return 0
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
|
180
|
+
ssh_cmd = ["ssh","-N"]
|
181
|
+
unbound_fs = forwarding_specs.select { |fs| !fs.bound? }
|
182
|
+
ssh_cmd += unbound_fs.map { |fs| fs.to_cmd_arg }
|
183
|
+
ssh_cmd += ["#{ssh_uri.user}@#{ssh_uri.host}"]
|
184
|
+
raise RHC::PortForwardFailedException.new("#{e.message + "\n" if options.debug}Error trying to forward ports. You can try to forward manually by running:\n" + ssh_cmd.join(" "))
|
185
|
+
end
|
186
|
+
|
187
|
+
0
|
188
|
+
rescue RHC::Rest::ConnectionException => e
|
189
|
+
error "Connection to #{openshift_server} failed: #{e.message}"
|
190
|
+
1
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# mock for windows
|
196
|
+
if defined?(UNIXServer) != 'constant' or UNIXServer.class != Class then class UNIXServer; end; end
|
197
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rhc/commands/app'
|
2
|
+
|
3
|
+
module RHC::Commands
|
4
|
+
class Reload < App
|
5
|
+
|
6
|
+
summary "Reload the application's configuration"
|
7
|
+
syntax "<app> [--namespace NAME]"
|
8
|
+
takes_application :argument => true
|
9
|
+
|
10
|
+
def run(app)
|
11
|
+
self.class.superclass.instance_method(:reload).bind(self).call app
|
12
|
+
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rhc/commands/app'
|
2
|
+
|
3
|
+
module RHC::Commands
|
4
|
+
class Restart < App
|
5
|
+
|
6
|
+
summary "Restart the application"
|
7
|
+
syntax "<app> [--namespace NAME]"
|
8
|
+
takes_application :argument => true
|
9
|
+
|
10
|
+
def run(app)
|
11
|
+
self.class.superclass.instance_method(:restart).bind(self).call app
|
12
|
+
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rhc/commands/base'
|
2
|
+
require 'rhc/scp_helpers'
|
3
|
+
|
4
|
+
|
5
|
+
module RHC::Commands
|
6
|
+
class Scp < Base
|
7
|
+
suppress_wizard
|
8
|
+
|
9
|
+
summary "SCP a file to or from your application"
|
10
|
+
description <<-DESC
|
11
|
+
Transfer files to and from your applications using SCP. This will transfer
|
12
|
+
files to and from your primary gear (the one with the Git repository and web
|
13
|
+
cartridge) by default.
|
14
|
+
|
15
|
+
Examples:
|
16
|
+
Uploading a file from your workding directory to your app-root/data directory
|
17
|
+
app scp myapp upload somefile.txt app-root/data
|
18
|
+
|
19
|
+
Downloading a file from your app-root/data directory to your working directory
|
20
|
+
app scp myapp download ./ app-root/data/somebigarchive.tar.gz
|
21
|
+
DESC
|
22
|
+
syntax "[<app> --] <action> <local_path> <remote_path>"
|
23
|
+
takes_application :argument => true
|
24
|
+
argument :action, "Transfer direction: upload|download", ["-t", "--transfer_direction upload|download"], :optional => false
|
25
|
+
argument :local_path, "Local filesystem path", ["-l", "--local_path file_path"], :optional => false
|
26
|
+
argument :remote_path, "Remote filesystem path", ["-r", "--remote_path file_path"], :optional => false
|
27
|
+
alias_action 'app scp', :root_command => true
|
28
|
+
def run(_, action, local_path, remote_path)
|
29
|
+
rest_app = find_app
|
30
|
+
ssh_opts = rest_app.ssh_url.gsub("ssh://","").split("@")
|
31
|
+
|
32
|
+
raise RHC::ArgumentNotValid.new("'#{action}' is not a valid argument for this command. Please use upload or download.") unless action == 'download' || action == 'upload'
|
33
|
+
raise RHC::FileOrPathNotFound.new("Local file, file_path, or directory could not be found.") unless File.exist?(local_path)
|
34
|
+
|
35
|
+
begin
|
36
|
+
start_time = Time.now
|
37
|
+
Net::SCP.send("#{action}!".to_sym, ssh_opts[1], ssh_opts[0], (action == 'upload' ? local_path : remote_path), (action == 'upload' ? remote_path : local_path)) do |ch, name, sent, total|
|
38
|
+
#:nocov:
|
39
|
+
$stderr.print "\r #{action}ing #{name}: #{((sent.to_f/total.to_f)*100).to_i}% complete. #{sent}/#{total} bytes transferred " + (sent == total ? "in #{Time.now - start_time} seconds \n" : "")
|
40
|
+
#:nocov:
|
41
|
+
end
|
42
|
+
rescue Errno::ECONNREFUSED
|
43
|
+
raise RHC::SSHConnectionRefused.new(ssh_opts[0], ssh_opts[1])
|
44
|
+
rescue SocketError => e
|
45
|
+
raise RHC::ConnectionFailed, "The connection to #{ssh_opts[1]} failed: #{e.message}"
|
46
|
+
rescue Net::SCP::Error => e
|
47
|
+
raise RHC::RemoteFileOrPathNotFound.new("Remote file, file_path, or directory could not be found.")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
include RHC::SCPHelpers
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RHC::Commands
|
2
|
+
class Server < Base
|
3
|
+
suppress_wizard
|
4
|
+
|
5
|
+
summary "Display information about the status of the OpenShift service"
|
6
|
+
description <<-DESC
|
7
|
+
Retrieves any open issues or notices about the operation of the
|
8
|
+
OpenShift service and displays them in the order they were opened.
|
9
|
+
|
10
|
+
When connected to an OpenShift Enterprise server, will only display
|
11
|
+
the version of the API that it is connecting to.
|
12
|
+
DESC
|
13
|
+
def run
|
14
|
+
say "Connected to #{openshift_server}"
|
15
|
+
|
16
|
+
if openshift_online_server?
|
17
|
+
#status = decode_json(get("#{openshift_url}/app/status/status.json").body)
|
18
|
+
status = rest_client.request(:method => :get, :url => "#{openshift_url}/app/status/status.json", :lazy_auth => true){ |res| decode_json(res.content) }
|
19
|
+
open = status['open']
|
20
|
+
|
21
|
+
(success 'All systems running fine' and return 0) if open.blank?
|
22
|
+
|
23
|
+
open.each do |i|
|
24
|
+
i = i['issue']
|
25
|
+
say color("%-3s %s" % ["##{i['id']}", i['title']], :bold)
|
26
|
+
items = i['updates'].map{ |u| [u['description'], date(u['created_at'])] }
|
27
|
+
items.unshift ['Opened', date(i['created_at'])]
|
28
|
+
table(items, :align => [nil,:right], :join => ' ').each{ |s| say " #{s}" }
|
29
|
+
end
|
30
|
+
say "\n"
|
31
|
+
warn pluralize(open.length, "open issue")
|
32
|
+
|
33
|
+
open.length #exit with the count of open items
|
34
|
+
else
|
35
|
+
success "Using API version #{rest_client.api_version_negotiated}"
|
36
|
+
0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rhc/commands/base'
|
2
|
+
require 'rhc/wizard'
|
3
|
+
require 'rhc/config'
|
4
|
+
|
5
|
+
module RHC::Commands
|
6
|
+
class Setup < Base
|
7
|
+
suppress_wizard
|
8
|
+
|
9
|
+
summary "Connects to StartApp and sets up your keys and domain"
|
10
|
+
description <<-DESC
|
11
|
+
Connects to an StartApp server to get you started. Will help you
|
12
|
+
configure your SSH keys, set up a domain, and check for any potential
|
13
|
+
problems with Git or SSH.
|
14
|
+
|
15
|
+
Any options you pass to the setup command will be stored in a
|
16
|
+
.openshift/express.conf file in your home directory. If you run
|
17
|
+
setup at a later time, any previous configuration will be reused.
|
18
|
+
|
19
|
+
Pass the --clean option to ignore your saved configuration and only
|
20
|
+
use options you pass on the command line. Pass --config FILE to use
|
21
|
+
default values from another config (the values will still be written
|
22
|
+
to .startapp/express.conf).
|
23
|
+
|
24
|
+
If the server supports authorization tokens, you may pass the
|
25
|
+
--create-token option to instruct the wizard to generate a key for you.
|
26
|
+
|
27
|
+
If you would like to enable tab-completion in Bash shells, pass
|
28
|
+
--autocomplete for more information.
|
29
|
+
DESC
|
30
|
+
option ["--server NAME"], "Hostname of an OpenShift server", :default => :server_context, :required => true
|
31
|
+
option ['--clean'], "Ignore any saved configuration options"
|
32
|
+
option ['--[no-]create-token'], "Create an authorization token for this server"
|
33
|
+
option ['--autocomplete'], "Instructions for enabling tab-completion"
|
34
|
+
def run
|
35
|
+
if options.autocomplete
|
36
|
+
src = File.join(File.join(Gem.loaded_specs['app'].full_gem_path, "autocomplete"), "rhc_bash")
|
37
|
+
dest = File.join(RHC::Config.home_conf_dir, "bash_autocomplete")
|
38
|
+
|
39
|
+
FileUtils.mkdir_p(RHC::Config.home_conf_dir)
|
40
|
+
FileUtils.cp(src, dest)
|
41
|
+
|
42
|
+
say <<-LINE.strip_heredoc
|
43
|
+
To enable tab-completion for `app` under Bash shells, add the following command to
|
44
|
+
your .bashrc or .bash_profile file:
|
45
|
+
|
46
|
+
. #{dest}
|
47
|
+
|
48
|
+
Save your shell and then restart. Type "app" and then hit the TAB key twice to
|
49
|
+
trigger completion of your command.
|
50
|
+
|
51
|
+
Tab-completion is not available in the Windows terminal.
|
52
|
+
LINE
|
53
|
+
return 0
|
54
|
+
end
|
55
|
+
|
56
|
+
raise OptionParser::InvalidOption, "Setup can not be run with the --noprompt option" if options.noprompt
|
57
|
+
RHC::RerunWizard.new(config, options).run ? 0 : 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|