pbox 1.17.2
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 +40 -0
- data/Rakefile +6 -0
- data/autocomplete/pbox_bash +1639 -0
- data/bin/pbox +37 -0
- data/conf/protonbox.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +178 -0
- data/features/deployments_feature.rb +127 -0
- data/features/domains_feature.rb +49 -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 +186 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +701 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +96 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +326 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +167 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/git_clone.rb +29 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -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/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +320 -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 +88 -0
- data/lib/rhc/exceptions.rb +232 -0
- data/lib/rhc/git_helpers.rb +91 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +477 -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 +332 -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 +154 -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 +1024 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/ssh_helpers.rb +378 -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 +35 -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 +633 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +89 -0
- data/spec/direct_execution_helper.rb +338 -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 +188 -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 +754 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +145 -0
- data/spec/rhc/commands/cartridge_spec.rb +641 -0
- data/spec/rhc/commands/deployment_spec.rb +286 -0
- data/spec/rhc/commands/domain_spec.rb +383 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +80 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +228 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -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 +524 -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 +248 -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 +435 -0
|
@@ -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 'pbox-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 = "pbox-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 ProtonBox, 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", " ", "ProtonBox"]
|
|
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 #{protonbox_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,40 @@
|
|
|
1
|
+
module RHC::Commands
|
|
2
|
+
class Server < Base
|
|
3
|
+
suppress_wizard
|
|
4
|
+
|
|
5
|
+
summary "Display information about the status of the ProtonBox service"
|
|
6
|
+
description <<-DESC
|
|
7
|
+
Retrieves any open issues or notices about the operation of the
|
|
8
|
+
ProtonBox service and displays them in the order they were opened.
|
|
9
|
+
|
|
10
|
+
When connected to an ProtonBox 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 #{protonbox_server}"
|
|
15
|
+
|
|
16
|
+
if protonbox_online_server?
|
|
17
|
+
#status = decode_json(get("#{protonbox_url}/app/status/status.json").body)
|
|
18
|
+
status = rest_client.request(:method => :get, :url => "#{protonbox_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 ProtonBox and sets up your keys and domain"
|
|
10
|
+
description <<-DESC
|
|
11
|
+
Connects to an ProtonBox 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
|
+
.protonbox/protonbox.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 .protonbox/protonbox.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 the ProtonBox 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['pbox'].full_gem_path, "autocomplete"), "pbox_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 PBOX 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 "pbox" 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
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'rhc/commands/base'
|
|
2
|
+
|
|
3
|
+
module RHC::Commands
|
|
4
|
+
class Snapshot < Base
|
|
5
|
+
summary "Save the current state of your application locally"
|
|
6
|
+
syntax "<action>"
|
|
7
|
+
description <<-DESC
|
|
8
|
+
Snapshots allow you to export the current state of your ProtonBox application
|
|
9
|
+
into an archive on your local system, and then to restore it later.
|
|
10
|
+
|
|
11
|
+
The snapshot archive contains the Git repository, dumps of any attached databases,
|
|
12
|
+
and any other information that the cartridges decide to export.
|
|
13
|
+
|
|
14
|
+
WARNING: Both 'save' and 'restore' will stop the application and then restart
|
|
15
|
+
after the operation completes.
|
|
16
|
+
DESC
|
|
17
|
+
alias_action :"app snapshot", :root_command => true
|
|
18
|
+
default_action :help
|
|
19
|
+
|
|
20
|
+
summary "Save a snapshot of your app to disk"
|
|
21
|
+
syntax "<application> [--filepath FILE] [--ssh path_to_ssh_executable]"
|
|
22
|
+
takes_application :argument => true
|
|
23
|
+
option ["-f", "--filepath FILE"], "Local path to save tarball (default: ./$APPNAME.tar.gz)"
|
|
24
|
+
option ["--deployment"], "Snapshot as a deployable file which can be deployed with 'pbox deploy'"
|
|
25
|
+
option ["--ssh PATH"], "Full path to your SSH executable with additional options"
|
|
26
|
+
alias_action :"app snapshot save", :root_command => true, :deprecated => true
|
|
27
|
+
def save(app)
|
|
28
|
+
ssh = check_ssh_executable! options.ssh
|
|
29
|
+
rest_app = find_app
|
|
30
|
+
|
|
31
|
+
raise RHC::DeploymentsNotSupportedException.new if options.deployment && !rest_app.supports?("DEPLOY")
|
|
32
|
+
|
|
33
|
+
ssh_uri = URI.parse(rest_app.ssh_url)
|
|
34
|
+
filename = options.filepath ? options.filepath : "#{rest_app.name}.tar.gz"
|
|
35
|
+
|
|
36
|
+
snapshot_cmd = options.deployment ? 'gear archive-deployment' : 'snapshot'
|
|
37
|
+
ssh_cmd = "#{ssh} #{ssh_uri.user}@#{ssh_uri.host} '#{snapshot_cmd}' > #{filename}"
|
|
38
|
+
debug ssh_cmd
|
|
39
|
+
|
|
40
|
+
say "Pulling down a snapshot to #{filename}..."
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
if !RHC::Helpers.windows?
|
|
44
|
+
status, output = exec(ssh_cmd)
|
|
45
|
+
if status != 0
|
|
46
|
+
debug output
|
|
47
|
+
raise RHC::SnapshotSaveException.new "Error in trying to save snapshot. You can try to save manually by running:\n#{ssh_cmd}"
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
Net::SSH.start(ssh_uri.host, ssh_uri.user) do |ssh|
|
|
51
|
+
File.open(filename, 'wb') do |file|
|
|
52
|
+
ssh.exec! "snapshot" do |channel, stream, data|
|
|
53
|
+
if stream == :stdout
|
|
54
|
+
file.write(data)
|
|
55
|
+
else
|
|
56
|
+
debug data
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
|
|
63
|
+
debug e.backtrace
|
|
64
|
+
raise RHC::SnapshotSaveException.new "Error in trying to save snapshot. You can try to save manually by running:\n#{ssh_cmd}"
|
|
65
|
+
end
|
|
66
|
+
results { say "Success" }
|
|
67
|
+
0
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
summary "Restores a previously saved snapshot"
|
|
71
|
+
syntax "<application> [--filepath FILE] [--ssh path_to_ssh_executable]"
|
|
72
|
+
takes_application :argument => true
|
|
73
|
+
option ["-f", "--filepath FILE"], "Local path to restore tarball"
|
|
74
|
+
option ["--ssh PATH"], "Full path to your SSH executable with additional options"
|
|
75
|
+
alias_action :"app snapshot restore", :root_command => true, :deprecated => true
|
|
76
|
+
def restore(app)
|
|
77
|
+
ssh = check_ssh_executable! options.ssh
|
|
78
|
+
rest_app = find_app
|
|
79
|
+
filename = options.filepath ? options.filepath : "#{rest_app.name}.tar.gz"
|
|
80
|
+
|
|
81
|
+
if File.exists? filename
|
|
82
|
+
|
|
83
|
+
include_git = RHC::Helpers.windows? ? true : RHC::TarGz.contains(filename, './*/git')
|
|
84
|
+
ssh_uri = URI.parse(rest_app.ssh_url)
|
|
85
|
+
|
|
86
|
+
ssh_cmd = "cat '#{filename}' | #{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'"
|
|
87
|
+
|
|
88
|
+
say "Restoring from snapshot #{filename}..."
|
|
89
|
+
debug ssh_cmd
|
|
90
|
+
|
|
91
|
+
begin
|
|
92
|
+
if !RHC::Helpers.windows?
|
|
93
|
+
status, output = exec(ssh_cmd)
|
|
94
|
+
if status != 0
|
|
95
|
+
debug output
|
|
96
|
+
raise RHC::SnapshotRestoreException.new "Error in trying to restore snapshot. You can try to restore manually by running:\n#{ssh_cmd}"
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
ssh = Net::SSH.start(ssh_uri.host, ssh_uri.user)
|
|
100
|
+
ssh.open_channel do |channel|
|
|
101
|
+
channel.exec("restore#{include_git ? ' INCLUDE_GIT' : ''}") do |ch, success|
|
|
102
|
+
channel.on_data do |ch, data|
|
|
103
|
+
say data
|
|
104
|
+
end
|
|
105
|
+
channel.on_extended_data do |ch, type, data|
|
|
106
|
+
say data
|
|
107
|
+
end
|
|
108
|
+
channel.on_close do |ch|
|
|
109
|
+
say "Terminating..."
|
|
110
|
+
end
|
|
111
|
+
File.open(filename, 'rb') do |file|
|
|
112
|
+
file.chunk(1024) do |chunk|
|
|
113
|
+
channel.send_data chunk
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
channel.eof!
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
ssh.loop
|
|
120
|
+
end
|
|
121
|
+
rescue Timeout::Error, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Net::SSH::AuthenticationFailed => e
|
|
122
|
+
debug e.backtrace
|
|
123
|
+
raise RHC::SnapshotRestoreException.new "Error in trying to restore snapshot. You can try to restore manually by running:\n#{ssh_cmd}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
else
|
|
127
|
+
raise RHC::SnapshotRestoreException.new "Archive not found: #{filename}"
|
|
128
|
+
end
|
|
129
|
+
results { say "Success" }
|
|
130
|
+
0
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
protected
|
|
134
|
+
include RHC::SSHHelpers
|
|
135
|
+
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rhc/commands/base'
|
|
2
|
+
require 'resolv'
|
|
3
|
+
require 'rhc/git_helpers'
|
|
4
|
+
require 'rhc/cartridge_helpers'
|
|
5
|
+
|
|
6
|
+
module RHC::Commands
|
|
7
|
+
class Ssh < Base
|
|
8
|
+
suppress_wizard
|
|
9
|
+
|
|
10
|
+
summary "SSH into the specified application"
|
|
11
|
+
description <<-DESC
|
|
12
|
+
Connect to your application using SSH. This will connect to your primary gear
|
|
13
|
+
(the one with your Git repository and web cartridge) by default. To SSH to
|
|
14
|
+
other gears run 'pbox show-app --gears' to get a list of their SSH hosts.
|
|
15
|
+
|
|
16
|
+
You may run a specific SSH command by passing one or more arguments, or use a
|
|
17
|
+
different SSH executable or pass options to SSH with the '--ssh' option.
|
|
18
|
+
DESC
|
|
19
|
+
syntax "[--ssh path_to_ssh_executable] [--gears] [<app> --] <command>"
|
|
20
|
+
takes_application :argument => true
|
|
21
|
+
argument :command, "Command to run in the application's SSH session", ['--command COMMAND'], :type => :list, :optional => true
|
|
22
|
+
option ["--ssh PATH"], "Path to your SSH executable or additional options"
|
|
23
|
+
option ["--gears"], "Execute this command on all gears in the app. Requires a command."
|
|
24
|
+
option ["--limit INTEGER"], "Limit the number of simultaneous SSH connections opened with --gears (default: 5).", :type => Integer, :default => 5
|
|
25
|
+
option ["--raw"], "Output only the data returned by each host, no hostname prefix."
|
|
26
|
+
alias_action 'app ssh', :root_command => true
|
|
27
|
+
def run(_, command)
|
|
28
|
+
raise ArgumentError, "--gears requires a command" if options.gears && command.blank?
|
|
29
|
+
raise ArgumentError, "--limit must be an integer greater than zero" if options.limit && options.limit < 1
|
|
30
|
+
|
|
31
|
+
ssh = check_ssh_executable! options.ssh
|
|
32
|
+
|
|
33
|
+
if options.gears
|
|
34
|
+
run_on_gears(command.join(' '), find_app(:with_gear_groups => true))
|
|
35
|
+
0
|
|
36
|
+
else
|
|
37
|
+
rest_app = find_app
|
|
38
|
+
$stderr.puts "Connecting to #{rest_app.ssh_string.to_s} ..." unless command.present?
|
|
39
|
+
|
|
40
|
+
debug "Using user specified SSH: #{options.ssh}" if options.ssh
|
|
41
|
+
|
|
42
|
+
command_line = [ ssh.split, rest_app.ssh_string.to_s, command].flatten.compact
|
|
43
|
+
debug "Invoking Kernel.exec with #{command_line.inspect}"
|
|
44
|
+
Kernel.send(:exec, *command_line)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
include RHC::SSHHelpers
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'rhc/commands/base'
|
|
2
|
+
|
|
3
|
+
module RHC::Commands
|
|
4
|
+
class Sshkey < Base
|
|
5
|
+
include RHC::SSHHelpers
|
|
6
|
+
|
|
7
|
+
summary 'Add and remove keys for Git and SSH'
|
|
8
|
+
syntax '<action>'
|
|
9
|
+
description <<-DESC
|
|
10
|
+
ProtonBox uses public keys to securely access your application source
|
|
11
|
+
code and to control access to your application gears via SSH. Your
|
|
12
|
+
account may have one or more public SSH keys associated with it, and
|
|
13
|
+
any computer with the private SSH key will be able to download code
|
|
14
|
+
from Git or SSH to the application.
|
|
15
|
+
|
|
16
|
+
Depending on your operating system, you may have to ensure that both
|
|
17
|
+
Git and the local SSH installation have access to your keys. Running
|
|
18
|
+
the 'setup' command is any easy way to get your first key created and
|
|
19
|
+
uploaded.
|
|
20
|
+
DESC
|
|
21
|
+
default_action :list
|
|
22
|
+
|
|
23
|
+
summary 'Display all the SSH keys for your account'
|
|
24
|
+
syntax ''
|
|
25
|
+
def list
|
|
26
|
+
keys = rest_client.sshkeys.each{ |key| paragraph{ display_key(key) } }
|
|
27
|
+
|
|
28
|
+
success "You have #{keys.length} SSH keys associated with your account."
|
|
29
|
+
|
|
30
|
+
0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
summary 'Show the SSH key with the given name'
|
|
34
|
+
syntax '<name>'
|
|
35
|
+
argument :name, 'SSH key to display', []
|
|
36
|
+
def show(name)
|
|
37
|
+
key = rest_client.find_key(name)
|
|
38
|
+
display_key(key)
|
|
39
|
+
|
|
40
|
+
0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
summary 'Add SSH key to your account'
|
|
44
|
+
syntax '<name> <path to SSH key file>'
|
|
45
|
+
argument :name, 'Name for this key', []
|
|
46
|
+
argument :key, 'SSH public key filepath', [], :optional => true
|
|
47
|
+
option ['--confirm'], 'Bypass key validation'
|
|
48
|
+
option ['--type TYPE'], 'Provide the key type directly if no key file is given'
|
|
49
|
+
option ['--content CONTENT'], 'Provide the key content directly if no key file is given'
|
|
50
|
+
def add(name, key_path=nil)
|
|
51
|
+
|
|
52
|
+
if key_path
|
|
53
|
+
type, content, comment = ssh_key_triple_for(key_path)
|
|
54
|
+
elsif options[:type].present? and options[:content].present?
|
|
55
|
+
type = options[:type]
|
|
56
|
+
content = options[:content]
|
|
57
|
+
else
|
|
58
|
+
raise ArgumentError, "You must either provide a key file, or the key type and content"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if type == 'krb5-principal'
|
|
62
|
+
# TODO: validate krb5?
|
|
63
|
+
else
|
|
64
|
+
# validate the user input before sending it to the server
|
|
65
|
+
begin
|
|
66
|
+
Net::SSH::KeyFactory.load_data_public_key "#{type} #{content}"
|
|
67
|
+
rescue NotImplementedError, OpenSSL::PKey::PKeyError, Net::SSH::Exception => e
|
|
68
|
+
debug e.inspect
|
|
69
|
+
if options.confirm
|
|
70
|
+
warn 'The key you are uploading is not recognized. You may not be able to authenticate to your application through Git or SSH.'
|
|
71
|
+
else
|
|
72
|
+
raise ::RHC::KeyDataInvalidException.new("File '#{key_path}' does not appear to be a recognizable key file (#{e}). You may specify the '--confirm' flag to add the key anyway.") if key_path
|
|
73
|
+
raise ::RHC::KeyDataInvalidException.new("The provided type and content does not appear to be a recognizable key (#{e}). You may specify the '--confirm' flag to add the key anyway.")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
rest_client.add_key(name, content, type)
|
|
79
|
+
results { say key_path ? "SSH key #{key_path} has been added as '#{name}'" : "SSH key '#{name}' has been added" }
|
|
80
|
+
|
|
81
|
+
0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
summary 'Remove SSH key from your account'
|
|
85
|
+
syntax '<name>'
|
|
86
|
+
alias_action :delete, :deprecated => true
|
|
87
|
+
argument :name, 'Name of SSH key to remove'
|
|
88
|
+
def remove(name)
|
|
89
|
+
say "Removing the key '#{name} ... "
|
|
90
|
+
rest_client.delete_key(name)
|
|
91
|
+
|
|
92
|
+
success "removed"
|
|
93
|
+
|
|
94
|
+
0
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rhc/commands/base'
|
|
2
|
+
require 'rhc/config'
|
|
3
|
+
require 'rhc/ssh_helpers'
|
|
4
|
+
|
|
5
|
+
module RHC::Commands
|
|
6
|
+
class Tail < Base
|
|
7
|
+
include RHC::SSHHelpers
|
|
8
|
+
|
|
9
|
+
summary "Tail the logs of an application"
|
|
10
|
+
syntax "<application>"
|
|
11
|
+
takes_application :argument => true
|
|
12
|
+
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')"
|
|
13
|
+
option ["-f", "--files files"], "File glob relative to app (default <application_name>/logs/*) (optional)"
|
|
14
|
+
option ["-g", "--gear ID"], "Tail only a specific gear"
|
|
15
|
+
#option ["-c", "--cartridge name"], "Tail only a specific cartridge"
|
|
16
|
+
alias_action :"app tail", :root_command => true, :deprecated => true
|
|
17
|
+
def run(app_name)
|
|
18
|
+
rest_app = find_app(:include => :cartridges)
|
|
19
|
+
ssh_url = options.gear ? rest_app.gear_ssh_url(options.gear) : rest_app.ssh_url
|
|
20
|
+
|
|
21
|
+
tail('*', URI(ssh_url), options)
|
|
22
|
+
|
|
23
|
+
0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
#Application log file tailing
|
|
28
|
+
def tail(cartridge_name, ssh_url, options)
|
|
29
|
+
debug "Tail in progress for cartridge #{cartridge_name}"
|
|
30
|
+
|
|
31
|
+
host = ssh_url.host
|
|
32
|
+
uuid = ssh_url.user
|
|
33
|
+
|
|
34
|
+
file_glob = options.files ? options.files : "#{cartridge_name}/log*/*"
|
|
35
|
+
remote_cmd = "tail#{options.opts ? ' --opts ' + Base64::encode64(options.opts).chomp : ''} #{file_glob}"
|
|
36
|
+
ssh_cmd = "ssh -t #{uuid}@#{host} '#{remote_cmd}'"
|
|
37
|
+
begin
|
|
38
|
+
#Use ssh -t to tail the logs
|
|
39
|
+
debug ssh_cmd
|
|
40
|
+
ssh_ruby(host, uuid, remote_cmd, false, true)
|
|
41
|
+
rescue
|
|
42
|
+
warn "You can tail this application directly with:\n#{ssh_cmd}"
|
|
43
|
+
raise
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rhc/commands/base'
|
|
2
|
+
module RHC::Commands
|
|
3
|
+
class Threaddump < Base
|
|
4
|
+
summary "Trigger a thread dump for JBoss and Ruby apps"
|
|
5
|
+
syntax "<application>"
|
|
6
|
+
takes_application :argument => true
|
|
7
|
+
def run(app)
|
|
8
|
+
rest_app = find_app
|
|
9
|
+
rest_app.threaddump.messages.each { |m| say m }
|
|
10
|
+
|
|
11
|
+
0
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|