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,32 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module RHC
|
4
|
+
module Rest
|
5
|
+
class User < Base
|
6
|
+
define_attr :id, :login, :plan_id, :max_gears, :consumed_gears, :max_domains
|
7
|
+
|
8
|
+
def add_key(name, content, type)
|
9
|
+
debug "Add key #{name} of type #{type} for user #{login}"
|
10
|
+
rest_method "ADD_KEY", :name => name, :type => type, :content => content
|
11
|
+
end
|
12
|
+
|
13
|
+
def keys
|
14
|
+
debug "Getting all keys for user #{login}"
|
15
|
+
rest_method "LIST_KEYS"
|
16
|
+
end
|
17
|
+
|
18
|
+
#Find Key by name
|
19
|
+
def find_key(name)
|
20
|
+
keys.detect { |key| key.name == name }
|
21
|
+
end
|
22
|
+
|
23
|
+
def max_domains
|
24
|
+
attributes['max_domains'] || 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def capabilities
|
28
|
+
@capabilities ||= OpenStruct.new attribute('capabilities')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rhc/rest.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module RHC
|
2
|
+
module Rest
|
3
|
+
|
4
|
+
autoload :Base, 'rhc/rest/base'
|
5
|
+
autoload :Attributes, 'rhc/rest/attributes'
|
6
|
+
|
7
|
+
autoload :HTTPClient, 'rhc/rest/httpclient'
|
8
|
+
|
9
|
+
autoload :Alias, 'rhc/rest/alias'
|
10
|
+
autoload :Api, 'rhc/rest/api'
|
11
|
+
autoload :Application, 'rhc/rest/application'
|
12
|
+
autoload :Authorization, 'rhc/rest/authorization'
|
13
|
+
autoload :Cartridge, 'rhc/rest/cartridge'
|
14
|
+
autoload :Client, 'rhc/rest/client'
|
15
|
+
autoload :Deployment, 'rhc/rest/deployment'
|
16
|
+
autoload :Domain, 'rhc/rest/domain'
|
17
|
+
autoload :EnvironmentVariable, 'rhc/rest/environment_variable'
|
18
|
+
autoload :GearGroup, 'rhc/rest/gear_group'
|
19
|
+
autoload :Key, 'rhc/rest/key'
|
20
|
+
autoload :Membership, 'rhc/rest/membership'
|
21
|
+
autoload :User, 'rhc/rest/user'
|
22
|
+
|
23
|
+
class Exception < RuntimeError
|
24
|
+
attr_reader :code
|
25
|
+
def initialize(message=nil, code=1)
|
26
|
+
super(message)
|
27
|
+
@code = (Integer(code) rescue nil)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#Exceptions thrown in case of an HTTP 5xx is received.
|
32
|
+
class ServerErrorException < Exception; end
|
33
|
+
|
34
|
+
#Exceptions thrown in case of an HTTP 503 is received.
|
35
|
+
#
|
36
|
+
#503 Service Unavailable
|
37
|
+
#
|
38
|
+
#The server is currently unable to handle the request due to a temporary
|
39
|
+
#overloading or maintenance of the server. The implication is that this
|
40
|
+
#is a temporary condition which will be alleviated after some delay.
|
41
|
+
class ServiceUnavailableException < ServerErrorException; end
|
42
|
+
|
43
|
+
#Exceptions thrown in case of an HTTP 4xx is received with the exception
|
44
|
+
#of 401, 403, 403 and 422 where a more sepcific exception is thrown
|
45
|
+
#
|
46
|
+
#HTTP Error Codes 4xx
|
47
|
+
#
|
48
|
+
#The 4xx class of status code is intended for cases in which the client
|
49
|
+
#seems to have errored.
|
50
|
+
class ClientErrorException < Exception; end
|
51
|
+
|
52
|
+
#Exceptions thrown in case of an HTTP 404 is received.
|
53
|
+
#
|
54
|
+
#404 Not Found
|
55
|
+
#
|
56
|
+
#The server has not found anything matching the Request-URI or the
|
57
|
+
#requested resource does not exist
|
58
|
+
class ResourceNotFoundException < ClientErrorException; end
|
59
|
+
class ApiEndpointNotFound < ResourceNotFoundException; end
|
60
|
+
|
61
|
+
# 404 errors for specific resource types
|
62
|
+
class DomainNotFoundException < ResourceNotFoundException
|
63
|
+
def initialize(msg)
|
64
|
+
super(msg,127)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
class ApplicationNotFoundException < ResourceNotFoundException
|
68
|
+
def initialize(msg)
|
69
|
+
super(msg,101)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#Exceptions thrown in case of an HTTP 422 is received.
|
74
|
+
class ValidationException < ClientErrorException
|
75
|
+
attr_reader :field
|
76
|
+
def initialize(message, field=nil, error_code=1)
|
77
|
+
super(message, error_code)
|
78
|
+
@field = field
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#Exceptions thrown in case of an HTTP 403 is received.
|
83
|
+
#
|
84
|
+
#403 Forbidden
|
85
|
+
#
|
86
|
+
#The server understood the request, but is refusing to fulfill it.
|
87
|
+
#Authorization will not help and the request SHOULD NOT be repeated.
|
88
|
+
class RequestDeniedException < ClientErrorException; end
|
89
|
+
|
90
|
+
#Exceptions thrown in case of an HTTP 401 is received.
|
91
|
+
#
|
92
|
+
#401 Unauthorized
|
93
|
+
#
|
94
|
+
#The request requires user authentication. If the request already
|
95
|
+
#included Authorization credentials, then the 401 response indicates
|
96
|
+
#that authorization has been refused for those credentials.
|
97
|
+
class UnAuthorizedException < ClientErrorException; end
|
98
|
+
class TokenExpiredOrInvalid < UnAuthorizedException; end
|
99
|
+
|
100
|
+
# DEPRECATED Unreachable host, SSL Exception
|
101
|
+
class ResourceAccessException < Exception; end
|
102
|
+
|
103
|
+
#I/O Exceptions Connection timeouts, etc
|
104
|
+
class ConnectionException < Exception; end
|
105
|
+
class TimeoutException < ConnectionException
|
106
|
+
def initialize(message, nested=nil)
|
107
|
+
super(message)
|
108
|
+
@nested = nested
|
109
|
+
end
|
110
|
+
|
111
|
+
def on_receive?
|
112
|
+
@nested.is_a? HTTPClient::ReceiveTimeoutError
|
113
|
+
end
|
114
|
+
def on_connect?
|
115
|
+
@nested.is_a? HTTPClient::ConnectTimeoutError
|
116
|
+
end
|
117
|
+
def on_send?
|
118
|
+
@nested.is_a? HTTPClient::SendTimeoutError
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class SSLConnectionFailed < ConnectionException
|
123
|
+
attr_reader :reason
|
124
|
+
def initialize(reason, message)
|
125
|
+
super message
|
126
|
+
@reason = reason
|
127
|
+
end
|
128
|
+
end
|
129
|
+
class CertificateVerificationFailed < SSLConnectionFailed; end
|
130
|
+
class SelfSignedCertificate < CertificateVerificationFailed; end
|
131
|
+
|
132
|
+
class SSLVersionRejected < SSLConnectionFailed; end
|
133
|
+
|
134
|
+
class MultipleCartridgeCreationNotSupported < Exception; end
|
135
|
+
|
136
|
+
class DownloadingCartridgesNotSupported < Exception; end
|
137
|
+
|
138
|
+
class InitialGitUrlNotSupported < Exception; end
|
139
|
+
|
140
|
+
class SslCertificatesNotSupported < Exception; end
|
141
|
+
|
142
|
+
class AuthorizationsNotSupported < Exception
|
143
|
+
def initialize(message="The server does not support setting, retrieving, or authenticating with authorization tokens.")
|
144
|
+
super(message, 1)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
###
|
2
|
+
# ssh_key_helpers.rb - methods to help manipulate ssh keys
|
3
|
+
#
|
4
|
+
# Copyright 2012 Red Hat, Inc. and/or its affiliates.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'net/ssh'
|
19
|
+
require 'net/scp'
|
20
|
+
require 'rhc/ssh_helpers'
|
21
|
+
require 'rhc/vendor/sshkey'
|
22
|
+
|
23
|
+
module RHC
|
24
|
+
module SCPHelpers
|
25
|
+
include SSHHelpers
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,380 @@
|
|
1
|
+
###
|
2
|
+
# ssh_key_helpers.rb - methods to help manipulate ssh keys
|
3
|
+
#
|
4
|
+
# Copyright 2012 Red Hat, Inc. and/or its affiliates.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'net/ssh'
|
19
|
+
require 'rhc/vendor/sshkey'
|
20
|
+
require 'httpclient'
|
21
|
+
|
22
|
+
module RHC
|
23
|
+
module SSHHelpers
|
24
|
+
|
25
|
+
class MultipleGearTask
|
26
|
+
def initialize(command, over, opts={})
|
27
|
+
requires_ssh_multi!
|
28
|
+
|
29
|
+
@command = command
|
30
|
+
@over = over
|
31
|
+
@opts = opts
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(&block)
|
35
|
+
out = nil
|
36
|
+
|
37
|
+
Net::SSH::Multi.start(
|
38
|
+
:concurrent_connections => @opts[:limit],
|
39
|
+
:on_error => lambda{ |server| $stderr.puts RHC::Helpers.color("Unable to connect to gear #{server}", :red) }
|
40
|
+
) do |session|
|
41
|
+
|
42
|
+
@over.each do |item|
|
43
|
+
case item
|
44
|
+
when RHC::Rest::GearGroup
|
45
|
+
item.gears.each do |gear|
|
46
|
+
session.use ssh_host_for(gear), :properties => {:gear => gear, :group => item}
|
47
|
+
end
|
48
|
+
#when RHC::Rest::Gear
|
49
|
+
# session.use ssh_host_for(item), :properties => {:gear => item}
|
50
|
+
#end
|
51
|
+
else
|
52
|
+
raise "Cannot establish an SSH session to this type"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
session.exec @command, &(
|
56
|
+
case
|
57
|
+
when @opts[:raw]
|
58
|
+
lambda { |ch, dest, data|
|
59
|
+
(dest == :stdout ? $stdout : $stderr).puts data
|
60
|
+
}
|
61
|
+
when @opts[:as] == :table
|
62
|
+
out = []
|
63
|
+
lambda { |ch, dest, data|
|
64
|
+
label = label_for(ch)
|
65
|
+
data.chomp.each_line do |line|
|
66
|
+
row = out.find{ |row| row[0] == label } || (out << [label, []])[-1]
|
67
|
+
row[1] << line
|
68
|
+
end
|
69
|
+
}
|
70
|
+
when @opts[:as] == :gear
|
71
|
+
lambda { |ch, dest, data| (ch.connection.properties[:gear]['data'] ||= "") << data }
|
72
|
+
else
|
73
|
+
width = 0
|
74
|
+
lambda { |ch, dest, data|
|
75
|
+
label = label_for(ch)
|
76
|
+
io = dest == :stdout ? $stdout : $stderr
|
77
|
+
data.chomp!
|
78
|
+
|
79
|
+
if data.each_line.to_a.count < 2
|
80
|
+
io.puts "[#{label}] #{data}"
|
81
|
+
elsif @opts[:always_prefix]
|
82
|
+
data.each_line do |line|
|
83
|
+
io.puts "[#{label}] #{line}"
|
84
|
+
end
|
85
|
+
else
|
86
|
+
io.puts "=== #{label}"
|
87
|
+
io.puts data
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end)
|
91
|
+
session.loop
|
92
|
+
end
|
93
|
+
|
94
|
+
if block_given? && !@opts[:raw]
|
95
|
+
case
|
96
|
+
when @opts[:as] == :gear
|
97
|
+
out = []
|
98
|
+
@over.each do |item|
|
99
|
+
case item
|
100
|
+
when RHC::Rest::GearGroup then item.gears.each{ |gear| out << yield(gear, gear['data'], item) }
|
101
|
+
#when RHC::Rest::Gear then out << yield(gear, gear['data'], nil)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
out
|
108
|
+
end
|
109
|
+
protected
|
110
|
+
def ssh_host_for(gear)
|
111
|
+
RHC::Helpers.ssh_string(gear['ssh_url']) or raise NoPerGearOperations
|
112
|
+
end
|
113
|
+
|
114
|
+
def label_for(channel)
|
115
|
+
channel.properties[:label] ||=
|
116
|
+
begin
|
117
|
+
group = channel.connection.properties[:group]
|
118
|
+
"#{key_for(channel)} #{group.cartridges.map{ |c| c['name'] }.join('+')}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def key_for(channel)
|
123
|
+
channel.connection.properties[:gear]['id']
|
124
|
+
end
|
125
|
+
|
126
|
+
def requires_ssh_multi!
|
127
|
+
begin
|
128
|
+
require 'net/ssh/multi'
|
129
|
+
rescue LoadError
|
130
|
+
raise RHC::OperationNotSupportedException, "You must install Net::SSH::Multi to use the --gears option. Most systems: 'gem install net-ssh-multi'"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def run_on_gears(command, gears, opts={}, &block)
|
136
|
+
debug "Executing #{command} on each of #{gears.inspect}"
|
137
|
+
MultipleGearTask.new(command, gears, {:limit => options.limit, :always_prefix => options.always_prefix, :raw => options.raw}.merge(opts)).run(&block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def table_from_gears(command, groups, opts={}, &block)
|
141
|
+
cells = run_on_gears(command, groups, {:as => :table}.merge(opts), &block)
|
142
|
+
cells.each{ |r| r.concat(r.pop.first.split(opts[:split_cells_on])) } if !block_given? && opts[:split_cells_on]
|
143
|
+
say table cells, opts unless options.raw
|
144
|
+
end
|
145
|
+
|
146
|
+
def ssh_command_for_op(operation)
|
147
|
+
#case operation
|
148
|
+
raise RHC::OperationNotSupportedException, "The operation #{operation} is not supported."
|
149
|
+
#end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Public: Run ssh command on remote host
|
153
|
+
#
|
154
|
+
# host - The String of the remote hostname to ssh to.
|
155
|
+
# username - The String username of the remote user to ssh as.
|
156
|
+
# command - The String command to run on the remote host.
|
157
|
+
# compression - Use compression in ssh, set to false if sending files.
|
158
|
+
# request_pty - Request for pty, set to false when pipe a file.
|
159
|
+
# block - Will yield this block and send the channel if provided.
|
160
|
+
#
|
161
|
+
# Examples
|
162
|
+
#
|
163
|
+
# ssh_ruby('myapp-t.rhcloud.com',
|
164
|
+
# '109745632b514e9590aa802ec015b074',
|
165
|
+
# 'rhcsh tail -f $OPENSHIFT_LOG_DIR/*"')
|
166
|
+
# # => true
|
167
|
+
#
|
168
|
+
# Returns true on success
|
169
|
+
def ssh_ruby(host, username, command, compression=false, request_pty=false, &block)
|
170
|
+
debug "Opening Net::SSH connection to #{host}, #{username}, #{command}"
|
171
|
+
exit_status = 0
|
172
|
+
options = {:compression => compression}
|
173
|
+
options[:verbose] = :debug if debug?
|
174
|
+
Net::SSH.start(host, username, options) do |session|
|
175
|
+
#:nocov:
|
176
|
+
channel = session.open_channel do |channel|
|
177
|
+
if request_pty
|
178
|
+
channel.request_pty do |ch, success|
|
179
|
+
say "pty could not be obtained" unless success
|
180
|
+
end
|
181
|
+
end
|
182
|
+
channel.exec(command) do |ch, success|
|
183
|
+
channel.on_data do |ch, data|
|
184
|
+
print data
|
185
|
+
end
|
186
|
+
channel.on_extended_data do |ch, type, data|
|
187
|
+
print data
|
188
|
+
end
|
189
|
+
channel.on_close do |ch|
|
190
|
+
debug "Terminating ... "
|
191
|
+
end
|
192
|
+
channel.on_request("exit-status") do |ch, data|
|
193
|
+
exit_status = data.read_long
|
194
|
+
end
|
195
|
+
yield channel if block_given?
|
196
|
+
channel.eof!
|
197
|
+
end
|
198
|
+
end
|
199
|
+
session.loop
|
200
|
+
#:nocov:
|
201
|
+
end
|
202
|
+
raise RHC::SSHCommandFailed.new(exit_status) if exit_status != 0
|
203
|
+
rescue Errno::ECONNREFUSED => e
|
204
|
+
debug_error e
|
205
|
+
raise RHC::SSHConnectionRefused.new(host, username)
|
206
|
+
rescue Net::SSH::AuthenticationFailed => e
|
207
|
+
debug_error e
|
208
|
+
raise RHC::SSHAuthenticationFailed.new(host, username)
|
209
|
+
rescue SocketError => e
|
210
|
+
debug_error e
|
211
|
+
raise RHC::ConnectionFailed, "The connection to #{host} failed: #{e.message}"
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
# Public: Run ssh command on remote host and pipe the specified
|
216
|
+
# file contents to the command input
|
217
|
+
#
|
218
|
+
# host - The String of the remote hostname to ssh to.
|
219
|
+
# username - The String username of the remote user to ssh as.
|
220
|
+
# command - The String command to run on the remote host.
|
221
|
+
# filename - The String path to file to send.
|
222
|
+
#
|
223
|
+
def ssh_send_file_ruby(host, username, command, filename)
|
224
|
+
filename = File.expand_path(filename)
|
225
|
+
ssh_ruby(host, username, command) do |channel|
|
226
|
+
File.open(filename, 'rb') do |file|
|
227
|
+
file.chunk(1024) do |chunk|
|
228
|
+
channel.send_data chunk
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Public: Run ssh command on remote host and pipe the specified
|
235
|
+
# url contents to the command input
|
236
|
+
#
|
237
|
+
# host - The String of the remote hostname to ssh to.
|
238
|
+
# username - The String username of the remote user to ssh as.
|
239
|
+
# command - The String command to run on the remote host.
|
240
|
+
# content_url - The url with the content to pipe to command.
|
241
|
+
#
|
242
|
+
def ssh_send_url_ruby(host, username, command, content_url)
|
243
|
+
content_url = URI.parse(URI.encode(content_url.to_s))
|
244
|
+
ssh_ruby(host, username, command) do |channel|
|
245
|
+
HTTPClient.new.get_content(content_url) do |chunk|
|
246
|
+
channel.send_data chunk
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Public: Generate an SSH key and store it in ~/.ssh/id_rsa
|
252
|
+
#
|
253
|
+
# type - The String type RSA or DSS.
|
254
|
+
# bits - The Integer value for number of bits.
|
255
|
+
# comment - The String comment for the key
|
256
|
+
#
|
257
|
+
# Examples
|
258
|
+
#
|
259
|
+
# generate_ssh_key_ruby
|
260
|
+
# # => /home/user/.ssh/id_rsa.pub
|
261
|
+
#
|
262
|
+
# Returns nil on failure or public key location as a String on success
|
263
|
+
def generate_ssh_key_ruby(type="RSA", bits = 2048, comment = "OpenShift-Key")
|
264
|
+
key = RHC::Vendor::SSHKey.generate(:type => type,
|
265
|
+
:bits => bits,
|
266
|
+
:comment => comment)
|
267
|
+
ssh_dir = RHC::Config.ssh_dir
|
268
|
+
priv_key = RHC::Config.ssh_priv_key_file_path
|
269
|
+
pub_key = RHC::Config.ssh_pub_key_file_path
|
270
|
+
|
271
|
+
if File.exists?(priv_key)
|
272
|
+
say "SSH key already exists: #{priv_key}. Reusing..."
|
273
|
+
return nil
|
274
|
+
else
|
275
|
+
unless File.exists?(ssh_dir)
|
276
|
+
FileUtils.mkdir_p(ssh_dir)
|
277
|
+
File.chmod(0700, ssh_dir)
|
278
|
+
end
|
279
|
+
File.open(priv_key, 'w') {|f| f.write(key.private_key)}
|
280
|
+
File.chmod(0600, priv_key)
|
281
|
+
File.open(pub_key, 'w') {|f| f.write(key.ssh_public_key)}
|
282
|
+
|
283
|
+
ssh_add
|
284
|
+
end
|
285
|
+
pub_key
|
286
|
+
end
|
287
|
+
|
288
|
+
def exe?(executable)
|
289
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
|
290
|
+
File.executable?(File.join(directory, executable.to_s))
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# For Net::SSH versions (< 2.0.11) that does not have
|
295
|
+
# Net::SSH::KeyFactory.load_public_key, we drop to shell to get
|
296
|
+
# the key's fingerprint
|
297
|
+
def ssh_keygen_fallback(path)
|
298
|
+
fingerprint = `ssh-keygen -lf #{path} 2>&1`.split(' ')[1]
|
299
|
+
|
300
|
+
if $?.exitstatus != 0
|
301
|
+
error "Unable to compute SSH public key finger print for #{path}"
|
302
|
+
end
|
303
|
+
fingerprint
|
304
|
+
end
|
305
|
+
|
306
|
+
def fingerprint_for_local_key(key)
|
307
|
+
Net::SSH::KeyFactory.load_public_key(key).fingerprint
|
308
|
+
rescue NoMethodError, NotImplementedError => e
|
309
|
+
ssh_keygen_fallback key
|
310
|
+
nil
|
311
|
+
rescue OpenSSL::PKey::PKeyError, Net::SSH::Exception => e
|
312
|
+
error e.message
|
313
|
+
nil
|
314
|
+
rescue => e
|
315
|
+
debug e.message
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
|
319
|
+
def fingerprint_for_default_key
|
320
|
+
fingerprint_for_local_key(RHC::Config.ssh_pub_key_file_path)
|
321
|
+
end
|
322
|
+
|
323
|
+
# for an SSH public key specified by 'key', return a triple
|
324
|
+
# [type, content, comment]
|
325
|
+
# which is basically the space-separated list of the SSH public key content
|
326
|
+
def ssh_key_triple_for(key)
|
327
|
+
begin
|
328
|
+
IO.read(key).chomp.split
|
329
|
+
rescue Errno::ENOENT => e
|
330
|
+
raise ::RHC::KeyFileNotExistentException.new("File '#{key}' does not exist.")
|
331
|
+
rescue Errno::EACCES => e
|
332
|
+
raise ::RHC::KeyFileAccessDeniedException.new("Access denied to '#{key}'.")
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def ssh_key_triple_for_default_key
|
337
|
+
ssh_key_triple_for(RHC::Config.ssh_pub_key_file_path)
|
338
|
+
end
|
339
|
+
|
340
|
+
# check the version of SSH that is installed
|
341
|
+
def ssh_version
|
342
|
+
@ssh_version ||= `ssh -V 2>&1`.strip
|
343
|
+
end
|
344
|
+
|
345
|
+
# return whether or not SSH is installed
|
346
|
+
def has_ssh?
|
347
|
+
@has_ssh ||= begin
|
348
|
+
@ssh_version = nil
|
349
|
+
ssh_version
|
350
|
+
$?.success?
|
351
|
+
rescue
|
352
|
+
false
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# return supplied ssh executable, if valid (executable, searches $PATH).
|
357
|
+
# if none was supplied, return installed ssh, if any.
|
358
|
+
def check_ssh_executable!(path)
|
359
|
+
if not path
|
360
|
+
raise RHC::InvalidSSHExecutableException.new("No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH.") unless has_ssh?
|
361
|
+
'ssh'
|
362
|
+
else
|
363
|
+
bin_path = path.split(' ').first
|
364
|
+
raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' does not exist.") unless File.exist?(bin_path) or exe?(bin_path)
|
365
|
+
raise RHC::InvalidSSHExecutableException.new("SSH executable '#{bin_path}' is not executable.") unless File.executable?(bin_path) or exe?(bin_path)
|
366
|
+
path
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
private
|
371
|
+
|
372
|
+
def ssh_add
|
373
|
+
if exe?('ssh-add')
|
374
|
+
#:nocov:
|
375
|
+
`ssh-add 2>&1`
|
376
|
+
#:nocov:
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
data/lib/rhc/tar_gz.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'archive/tar/minitar'
|
3
|
+
include Archive::Tar
|
4
|
+
|
5
|
+
TAR_BIN = File.executable?('/usr/bin/gnutar') ? '/usr/bin/gnutar' : 'tar'
|
6
|
+
|
7
|
+
module RHC
|
8
|
+
|
9
|
+
module TarGz
|
10
|
+
|
11
|
+
def self.contains(filename, search, force_ruby=false)
|
12
|
+
|
13
|
+
return false if ! (File.file? filename and File.basename(filename).downcase =~ /.\.t(ar\.)?gz$/i)
|
14
|
+
|
15
|
+
regex = Regexp.new search
|
16
|
+
if RHC::Helpers.windows? or RHC::Helpers.mac? or force_ruby
|
17
|
+
begin
|
18
|
+
zlib::GzipReader.open(filename) do |gz|
|
19
|
+
Minitar::Reader.open gz do |tar|
|
20
|
+
tar.each_entry do |entry|
|
21
|
+
if entry.full_name =~ regex
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
rescue zlib::GzipFile::Error, zlib::GzipFile::Error
|
28
|
+
false
|
29
|
+
end
|
30
|
+
else
|
31
|
+
# combining STDOUT and STDERR (i.e., 2>&1) does not suppress output
|
32
|
+
# when the specs run via 'bundle exec rake spec'
|
33
|
+
system "#{TAR_BIN} --wildcards -tf '#{filename}' '#{regex.source}' 2>/dev/null >/dev/null"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def self.zlib
|
39
|
+
#:nocov:
|
40
|
+
require 'zlib' rescue nil
|
41
|
+
if defined? Zlib::GzipReader
|
42
|
+
Zlib
|
43
|
+
else
|
44
|
+
require 'rhc/vendor/zliby'
|
45
|
+
RHC::Vendor::Zlib
|
46
|
+
end
|
47
|
+
#:nocov:
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Usage: <%= Array(program :name).first %> <%= @command.name %> <%= @command.syntax %>
|
2
|
+
|
3
|
+
<%= @command.description || @command.summary %>
|
4
|
+
<% if @actions.blank? or @command.root? and @command.info[:method].present? -%>
|
5
|
+
|
6
|
+
<% unless @command.info[:args].blank? or @command.options.all?{ |o| o[:hide] or o[:switches].present? } -%>
|
7
|
+
|
8
|
+
Arguments
|
9
|
+
<%= table(
|
10
|
+
@command.info[:args].select do |opt|
|
11
|
+
not opt[:hide] and not opt[:switches].present?
|
12
|
+
end.map do |opt|
|
13
|
+
[opt[:name], opt[:description]]
|
14
|
+
end,
|
15
|
+
table_args(' ', 25)
|
16
|
+
).join("\n") %>
|
17
|
+
<% end -%>
|
18
|
+
<% unless @command.options.blank? or @command.options.all?{ |o| o[:hide] } -%>
|
19
|
+
|
20
|
+
Options
|
21
|
+
<%= table(
|
22
|
+
@command.options.select do |opt|
|
23
|
+
not opt[:hide]
|
24
|
+
end.map do |opt|
|
25
|
+
[opt[:switches].join(', '), opt[:description]]
|
26
|
+
end,
|
27
|
+
table_args(' ', 25)
|
28
|
+
).join("\n") %>
|
29
|
+
<% end -%>
|
30
|
+
|
31
|
+
Global Options
|
32
|
+
<%= table(
|
33
|
+
@global_options.select do |opt|
|
34
|
+
not (opt[:hide] || @command.options.any?{ |o| (o[:switches] & opt[:switches]).present? })
|
35
|
+
end.map do |opt|
|
36
|
+
[opt[:switches].join(', '), opt[:description]]
|
37
|
+
end,
|
38
|
+
table_args(' ', 25)
|
39
|
+
).join("\n") %>
|
40
|
+
|
41
|
+
See '<%= Array(program :name).first %> help options' for a full list of global options.
|
42
|
+
<% end -%>
|
43
|
+
<% if @actions.present? -%>
|
44
|
+
|
45
|
+
List of Actions
|
46
|
+
<%= table(@actions.map{ |a| [a[:name], a[:summary]] }, table_args(' ', 13)).join("\n") %>
|
47
|
+
<% if @command.default_action? -%>
|
48
|
+
|
49
|
+
The default action for this resource is '<%= @command.default_action %>'
|
50
|
+
<% end -%>
|
51
|
+
<% end -%>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Usage: <%= Array(program :name).first %> <%= @command.name %><%= " #{@command.syntax}" if @command.syntax %>
|
2
|
+
<% unless @command.options.blank? or @command.options.all?{ |o| o[:hide] } -%>
|
3
|
+
Pass '--help' to see the full list of options
|
4
|
+
<% end -%>
|
5
|
+
<% unless @actions.nil? or @actions.empty? -%>
|
6
|
+
|
7
|
+
<%= @command.description || @command.summary %>
|
8
|
+
|
9
|
+
List of Actions
|
10
|
+
<%= table(@actions.map{ |a| [a[:name], a[:summary]] }, table_args(' ', 18)).join("\n") %>
|
11
|
+
<% end -%>
|