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,121 @@
|
|
|
1
|
+
require 'rhc/git_helpers'
|
|
2
|
+
|
|
3
|
+
module RHC
|
|
4
|
+
#
|
|
5
|
+
# Methods in this module should not attempt to read from the options hash
|
|
6
|
+
# in a recursive manner (server_context can't read options.server).
|
|
7
|
+
#
|
|
8
|
+
module ContextHelpers
|
|
9
|
+
include RHC::GitHelpers
|
|
10
|
+
|
|
11
|
+
def self.included(other)
|
|
12
|
+
other.module_eval do
|
|
13
|
+
def self.takes_domain(opts={})
|
|
14
|
+
if opts[:argument]
|
|
15
|
+
argument :namespace, "Name of a domain", ["-n", "--namespace NAME"], :allow_nil => true, :default => :from_local_git
|
|
16
|
+
else
|
|
17
|
+
option ["-n", "--namespace NAME"], "Name of a domain", :default => :from_local_git
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
# Does not take defaults to avoid conflicts
|
|
21
|
+
def self.takes_application_or_domain(opts={})
|
|
22
|
+
option ["-n", "--namespace NAME"], "Name of a domain"
|
|
23
|
+
option ["-a", "--app NAME"], "Name of an application"
|
|
24
|
+
if opts[:argument]
|
|
25
|
+
argument :target, "The name of a domain, or an application name with domain (domain or domain/application)", ["-t", "--target NAME_OR_PATH"], :allow_nil => true, :covered_by => [:application_id, :namespace, :app]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
def self.takes_application(opts={})
|
|
29
|
+
if opts[:argument]
|
|
30
|
+
argument :app, "Name of an application", ["-a", "--app NAME"], :allow_nil => true, :default => :from_local_git, :covered_by => :application_id
|
|
31
|
+
else
|
|
32
|
+
option ["-a", "--app NAME"], "Name of an application", :default => :from_local_git, :covered_by => :application_id
|
|
33
|
+
end
|
|
34
|
+
option ["-n", "--namespace NAME"], "Name of a domain", :default => :from_local_git
|
|
35
|
+
option ["--application-id ID"], "ID of an application", :hide => true, :default => :from_local_git, :covered_by => :app
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def find_domain(opts={})
|
|
41
|
+
domain = options.namespace || options.target || namespace_context
|
|
42
|
+
if domain
|
|
43
|
+
rest_client.find_domain(domain)
|
|
44
|
+
else
|
|
45
|
+
raise ArgumentError, "You must specify a domain with -n."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def find_app_or_domain(opts={})
|
|
50
|
+
domain, app =
|
|
51
|
+
if options.target.present?
|
|
52
|
+
options.target.split(/\//)
|
|
53
|
+
elsif options.namespace || options.app
|
|
54
|
+
if options.app =~ /\//
|
|
55
|
+
options.app.split(/\//)
|
|
56
|
+
else
|
|
57
|
+
[options.namespace || namespace_context, options.app]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
if app && domain
|
|
61
|
+
rest_client.find_application(domain, app)
|
|
62
|
+
elsif domain
|
|
63
|
+
rest_client.find_domain(domain)
|
|
64
|
+
else
|
|
65
|
+
raise ArgumentError, "You must specify a domain with -n, or an application with -a."
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def find_app(opts={})
|
|
70
|
+
if id = options.application_id
|
|
71
|
+
if opts.delete(:with_gear_groups)
|
|
72
|
+
return rest_client.find_application_by_id_gear_groups(id, opts)
|
|
73
|
+
else
|
|
74
|
+
return rest_client.find_application_by_id(id, opts)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
domain, app =
|
|
78
|
+
if options.app
|
|
79
|
+
if options.app =~ /\//
|
|
80
|
+
options.app.split(/\//)
|
|
81
|
+
else
|
|
82
|
+
[options.namespace || namespace_context, options.app]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
if app && domain
|
|
86
|
+
if opts.delete(:with_gear_groups)
|
|
87
|
+
rest_client.find_application_gear_groups(domain, app, opts)
|
|
88
|
+
else
|
|
89
|
+
rest_client.find_application(domain, app, opts)
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
raise ArgumentError, "You must specify an application with -a, or run this command from within a Git directory cloned from ProtonBox."
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def server_context(defaults=nil, arg=nil)
|
|
97
|
+
value = ENV['PROTONBOX_SERVER'] || (!options.clean && config['protonbox_server']) || "api.protonbox.com"
|
|
98
|
+
defaults[arg] = value if defaults && arg
|
|
99
|
+
value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def from_local_git(defaults, arg)
|
|
103
|
+
@local_git_config ||= {
|
|
104
|
+
:application_id => git_config_get('pbox.app-id').presence,
|
|
105
|
+
:app => git_config_get('pbox.app-name').presence,
|
|
106
|
+
:namespace => git_config_get('pbox.domain-name').presence,
|
|
107
|
+
}
|
|
108
|
+
defaults[arg] ||= @local_git_config[arg] unless @local_git_config[arg].nil?
|
|
109
|
+
@local_git_config
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def namespace_context
|
|
113
|
+
# right now we don't have any logic since we only support one domain
|
|
114
|
+
# TODO: add domain lookup based on uuid
|
|
115
|
+
domain = rest_client.domains.first
|
|
116
|
+
raise RHC::NoDomainsForUser if domain.nil?
|
|
117
|
+
|
|
118
|
+
domain.name
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/rhc/core_ext.rb
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# From Rails core_ext/object.rb
|
|
2
|
+
require 'rhc/json'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
require 'httpclient'
|
|
5
|
+
|
|
6
|
+
class Object
|
|
7
|
+
def present?
|
|
8
|
+
!blank?
|
|
9
|
+
end
|
|
10
|
+
def blank?
|
|
11
|
+
respond_to?(:empty?) ? empty? : !self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def presence
|
|
15
|
+
present? ? self : nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Avoid a conflict if to_json is already defined
|
|
19
|
+
unless Object.new.respond_to? :to_json
|
|
20
|
+
def to_json(options=nil)
|
|
21
|
+
RHC::Json.encode(self)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Array
|
|
27
|
+
# From rails
|
|
28
|
+
def split(value = nil)
|
|
29
|
+
using_block = block_given?
|
|
30
|
+
|
|
31
|
+
inject([[]]) do |results, element|
|
|
32
|
+
if (using_block && yield(element)) || (value == element)
|
|
33
|
+
results << []
|
|
34
|
+
else
|
|
35
|
+
results.last << element
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
results
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class File
|
|
44
|
+
def chunk(chunk_size=1024)
|
|
45
|
+
yield read(chunk_size) until eof?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class String
|
|
50
|
+
# Wrap string by the given length, and join it with the given character.
|
|
51
|
+
# The method doesn't distinguish between words, it will only work based on
|
|
52
|
+
# the length.
|
|
53
|
+
def wrap(wrap_length=80, char="\n")
|
|
54
|
+
scan(/.{#{wrap_length}}|.+/).join(char)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def strip_heredoc
|
|
58
|
+
indent = scan(/^[ \t]*(?=\S)/).min.size || 0
|
|
59
|
+
gsub(/^[ \t]{#{indent}}/, '').
|
|
60
|
+
gsub(/(\b|\S)[^\S\n]*\n(\S)/m, '\1 \2').
|
|
61
|
+
gsub(/\n+\Z/, '').
|
|
62
|
+
gsub(/\n{3,}/, "\n\n")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
ANSI_ESCAPE_SEQUENCE = /\e\[(\d{1,2}(?:;\d{1,2})*[@-~])/
|
|
66
|
+
ANSI_ESCAPE_MATCH = '\e\[\d+(?:;\d+)*[@-~]'
|
|
67
|
+
CHAR_SKIP_ANSI = "(?:(?:#{ANSI_ESCAPE_MATCH})+.?|.(?:#{ANSI_ESCAPE_MATCH})*)"
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Split the given string at limit, treating ANSI escape sequences as
|
|
71
|
+
# zero characters in length. Will insert an ANSI reset code (\e[0m)
|
|
72
|
+
# at the end of each line containing an ANSI code, assuming that a
|
|
73
|
+
# reset was not in the wrapped segment.
|
|
74
|
+
#
|
|
75
|
+
# All newlines are preserved.
|
|
76
|
+
#
|
|
77
|
+
# Lines longer than limit without natural breaks will be forcibly
|
|
78
|
+
# split at the exact limit boundary.
|
|
79
|
+
#
|
|
80
|
+
# Returns an Array
|
|
81
|
+
#
|
|
82
|
+
def textwrap_ansi(limit, breakword=true)
|
|
83
|
+
re = breakword ? /
|
|
84
|
+
(
|
|
85
|
+
# Match substrings that end in whitespace shorter than limit
|
|
86
|
+
#{CHAR_SKIP_ANSI}{1,#{limit}} # up to limit
|
|
87
|
+
(?:\s+|$) # require the limit to end on whitespace
|
|
88
|
+
|
|
|
89
|
+
# Match substrings equal to the limit
|
|
90
|
+
#{CHAR_SKIP_ANSI}{1,#{limit}}
|
|
91
|
+
)
|
|
92
|
+
/x :
|
|
93
|
+
/
|
|
94
|
+
(
|
|
95
|
+
# Match substrings that end in whitespace shorter than limit
|
|
96
|
+
#{CHAR_SKIP_ANSI}{1,#{limit}}
|
|
97
|
+
(?:\s|$) # require the limit to end on whitespace
|
|
98
|
+
|
|
|
99
|
+
# Match all continguous whitespace strings
|
|
100
|
+
#{CHAR_SKIP_ANSI}+?
|
|
101
|
+
(?:\s|$)
|
|
102
|
+
(?:\s+|$)?
|
|
103
|
+
)
|
|
104
|
+
/x
|
|
105
|
+
escapes = []
|
|
106
|
+
|
|
107
|
+
split("\n",-1).inject([]) do |a, line|
|
|
108
|
+
if line.length < limit
|
|
109
|
+
a << line
|
|
110
|
+
else
|
|
111
|
+
line.scan(re) do |segment, other|
|
|
112
|
+
if escapes.present?
|
|
113
|
+
a << escapes.map{ |s| "\e[#{s}"}.join
|
|
114
|
+
a[-1] << segment.rstrip
|
|
115
|
+
else
|
|
116
|
+
a << segment.rstrip
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
segment.scan(ANSI_ESCAPE_SEQUENCE).map{ |e| e.first }.each do |e|
|
|
120
|
+
case e
|
|
121
|
+
when '0m' then escapes.clear
|
|
122
|
+
else escapes << e
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
a[-1] << "\e[0m" if escapes.present?
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
a
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def strip_ansi
|
|
133
|
+
gsub(ANSI_ESCAPE_SEQUENCE, '')
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
unless HTTP::Message.method_defined? :ok?
|
|
138
|
+
#:nocov:
|
|
139
|
+
class HTTP::Message
|
|
140
|
+
def ok?
|
|
141
|
+
HTTP::Status.successful?(status)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
#:nocov:
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
unless DateTime.method_defined? :to_time
|
|
148
|
+
#:nocov:
|
|
149
|
+
class DateTime
|
|
150
|
+
def to_time
|
|
151
|
+
Time.parse(to_s)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
#:nocov:
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
#
|
|
158
|
+
# Allow http => https redirection, see
|
|
159
|
+
# http://bugs.ruby-lang.org/issues/859 to 1.8.7 for rough
|
|
160
|
+
# outline of change.
|
|
161
|
+
#
|
|
162
|
+
module OpenURI
|
|
163
|
+
def self.redirectable?(uri1, uri2) # :nodoc:
|
|
164
|
+
# This test is intended to forbid a redirection from http://... to
|
|
165
|
+
# file:///etc/passwd.
|
|
166
|
+
# However this is ad hoc. It should be extensible/configurable.
|
|
167
|
+
uri1.scheme.downcase == uri2.scheme.downcase ||
|
|
168
|
+
(/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:https?|ftp)\z/i =~ uri2.scheme)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class Hash
|
|
173
|
+
def stringify_keys!
|
|
174
|
+
keys.each do |key|
|
|
175
|
+
v = delete(key)
|
|
176
|
+
if v.is_a? Hash
|
|
177
|
+
v.stringify_keys!
|
|
178
|
+
elsif v.is_a? Array
|
|
179
|
+
v.each{ |value| value.stringify_keys! if value.is_a? Hash }
|
|
180
|
+
end
|
|
181
|
+
self[(key.to_s rescue key) || key] = v
|
|
182
|
+
end
|
|
183
|
+
self
|
|
184
|
+
end
|
|
185
|
+
def slice!(*args)
|
|
186
|
+
s = []
|
|
187
|
+
args.inject([]) do |a, k|
|
|
188
|
+
s << [k, delete(k)] if has_key?(k)
|
|
189
|
+
end
|
|
190
|
+
s
|
|
191
|
+
end
|
|
192
|
+
def slice(*args)
|
|
193
|
+
args.inject({}) do |h, k|
|
|
194
|
+
h[k] = self[k] if has_key?(k)
|
|
195
|
+
h
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
def reverse_merge!(other_hash)
|
|
199
|
+
# right wins if there is no left
|
|
200
|
+
merge!( other_hash ){|key,left,right| left }
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Must be the first module imported at entry points (executables that run
|
|
2
|
+
# in seperate processes from the test harness) otherwise coverage will be
|
|
3
|
+
# incomplete
|
|
4
|
+
|
|
5
|
+
if RUBY_VERSION >= '1.9' and ENV['PROTONBOX_FEATURE_COVERAGE']
|
|
6
|
+
require 'simplecov'
|
|
7
|
+
SimpleCov.start do
|
|
8
|
+
coverage_dir 'coverage/features/'
|
|
9
|
+
command_name 'Integration Tests'
|
|
10
|
+
|
|
11
|
+
# Filters - these files will be ignored.
|
|
12
|
+
add_filter 'lib/rhc/vendor/' # vendored files should be taken directly and only
|
|
13
|
+
# namespaces changed
|
|
14
|
+
add_filter 'features/' # Don't report on the files that run the cucumber tests
|
|
15
|
+
add_filter 'lib/rhc-feature-coverage-helper.rb'
|
|
16
|
+
add_filter 'spec/' # Don't report on the files that run the spec tests
|
|
17
|
+
|
|
18
|
+
# Groups - general categories of test areas
|
|
19
|
+
add_group('Commands') { |src_file| src_file.filename.include?(File.join(%w[lib rhc commands])) }
|
|
20
|
+
add_group('RHC Lib') { |src_file| src_file.filename.include?(File.join(%w[lib rhc])) }
|
|
21
|
+
add_group('REST') { |src_file| src_file.filename.include?(File.join(%w[lib rhc/rest])) }
|
|
22
|
+
add_group('Test') { |src_file| src_file.filename.include?(File.join(%w[features])) or
|
|
23
|
+
src_file.filename.include?(File.join(%w[spec])) }
|
|
24
|
+
|
|
25
|
+
use_merging = true
|
|
26
|
+
# Note, the #:nocov: coverage exclusion should only be used on external functions
|
|
27
|
+
# that cannot be nondestructively tested in a developer environment.
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# suppress output
|
|
31
|
+
SimpleCov.at_exit do
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'rhc/ssh_helpers'
|
|
2
|
+
|
|
3
|
+
module RHC
|
|
4
|
+
module DeploymentHelpers
|
|
5
|
+
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
include RHC::SSHHelpers
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
def deploy_artifact(rest_app, artifact, hot_deploy, force_clean_build)
|
|
13
|
+
File.file?(artifact) ?
|
|
14
|
+
deploy_local_file(rest_app, artifact, hot_deploy, force_clean_build) :
|
|
15
|
+
artifact =~ /^#{URI::regexp}$/ ?
|
|
16
|
+
deploy_file_from_url(rest_app, artifact, hot_deploy, force_clean_build) :
|
|
17
|
+
deploy_git_ref(rest_app, artifact, hot_deploy, force_clean_build)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deploy_git_ref(rest_app, ref, hot_deploy, force_clean_build)
|
|
21
|
+
say "Deployment of git ref '#{ref}' in progress for application #{rest_app.name} ..."
|
|
22
|
+
|
|
23
|
+
ssh_url = URI(rest_app.ssh_url)
|
|
24
|
+
remote_cmd = "gear deploy #{ref}#{hot_deploy ? ' --hot-deploy' : ''}#{force_clean_build ? ' --force-clean-build' : ''}"
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
ssh_ruby(ssh_url.host, ssh_url.user, remote_cmd)
|
|
28
|
+
success "Success"
|
|
29
|
+
rescue
|
|
30
|
+
ssh_cmd = "ssh -t #{ssh_url.user}@#{ssh_url.host} '#{remote_cmd}'"
|
|
31
|
+
warn "Error deploying git ref. You can try to deploy manually with:\n#{ssh_cmd}"
|
|
32
|
+
raise
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def deploy_local_file(rest_app, filename, hot_deploy, force_clean_build)
|
|
37
|
+
filename = File.expand_path(filename)
|
|
38
|
+
say "Deployment of file '#{filename}' in progress for application #{rest_app.name} ..."
|
|
39
|
+
|
|
40
|
+
ssh_url = URI(rest_app.ssh_url)
|
|
41
|
+
remote_cmd = "oo-binary-deploy#{hot_deploy ? ' --hot-deploy' : ''}#{force_clean_build ? ' --force-clean-build' : ''}"
|
|
42
|
+
|
|
43
|
+
begin
|
|
44
|
+
ssh_send_file_ruby(ssh_url.host, ssh_url.user, remote_cmd, filename)
|
|
45
|
+
success "Success"
|
|
46
|
+
rescue
|
|
47
|
+
ssh_cmd = "ssh -t #{ssh_url.user}@#{ssh_url.host} '#{remote_cmd}'"
|
|
48
|
+
warn "Error deploying local file. You can try to deploy manually with:\n#{ssh_cmd}"
|
|
49
|
+
raise
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def deploy_file_from_url(rest_app, file_url, hot_deploy, force_clean_build)
|
|
54
|
+
say "Deployment of file '#{file_url}' in progress for application #{rest_app.name} ..."
|
|
55
|
+
|
|
56
|
+
ssh_url = URI(rest_app.ssh_url)
|
|
57
|
+
file_url = URI(file_url)
|
|
58
|
+
|
|
59
|
+
remote_cmd = "oo-binary-deploy#{hot_deploy ? ' --hot-deploy' : ''}#{force_clean_build ? ' --force-clean-build' : ''}"
|
|
60
|
+
|
|
61
|
+
begin
|
|
62
|
+
ssh_send_url_ruby(ssh_url.host, ssh_url.user, remote_cmd, file_url)
|
|
63
|
+
success "Success"
|
|
64
|
+
rescue
|
|
65
|
+
ssh_cmd = "ssh -t #{ssh_url.user}@#{ssh_url.host} '#{remote_cmd}'"
|
|
66
|
+
warn "Error deploying file from url. You can try to deploy manually with:\n#{ssh_cmd}"
|
|
67
|
+
raise
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def activate_deployment(rest_app, deployment_id)
|
|
72
|
+
say "Activating deployment '#{deployment_id}' on application #{rest_app.name} ..."
|
|
73
|
+
|
|
74
|
+
ssh_url = URI(rest_app.ssh_url)
|
|
75
|
+
remote_cmd = "gear activate #{deployment_id}"
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
ssh_ruby(ssh_url.host, ssh_url.user, remote_cmd)
|
|
79
|
+
success "Success"
|
|
80
|
+
rescue
|
|
81
|
+
ssh_cmd = "ssh -t #{ssh_url.user}@#{ssh_url.host} '#{remote_cmd}'"
|
|
82
|
+
warn "Error activating deployment. You can try to activate manually with:\n#{ssh_cmd}"
|
|
83
|
+
raise
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
module RHC
|
|
2
|
+
class Exception < StandardError
|
|
3
|
+
attr_reader :code
|
|
4
|
+
def initialize(message=nil, code=1)
|
|
5
|
+
super(message)
|
|
6
|
+
@code = code
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class ConfirmationError < Exception
|
|
11
|
+
def initialize(message="This action requires the --confirm option (or entering 'yes' at a prompt) to run.", code=1)
|
|
12
|
+
super(message, code)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class CartridgeNotFoundException < Exception
|
|
17
|
+
def initialize(message="Cartridge not found")
|
|
18
|
+
super message, 154
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class AliasNotFoundException < Exception
|
|
23
|
+
def initialize(message="Alias not found")
|
|
24
|
+
super message, 156
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class MultipleCartridgesException < Exception
|
|
29
|
+
def initialize(message="Multiple cartridge found")
|
|
30
|
+
super message, 155
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class EnvironmentVariableNotFoundException < Exception
|
|
35
|
+
def initialize(message="Environment variable not found")
|
|
36
|
+
super message, 157
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class EnvironmentVariablesNotSupportedException < Exception
|
|
41
|
+
def initialize(message="Server does not support environment variables")
|
|
42
|
+
super message, 158
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class EnvironmentVariableNotProvidedException < Exception
|
|
47
|
+
def initialize(message="Environment variable not provided")
|
|
48
|
+
super message, 159
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class JenkinsNotInstalledOnServer < Exception
|
|
53
|
+
def initialize(message="There is no installed cartridge that exposes Jenkins")
|
|
54
|
+
super message, 160
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class KeyNotFoundException < Exception
|
|
59
|
+
def initialize(message="SSHKey not found")
|
|
60
|
+
super message, 118
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class GitException < Exception
|
|
65
|
+
def initialize(message="Git returned an error")
|
|
66
|
+
super message, 216
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class GitPermissionDenied < GitException; end
|
|
71
|
+
class GitDirectoryExists < GitException; end
|
|
72
|
+
|
|
73
|
+
class DeprecatedError < RuntimeError; end
|
|
74
|
+
|
|
75
|
+
class KeyFileNotExistentException < Exception
|
|
76
|
+
def initialize(message="SSH Key file not found")
|
|
77
|
+
super message, 128
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class KeyFileAccessDeniedException < Exception
|
|
82
|
+
def initialize(message = "Insufficient acces to SSH Key file")
|
|
83
|
+
super message, 128
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class KeyDataInvalidException < Exception
|
|
88
|
+
def initialize(message = "SSH Key file contains invalid data")
|
|
89
|
+
super message, 128
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class PermissionDeniedException < Exception
|
|
94
|
+
def initialize(message="Permission denied")
|
|
95
|
+
super message, 129
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class NoPortsToForwardException < Exception
|
|
100
|
+
def initialize(message="No available ports to forward")
|
|
101
|
+
super message, 102
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class PortForwardFailedException < Exception
|
|
106
|
+
def initialize(message="Port forward failed")
|
|
107
|
+
super message, 1
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class SnapshotSaveException < Exception
|
|
112
|
+
def initialize(message="Error trying to save snapshot")
|
|
113
|
+
super message, 130
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class SnapshotRestoreException < Exception
|
|
118
|
+
def initialize(message="Error trying to restore snapshot")
|
|
119
|
+
super message, 130
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class DeploymentNotFoundException < Exception
|
|
124
|
+
def initialize(message="Deployment not found")
|
|
125
|
+
super message, 131
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class DeploymentsNotSupportedException < Exception
|
|
130
|
+
def initialize(message="The server does not support deployments")
|
|
131
|
+
super message, 132
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class MissingScalingValueException < Exception
|
|
136
|
+
def initialize(message="Must provide either a min or max value for scaling")
|
|
137
|
+
super message
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class CartridgeNotScalableException < Exception
|
|
142
|
+
def initialize(message="Cartridge is not scalable")
|
|
143
|
+
super message
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class ConnectionFailed < Exception
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class SSHAuthenticationFailed < Exception
|
|
151
|
+
def initialize(host, user)
|
|
152
|
+
super "Authentication to server #{host} with user #{user} failed"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
class SSHConnectionRefused < ConnectionFailed
|
|
157
|
+
def initialize(host, user)
|
|
158
|
+
super "The server #{host} refused a connection with user #{user}. The application may be unavailable.", 1
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
class SSHCommandFailed < Exception
|
|
163
|
+
def initialize(exit_status, message=nil)
|
|
164
|
+
super message || "SSH command finished with exit status = #{exit_status}", 133
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class AdditionalStorageArgumentsException < Exception
|
|
169
|
+
def initialize(message="Only one storage action can be performed at a time.")
|
|
170
|
+
super message, 1
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
class AdditionalStorageValueException < Exception
|
|
175
|
+
def initialize(message="The amount format must be a number, optionally followed by 'GB' (ex.: 5GB)")
|
|
176
|
+
super message, 1
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class AdditionalStorageRemoveException < Exception
|
|
181
|
+
def initialize(message="The amount of additional storage to be removed exceeds the total amount in use. Add the -f flag to override.")
|
|
182
|
+
super message, 1
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class ChangeMembersOnResourceNotSupported < Exception
|
|
187
|
+
def initialize(message="You can only add or remove members on a domain.")
|
|
188
|
+
super message, 1
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
class MembersNotSupported < Exception
|
|
193
|
+
def initialize(message="The server does not support adding or removing members.")
|
|
194
|
+
super message, 1
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class UnsupportedError < Exception
|
|
199
|
+
def initialize(message="This operation is not supported by the server.")
|
|
200
|
+
super message, 1
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
class NoPerGearOperations < UnsupportedError
|
|
204
|
+
def initialize
|
|
205
|
+
super "The server does not support operations on individual gears."
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
class ServerAPINotSupportedException < UnsupportedError
|
|
209
|
+
def initialize(min_version, current_version)
|
|
210
|
+
super "The server does not support this command (requires #{min_version}, found #{current_version})."
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
class OperationNotSupportedException < UnsupportedError; end
|
|
214
|
+
|
|
215
|
+
class InvalidURIException < Exception
|
|
216
|
+
def initialize(uri)
|
|
217
|
+
super "Invalid URI specified: #{uri}"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class InvalidSSHExecutableException < Exception
|
|
222
|
+
def initialize(message="Invalid or missing SSH executable")
|
|
223
|
+
super message
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class NoDomainsForUser < Exception
|
|
228
|
+
def initialize(message="In order to deploy applications, you must create a domain with 'pbox setup' or 'pbox create-domain'.")
|
|
229
|
+
super message, 1
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|