startapp 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/autocomplete/rhc_bash +1672 -0
- data/bin/app +37 -0
- data/conf/express.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +191 -0
- data/features/deployments_feature.rb +129 -0
- data/features/domains_feature.rb +58 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +166 -0
- data/lib/rhc/auth/basic.rb +64 -0
- data/lib/rhc/auth/token.rb +102 -0
- data/lib/rhc/auth/token_store.rb +53 -0
- data/lib/rhc/auth.rb +5 -0
- data/lib/rhc/autocomplete.rb +66 -0
- data/lib/rhc/autocomplete_templates/bash.erb +39 -0
- data/lib/rhc/cartridge_helpers.rb +118 -0
- data/lib/rhc/cli.rb +40 -0
- data/lib/rhc/command_runner.rb +185 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +726 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +115 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +329 -0
- data/lib/rhc/commands/clone.rb +66 -0
- data/lib/rhc/commands/configure.rb +20 -0
- data/lib/rhc/commands/create.rb +100 -0
- data/lib/rhc/commands/delete.rb +19 -0
- data/lib/rhc/commands/deploy.rb +32 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +172 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/force_stop.rb +17 -0
- data/lib/rhc/commands/git_clone.rb +34 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/logs.rb +21 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/reload.rb +17 -0
- data/lib/rhc/commands/restart.rb +17 -0
- data/lib/rhc/commands/scp.rb +54 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/show.rb +43 -0
- data/lib/rhc/commands/snapshot.rb +137 -0
- data/lib/rhc/commands/ssh.rb +51 -0
- data/lib/rhc/commands/sshkey.rb +97 -0
- data/lib/rhc/commands/start.rb +17 -0
- data/lib/rhc/commands/stop.rb +17 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands/tidy.rb +17 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +321 -0
- data/lib/rhc/context_helper.rb +121 -0
- data/lib/rhc/core_ext.rb +202 -0
- data/lib/rhc/coverage_helper.rb +33 -0
- data/lib/rhc/deployment_helpers.rb +111 -0
- data/lib/rhc/exceptions.rb +256 -0
- data/lib/rhc/git_helpers.rb +106 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +481 -0
- data/lib/rhc/highline_extensions.rb +479 -0
- data/lib/rhc/json.rb +51 -0
- data/lib/rhc/output_helpers.rb +260 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/alias.rb +42 -0
- data/lib/rhc/rest/api.rb +87 -0
- data/lib/rhc/rest/application.rb +348 -0
- data/lib/rhc/rest/attributes.rb +36 -0
- data/lib/rhc/rest/authorization.rb +8 -0
- data/lib/rhc/rest/base.rb +79 -0
- data/lib/rhc/rest/cartridge.rb +162 -0
- data/lib/rhc/rest/client.rb +650 -0
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/domain.rb +98 -0
- data/lib/rhc/rest/environment_variable.rb +15 -0
- data/lib/rhc/rest/gear_group.rb +16 -0
- data/lib/rhc/rest/httpclient.rb +145 -0
- data/lib/rhc/rest/key.rb +44 -0
- data/lib/rhc/rest/membership.rb +105 -0
- data/lib/rhc/rest/mock.rb +1042 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/scp_helpers.rb +27 -0
- data/lib/rhc/ssh_helpers.rb +380 -0
- data/lib/rhc/tar_gz.rb +51 -0
- data/lib/rhc/usage_templates/command_help.erb +51 -0
- data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
- data/lib/rhc/usage_templates/help.erb +61 -0
- data/lib/rhc/usage_templates/missing_help.erb +1 -0
- data/lib/rhc/usage_templates/options_help.erb +12 -0
- data/lib/rhc/vendor/okjson.rb +600 -0
- data/lib/rhc/vendor/parseconfig.rb +178 -0
- data/lib/rhc/vendor/sshkey.rb +253 -0
- data/lib/rhc/vendor/zliby.rb +628 -0
- data/lib/rhc/version.rb +5 -0
- data/lib/rhc/wizard.rb +637 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +82 -0
- data/spec/direct_execution_helper.rb +339 -0
- data/spec/keys/example.pem +23 -0
- data/spec/keys/example_private.pem +27 -0
- data/spec/keys/server.pem +19 -0
- data/spec/rest_spec_helper.rb +31 -0
- data/spec/rhc/assets/cert.crt +22 -0
- data/spec/rhc/assets/cert_key_rsa +27 -0
- data/spec/rhc/assets/empty.txt +0 -0
- data/spec/rhc/assets/env_vars.txt +7 -0
- data/spec/rhc/assets/env_vars_2.txt +1 -0
- data/spec/rhc/assets/foo.txt +1 -0
- data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
- data/spec/rhc/assets/targz_sample.tar.gz +0 -0
- data/spec/rhc/auth_spec.rb +442 -0
- data/spec/rhc/cli_spec.rb +186 -0
- data/spec/rhc/command_spec.rb +435 -0
- data/spec/rhc/commands/account_spec.rb +42 -0
- data/spec/rhc/commands/alias_spec.rb +333 -0
- data/spec/rhc/commands/app_spec.rb +777 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +157 -0
- data/spec/rhc/commands/cartridge_spec.rb +665 -0
- data/spec/rhc/commands/clone_spec.rb +41 -0
- data/spec/rhc/commands/deployment_spec.rb +327 -0
- data/spec/rhc/commands/domain_spec.rb +401 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +102 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +247 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/scp_spec.rb +77 -0
- data/spec/rhc/commands/server_spec.rb +69 -0
- data/spec/rhc/commands/setup_spec.rb +118 -0
- data/spec/rhc/commands/snapshot_spec.rb +179 -0
- data/spec/rhc/commands/ssh_spec.rb +163 -0
- data/spec/rhc/commands/sshkey_spec.rb +188 -0
- data/spec/rhc/commands/tail_spec.rb +81 -0
- data/spec/rhc/commands/threaddump_spec.rb +84 -0
- data/spec/rhc/config_spec.rb +407 -0
- data/spec/rhc/helpers_spec.rb +531 -0
- data/spec/rhc/highline_extensions_spec.rb +314 -0
- data/spec/rhc/json_spec.rb +30 -0
- data/spec/rhc/rest_application_spec.rb +258 -0
- data/spec/rhc/rest_client_spec.rb +752 -0
- data/spec/rhc/rest_spec.rb +740 -0
- data/spec/rhc/targz_spec.rb +55 -0
- data/spec/rhc/wizard_spec.rb +756 -0
- data/spec/spec_helper.rb +575 -0
- data/spec/wizard_spec_helper.rb +330 -0
- metadata +469 -0
|
@@ -0,0 +1,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 -%>
|