webbynode 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of webbynode might be problematic. Click here for more details.
- data/Manifest +88 -4
- data/PostInstall.txt +42 -9
- data/README.rdoc +6 -0
- data/Rakefile +16 -19
- data/assets/webbynode.png +0 -0
- data/bin/webbynode +3 -3
- data/bin/wn +7 -0
- data/cucumber.yml +1 -0
- data/devver.rake +174 -0
- data/features/bootstrap.feature +17 -0
- data/features/step_definitions/command_steps.rb +14 -0
- data/features/support/env.rb +22 -0
- data/features/support/hooks.rb +8 -0
- data/{test/test_webbynode.rb → features/support/io_features.rb} +0 -0
- data/features/support/mocha.rb +15 -0
- data/lib/templates/api_token +14 -0
- data/lib/templates/backup +15 -0
- data/lib/templates/gitignore +4 -0
- data/lib/templates/help +53 -0
- data/lib/webbynode/api_client.rb +121 -0
- data/lib/webbynode/application.rb +21 -0
- data/lib/webbynode/command.rb +332 -0
- data/lib/webbynode/commands/add_backup.rb +33 -0
- data/lib/webbynode/commands/add_key.rb +24 -0
- data/lib/webbynode/commands/alias.rb +153 -0
- data/lib/webbynode/commands/change_dns.rb +29 -0
- data/lib/webbynode/commands/config.rb +15 -0
- data/lib/webbynode/commands/delete.rb +25 -0
- data/lib/webbynode/commands/help.rb +25 -0
- data/lib/webbynode/commands/init.rb +77 -0
- data/lib/webbynode/commands/push.rb +70 -0
- data/lib/webbynode/commands/remote.rb +28 -0
- data/lib/webbynode/commands/restart.rb +25 -0
- data/lib/webbynode/commands/start.rb +23 -0
- data/lib/webbynode/commands/stop.rb +23 -0
- data/lib/webbynode/commands/tasks.rb +149 -0
- data/lib/webbynode/commands/version.rb +8 -0
- data/lib/webbynode/commands/webbies.rb +30 -0
- data/lib/webbynode/git.rb +112 -0
- data/lib/webbynode/io.rb +175 -0
- data/lib/webbynode/notify.rb +19 -0
- data/lib/webbynode/option.rb +84 -0
- data/lib/webbynode/parameter.rb +27 -0
- data/lib/webbynode/properties.rb +43 -0
- data/lib/webbynode/push_and.rb +16 -0
- data/lib/webbynode/remote_executor.rb +21 -0
- data/lib/webbynode/server.rb +39 -0
- data/lib/webbynode/ssh.rb +65 -0
- data/lib/webbynode/ssh_keys.rb +36 -0
- data/lib/webbynode.rb +56 -0
- data/spec/fixtures/aliases +0 -0
- data/spec/fixtures/api/credentials +3 -0
- data/spec/fixtures/api/dns +30 -0
- data/spec/fixtures/api/dns_a_record +20 -0
- data/spec/fixtures/api/dns_a_record_already_exists +15 -0
- data/spec/fixtures/api/dns_a_record_error +13 -0
- data/spec/fixtures/api/dns_new_zone +17 -0
- data/spec/fixtures/api/webbies +26 -0
- data/spec/fixtures/api/webbies_unauthorized +12 -0
- data/spec/fixtures/api/webby +6 -0
- data/spec/fixtures/commands/tasks/after_push +3 -0
- data/spec/fixtures/fixture_helpers +2 -0
- data/spec/fixtures/git/config/210.11.13.12 +9 -0
- data/spec/fixtures/git/config/67.23.79.31 +9 -0
- data/spec/fixtures/git/config/67.23.79.32 +9 -0
- data/spec/fixtures/git/config/config +9 -0
- data/spec/fixtures/git/status/clean +2 -0
- data/spec/fixtures/git/status/dirty +9 -0
- data/spec/fixtures/pushand +2 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/webbynode/api_client_spec.rb +227 -0
- data/spec/webbynode/application_spec.rb +50 -0
- data/spec/webbynode/command_spec.rb +285 -0
- data/spec/webbynode/commands/add_backup_spec.rb +66 -0
- data/spec/webbynode/commands/add_key_spec.rb +58 -0
- data/spec/webbynode/commands/alias_spec.rb +213 -0
- data/spec/webbynode/commands/change_dns_spec.rb +51 -0
- data/spec/webbynode/commands/config_spec.rb +22 -0
- data/spec/webbynode/commands/delete_spec.rb +78 -0
- data/spec/webbynode/commands/help_spec.rb +43 -0
- data/spec/webbynode/commands/init_spec.rb +326 -0
- data/spec/webbynode/commands/push_spec.rb +175 -0
- data/spec/webbynode/commands/remote_spec.rb +76 -0
- data/spec/webbynode/commands/tasks_spec.rb +288 -0
- data/spec/webbynode/commands/version_spec.rb +18 -0
- data/spec/webbynode/commands/webbies_spec.rb +27 -0
- data/spec/webbynode/git_spec.rb +310 -0
- data/spec/webbynode/io_spec.rb +198 -0
- data/spec/webbynode/option_spec.rb +48 -0
- data/spec/webbynode/parameter_spec.rb +70 -0
- data/spec/webbynode/push_and_spec.rb +28 -0
- data/spec/webbynode/remote_executor_spec.rb +25 -0
- data/spec/webbynode/server_spec.rb +101 -0
- data/webbynode.gemspec +25 -16
- metadata +182 -14
- data/lib/wn.rb +0 -106
- data/test/test_helper.rb +0 -9
- data/test/test_wn.rb +0 -220
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Webbynode
|
4
|
+
class ApiClient
|
5
|
+
include HTTParty
|
6
|
+
base_uri "https://manager.webbynode.com/api/yaml"
|
7
|
+
|
8
|
+
CREDENTIALS_FILE = "#{ENV['HOME']}/.webbynode"
|
9
|
+
|
10
|
+
Unauthorized = Class.new(StandardError)
|
11
|
+
InactiveZone = Class.new(StandardError)
|
12
|
+
ApiError = Class.new(StandardError)
|
13
|
+
|
14
|
+
def io
|
15
|
+
@io ||= Io.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def zones
|
19
|
+
response = post("/dns")
|
20
|
+
if zones = response["zones"]
|
21
|
+
zones.inject({}) { |h, zone| h[zone[:domain]] = zone; h }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_record(record, ip)
|
26
|
+
original_record = record
|
27
|
+
|
28
|
+
url = Domainatrix.parse("http://#{record}")
|
29
|
+
record = url.subdomain
|
30
|
+
domain = "#{url.domain}.#{url.public_suffix}."
|
31
|
+
|
32
|
+
zone = zones[domain]
|
33
|
+
if zone
|
34
|
+
raise InactiveZone, domain unless zone[:status] == 'Active'
|
35
|
+
else
|
36
|
+
zone = create_zone(domain)
|
37
|
+
end
|
38
|
+
|
39
|
+
create_a_record(zone[:id], record, ip, original_record)
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_zone(zone)
|
43
|
+
response = post("/dns/new", :query => {"zone[domain]" => zone, "zone[ttl]" => "86400"})
|
44
|
+
handle_error(response)
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_a_record(id, record, ip, original_record)
|
49
|
+
response = post("/dns/#{id}/records/new", :query => {"record[name]" => record, "record[type]" => "A", "record[data]" => ip})
|
50
|
+
if response["errors"] and response["errors"] =~ /Data has already been taken/
|
51
|
+
io.log "'#{original_record}' is already setup on Webbynode DNS, make sure it's pointing to #{ip}", :warning
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
handle_error(response)
|
56
|
+
response["record"]
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_error(response)
|
60
|
+
raise ApiError, response["error"] if response["error"]
|
61
|
+
raise ApiError, "invalid response from the API (code #{response.code})" unless response.code == 200
|
62
|
+
end
|
63
|
+
|
64
|
+
def ip_for(hostname)
|
65
|
+
(webbies[hostname] || {})[:ip]
|
66
|
+
end
|
67
|
+
|
68
|
+
def webbies
|
69
|
+
unless @webbies
|
70
|
+
response = post("/webbies") || {}
|
71
|
+
|
72
|
+
@webbies = response
|
73
|
+
end
|
74
|
+
|
75
|
+
@webbies['webbies'].inject({}) { |h, webby| h[webby[:name]] = webby; h }
|
76
|
+
end
|
77
|
+
|
78
|
+
def credentials
|
79
|
+
@credentials ||= init_credentials
|
80
|
+
end
|
81
|
+
|
82
|
+
def init_credentials(overwrite=false)
|
83
|
+
creds = if io.file_exists?(CREDENTIALS_FILE) and !overwrite
|
84
|
+
properties
|
85
|
+
else
|
86
|
+
email = overwrite[:email] if overwrite.is_a?(Hash) and overwrite[:email]
|
87
|
+
token = overwrite[:token] if overwrite.is_a?(Hash) and overwrite[:token]
|
88
|
+
|
89
|
+
puts io.read_from_template("api_token") unless email and token
|
90
|
+
|
91
|
+
email ||= ask("Login email: ")
|
92
|
+
token ||= ask("API token: ")
|
93
|
+
|
94
|
+
response = self.class.post("/webbies", :body => { :email => email, :token => token })
|
95
|
+
if response.code == 401 or response.code == 411
|
96
|
+
raise Unauthorized, "You have provided the wrong credentials"
|
97
|
+
end
|
98
|
+
|
99
|
+
properties['email'] = email
|
100
|
+
properties['token'] = token
|
101
|
+
properties.save
|
102
|
+
|
103
|
+
puts
|
104
|
+
|
105
|
+
{ :email => email, :token => token }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def properties
|
110
|
+
@properties ||= Webbynode::Properties.new(CREDENTIALS_FILE)
|
111
|
+
end
|
112
|
+
|
113
|
+
def post(uri, options={})
|
114
|
+
response = self.class.post(uri, { :body => credentials }.merge(options))
|
115
|
+
if response.code == 401 or response.code == 411
|
116
|
+
raise Unauthorized, "You have provided the wrong credentials"
|
117
|
+
end
|
118
|
+
response
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Webbynode
|
2
|
+
class AppError < StandardError; end
|
3
|
+
|
4
|
+
class Application
|
5
|
+
attr_reader :command, :params, :aliases
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
args = ["help", "commands"] unless args.any?
|
9
|
+
|
10
|
+
@command = args.shift
|
11
|
+
@params = args
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute
|
15
|
+
if command_class = Webbynode::Command.for(command)
|
16
|
+
cmd = command_class.new(*params)
|
17
|
+
cmd.run
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
require 'jcode'
|
2
|
+
|
3
|
+
module Webbynode
|
4
|
+
class Command
|
5
|
+
Aliases = {}
|
6
|
+
Settings = {}
|
7
|
+
|
8
|
+
InvalidOption = Class.new(StandardError)
|
9
|
+
InvalidCommand = Class.new(StandardError)
|
10
|
+
CommandError = Class.new(StandardError)
|
11
|
+
|
12
|
+
def Command.inherited(child)
|
13
|
+
Settings[child] ||= {
|
14
|
+
:parameters_hash => {},
|
15
|
+
:options_hash => {},
|
16
|
+
:parameters => [],
|
17
|
+
:options => []
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# classes that requires checking
|
23
|
+
# for webbynode init must call
|
24
|
+
# this method
|
25
|
+
def requires_initialization!
|
26
|
+
Settings[self][:requires_initialization!] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def requires_options!
|
30
|
+
Settings[self][:requires_options!] = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def requires_pushed_application!
|
34
|
+
Settings[self][:requires_pushed_application!] = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def description(s)
|
38
|
+
Settings[self][:description] = s
|
39
|
+
end
|
40
|
+
|
41
|
+
def summary(s)
|
42
|
+
Settings[self][:summary] = s
|
43
|
+
end
|
44
|
+
|
45
|
+
def setting(s)
|
46
|
+
Settings[self][s]
|
47
|
+
end
|
48
|
+
|
49
|
+
def parameter(*args)
|
50
|
+
param = Parameter.new(*args)
|
51
|
+
Settings[self][:parameters] << param
|
52
|
+
Settings[self][:parameters_hash][param.name] = param
|
53
|
+
end
|
54
|
+
|
55
|
+
def option(*args)
|
56
|
+
option = Option.new(*args)
|
57
|
+
Settings[self][:options] << option
|
58
|
+
Settings[self][:options_hash][option.name] = option
|
59
|
+
end
|
60
|
+
|
61
|
+
def class_for(command)
|
62
|
+
Webbynode::Commands.const_get command_class_name(command)
|
63
|
+
end
|
64
|
+
|
65
|
+
def for(command)
|
66
|
+
begin
|
67
|
+
class_for(command)
|
68
|
+
rescue NameError
|
69
|
+
|
70
|
+
# Assumes Command Not Found
|
71
|
+
# Will attempt to find/read possible aliases that might
|
72
|
+
# have been set up by the user
|
73
|
+
if File.directory?('.webbynode')
|
74
|
+
a = Webbynode::Commands::Alias.new
|
75
|
+
a.read_aliases_file
|
76
|
+
if a.exists?(command)
|
77
|
+
remote_command = a.extract_command(command)
|
78
|
+
r = Webbynode::Commands::Remote.new(remote_command)
|
79
|
+
r.execute
|
80
|
+
return
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# If no aliases:
|
85
|
+
puts "Command \"#{command}\" doesn't exist"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_alias(alias_name)
|
90
|
+
Aliases[alias_name] = self.name.split("::").last
|
91
|
+
end
|
92
|
+
|
93
|
+
def command_class_name(command)
|
94
|
+
return Aliases[command] if Aliases[command]
|
95
|
+
class_name = command.split("_").inject([]) { |arr, item| arr << item.capitalize }.join("")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def initialize(*args)
|
100
|
+
parse_args(args)
|
101
|
+
rescue InvalidCommand, CommandError
|
102
|
+
@parse_error = $!.message
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.command
|
106
|
+
str = ""
|
107
|
+
self.name.split("::").last.each_char do |ch|
|
108
|
+
str << "_" if ch.match(/[A-Z]/) and !str.empty?
|
109
|
+
str << ch.downcase
|
110
|
+
end
|
111
|
+
str
|
112
|
+
end
|
113
|
+
|
114
|
+
def param(p)
|
115
|
+
params_hash[p].value if params_hash[p]
|
116
|
+
end
|
117
|
+
|
118
|
+
def option(p)
|
119
|
+
raise CommandError, "Unknown option: #{p}." unless options[p]
|
120
|
+
options[p].value if options[p]
|
121
|
+
end
|
122
|
+
|
123
|
+
def params_hash
|
124
|
+
settings[:parameters_hash]
|
125
|
+
end
|
126
|
+
|
127
|
+
def options
|
128
|
+
settings[:options_hash]
|
129
|
+
end
|
130
|
+
|
131
|
+
def params
|
132
|
+
settings[:parameters]
|
133
|
+
end
|
134
|
+
|
135
|
+
def param_values
|
136
|
+
settings[:parameters].map { |p| p.value }
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.summary_help
|
140
|
+
Settings[self][:summary]
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.usage
|
144
|
+
help = "Usage: webbynode #{command}"
|
145
|
+
if (params = Settings[self][:parameters])
|
146
|
+
params.each do |p|
|
147
|
+
help << " #{p.to_s}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
help << " [options]" if (Settings[self][:options] || []).any?
|
152
|
+
help
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.params_help
|
156
|
+
help = []
|
157
|
+
if (params = Settings[self][:parameters])
|
158
|
+
help << "Parameters:"
|
159
|
+
params.each do |p|
|
160
|
+
help << " #{p.name.to_s.ljust(20)}#{p.desc}#{p.required? ? "" : ", optional"}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
help.join("\n")
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.options_help
|
167
|
+
help = []
|
168
|
+
if (options = Settings[self][:options] || []).any?
|
169
|
+
help << "Options:"
|
170
|
+
options.each do |p|
|
171
|
+
help << " #{p.to_s.ljust(20)}#{p.desc}#{p.required? ? "" : ", optional"}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
help.join("\n")
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.help
|
178
|
+
help = []
|
179
|
+
help << summary_help if summary_help
|
180
|
+
help << usage
|
181
|
+
help << params_help if Settings[self][:parameters].any?
|
182
|
+
help << options_help if (Settings[self][:options] || []).any?
|
183
|
+
|
184
|
+
help.join("\n")
|
185
|
+
end
|
186
|
+
|
187
|
+
def io
|
188
|
+
@@io ||= Webbynode::Io.new
|
189
|
+
end
|
190
|
+
|
191
|
+
def git
|
192
|
+
@@git ||= Webbynode::Git.new
|
193
|
+
end
|
194
|
+
|
195
|
+
def server
|
196
|
+
@server ||= Webbynode::Server.new(git.parse_remote_ip)
|
197
|
+
end
|
198
|
+
|
199
|
+
def remote_executor
|
200
|
+
@remote_executor ||= Webbynode::RemoteExecutor.new(git.parse_remote_ip)
|
201
|
+
end
|
202
|
+
|
203
|
+
def pushand
|
204
|
+
@pushand ||= PushAnd.new
|
205
|
+
end
|
206
|
+
|
207
|
+
def api
|
208
|
+
@@api ||= Webbynode::ApiClient.new
|
209
|
+
end
|
210
|
+
|
211
|
+
def notify(msg)
|
212
|
+
Webbynode::Notify.message(msg)
|
213
|
+
end
|
214
|
+
|
215
|
+
def yes?(question)
|
216
|
+
answer = ask(question).downcase
|
217
|
+
return true if %w[y yes].include?(answer)
|
218
|
+
return false if %w[n no].include?(answer)
|
219
|
+
exit
|
220
|
+
end
|
221
|
+
|
222
|
+
def no?(question)
|
223
|
+
answer = ask(question).downcase
|
224
|
+
return true if %w[n no].include?(answer)
|
225
|
+
return false if %w[y yes].include?(answer)
|
226
|
+
exit
|
227
|
+
end
|
228
|
+
|
229
|
+
def validate_initialization
|
230
|
+
raise CommandError,
|
231
|
+
"Ahn!? Hello, McFly, anybody home?" unless git.present?
|
232
|
+
raise CommandError,
|
233
|
+
"Webbynode has not been initialized for this git repository." unless git.remote_webbynode?
|
234
|
+
raise CommandError,
|
235
|
+
"Could not find .webbynode folder, has Webbynode been initialized for this repository?" unless io.directory?('.webbynode')
|
236
|
+
raise CommandError,
|
237
|
+
"Could not find .pushand file, has Webbynode been initialized for this repository?" unless pushand.present?
|
238
|
+
end
|
239
|
+
|
240
|
+
def validate_options
|
241
|
+
raise Webbynode::Commands::NoOptionsProvided,
|
242
|
+
"No remote options were provided." if params.empty?
|
243
|
+
end
|
244
|
+
|
245
|
+
def settings
|
246
|
+
Settings[self.class] || {}
|
247
|
+
end
|
248
|
+
|
249
|
+
def run
|
250
|
+
if @parse_error
|
251
|
+
puts "#{@parse_error} Use \"webbynode help #{self.class.command}\" for more information."
|
252
|
+
puts self.class.usage
|
253
|
+
return
|
254
|
+
end
|
255
|
+
|
256
|
+
if @help
|
257
|
+
puts self.class.help
|
258
|
+
return
|
259
|
+
end
|
260
|
+
|
261
|
+
begin
|
262
|
+
validate_initialization if settings[:requires_initialization!]
|
263
|
+
validate_options if settings[:requires_options!]
|
264
|
+
execute
|
265
|
+
rescue Webbynode::ApiClient::Unauthorized
|
266
|
+
puts "Your credentials didn't match any Webbynode account."
|
267
|
+
rescue CommandError
|
268
|
+
# io.log $!
|
269
|
+
puts $!
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def parse_args(args)
|
276
|
+
settings[:options].each { |o| o.reset! }
|
277
|
+
settings[:parameters].each { |p| p.reset! }
|
278
|
+
|
279
|
+
i = 0
|
280
|
+
while (opt = args.shift)
|
281
|
+
if opt == "--help"
|
282
|
+
@help = true
|
283
|
+
return
|
284
|
+
elsif (name = Option.name_for(opt))
|
285
|
+
option = settings[:options_hash][name.to_sym]
|
286
|
+
raise Webbynode::Command::CommandError, "Unknown option: #{name.to_sym}." unless option
|
287
|
+
option.parse(opt)
|
288
|
+
else
|
289
|
+
raise InvalidCommand, "command '#{self.class.command}' takes no parameters" if settings[:parameters].empty?
|
290
|
+
|
291
|
+
if settings[:parameters][i].array?
|
292
|
+
settings[:parameters][i].value << opt
|
293
|
+
else
|
294
|
+
settings[:parameters][i].value = opt
|
295
|
+
i += 1
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# If help is invoked without any arguments, the first argument will
|
301
|
+
# be set to "commands" so it will always display a list of available commands.
|
302
|
+
if self.class.command.eql?("help") and param(:command).blank?
|
303
|
+
settings[:parameters][0].value = "commands"
|
304
|
+
end
|
305
|
+
|
306
|
+
settings[:parameters].each { |p| p.validate! }
|
307
|
+
end
|
308
|
+
|
309
|
+
def handle_dns(dns)
|
310
|
+
url = Domainatrix.parse("http://#{dns}")
|
311
|
+
ip = git.parse_remote_ip
|
312
|
+
|
313
|
+
if url.subdomain.empty?
|
314
|
+
dns = "#{url.domain}.#{url.public_suffix}"
|
315
|
+
io.log "Creating DNS entries for www.#{dns} and #{dns}..."
|
316
|
+
io.add_setting "dns_alias", "'www.#{dns}'"
|
317
|
+
api.create_record "#{dns}", ip
|
318
|
+
api.create_record "www.#{dns}", ip
|
319
|
+
else
|
320
|
+
io.log "Creating DNS entry for #{dns}..."
|
321
|
+
io.remove_setting "dns_alias"
|
322
|
+
api.create_record dns, ip
|
323
|
+
end
|
324
|
+
rescue Webbynode::ApiClient::ApiError
|
325
|
+
if $!.message =~ /Data has already been taken/
|
326
|
+
io.log "The DNS entry for '#{dns}' already existed, ignoring."
|
327
|
+
else
|
328
|
+
io.log "Couldn't create your DNS entry: #{$!.message}"
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Webbynode::Commands
|
2
|
+
class AddBackup < Webbynode::Command
|
3
|
+
summary "Configures automatic nightly backups for the current application"
|
4
|
+
option :retain, "Number of backups to retain on S3 (one backup per day), default is 30", :take => :days
|
5
|
+
|
6
|
+
def execute
|
7
|
+
key = io.general_settings['aws_key']
|
8
|
+
secret = io.general_settings['aws_secret']
|
9
|
+
|
10
|
+
unless key and secret
|
11
|
+
puts io.read_from_template("backup")
|
12
|
+
|
13
|
+
key = ask("AWS key: ")
|
14
|
+
secret = ask("AWS secret: ") unless key.blank?
|
15
|
+
|
16
|
+
if key.blank? or secret.blank?
|
17
|
+
puts
|
18
|
+
puts "Aborted."
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
io.add_general_setting("aws_key", key)
|
23
|
+
io.add_general_setting("aws_secret", secret)
|
24
|
+
end
|
25
|
+
|
26
|
+
app_name = io.app_name
|
27
|
+
io.log "Configuring backup for #{app_name}...", :start
|
28
|
+
|
29
|
+
retain = option(:retain)
|
30
|
+
remote_executor.exec %Q(config_app_backup #{app_name} "#{key}" "#{secret}"#{retain.blank? ? "" : " #{retain}"}), true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Webbynode::Commands
|
2
|
+
class AddKey < Webbynode::Command
|
3
|
+
requires_initialization!
|
4
|
+
|
5
|
+
summary "Adds your ssh public key to your Webby, making deployments easy"
|
6
|
+
option :passphrase, "If present, passphrase will be used when creating a new SSH key", :take => :words
|
7
|
+
|
8
|
+
LocalSshKey = "#{ENV['HOME']}/.ssh/id_rsa.pub"
|
9
|
+
|
10
|
+
add_alias "addkey"
|
11
|
+
|
12
|
+
def execute
|
13
|
+
server.add_ssh_key LocalSshKey, options[:passphrase].value
|
14
|
+
io.log "Your local SSH Key has been added to your webby!", :notify
|
15
|
+
|
16
|
+
rescue Webbynode::InvalidAuthentication
|
17
|
+
io.log "Could not connect to webby: invalid authentication.", true
|
18
|
+
|
19
|
+
rescue Webbynode::PermissionError
|
20
|
+
io.log "Could not create an SSH key: permission error.", true
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Webbynode::Commands
|
2
|
+
class Alias < Webbynode::Command
|
3
|
+
|
4
|
+
attr_accessor :action, :alias, :command, :session_aliases
|
5
|
+
|
6
|
+
add_alias "aliases"
|
7
|
+
|
8
|
+
requires_initialization!
|
9
|
+
|
10
|
+
summary "Manages a list of alias for your common used remote commands"
|
11
|
+
parameter :action, String, "add, remove or show",
|
12
|
+
:required => true,
|
13
|
+
:validate => { :in => ['add', 'remove', 'show' ]}
|
14
|
+
parameter :alias, String, "name of the alias", :required => false
|
15
|
+
parameter :command, Array, "command to be executed", :required => false
|
16
|
+
|
17
|
+
FilePath = ".webbynode/aliases"
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
super
|
21
|
+
@session_aliases = Array.new
|
22
|
+
|
23
|
+
# Ensures that the aliases file exists inside the .webbynode/ folder
|
24
|
+
ensure_aliases_file_exists
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
|
29
|
+
# Reads out the aliases from the aliases file and stores them in session_aliases
|
30
|
+
read_aliases_file
|
31
|
+
|
32
|
+
# Parses the parameters that were provided by the user
|
33
|
+
parse_parameters
|
34
|
+
|
35
|
+
# Initializes the specified action
|
36
|
+
send(action)
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Reads out the aliases file and stores all the aliases inside the session_aliases array
|
41
|
+
def read_aliases_file
|
42
|
+
@session_aliases = Array.new
|
43
|
+
io.read_file(FilePath).each_line do |line|
|
44
|
+
if line =~ /\[(.+)\] (.+)/
|
45
|
+
@session_aliases << "[#{$1}] #{$2}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Determines whether an alias already exists inside the session_aliases array
|
51
|
+
def exists?(a)
|
52
|
+
session_aliases.each do |session_alias|
|
53
|
+
if session_alias =~ /\[(.+)\]/
|
54
|
+
return true if $1.eql?(a)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
# Ensures that the aliases file exists
|
61
|
+
# If the file does not exist, it will be created
|
62
|
+
def ensure_aliases_file_exists
|
63
|
+
unless io.file_exists?(FilePath)
|
64
|
+
io.exec("touch #{FilePath}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_command(a)
|
69
|
+
session_aliases.each do |session_alias|
|
70
|
+
if session_alias =~ /\[(.+)\] (.+)/
|
71
|
+
return $2 if a.eql?($1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Parses the paramters provided by the user
|
80
|
+
def parse_parameters
|
81
|
+
@action = param(:action)
|
82
|
+
@alias = param(:alias)
|
83
|
+
@command = param(:command).join(" ")
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds/Writes a new alias to the aliases file
|
87
|
+
def add
|
88
|
+
append_alias
|
89
|
+
write_aliases
|
90
|
+
show_aliases
|
91
|
+
end
|
92
|
+
|
93
|
+
# Removes an alias from the aliases file
|
94
|
+
def remove
|
95
|
+
remove_alias
|
96
|
+
write_aliases
|
97
|
+
show_aliases
|
98
|
+
end
|
99
|
+
|
100
|
+
# Displays the list of currently inserted aliases
|
101
|
+
def show
|
102
|
+
show_aliases
|
103
|
+
end
|
104
|
+
|
105
|
+
# Writes all aliases from the session_aliases array to the file
|
106
|
+
# It will completely overwrite the existing file
|
107
|
+
def write_aliases
|
108
|
+
io.open_file(FilePath, "w") do |file|
|
109
|
+
session_aliases.each do |a|
|
110
|
+
file << a + "\n"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Appends an alias to the session_aliases unless..
|
116
|
+
# - There is no command provided for the alias
|
117
|
+
# - There is already an alias that exists with the same name
|
118
|
+
def append_alias
|
119
|
+
if !command.blank? and !exists?(@alias)
|
120
|
+
notify("Alias added:\n[#{@alias}] #{command}")
|
121
|
+
@session_aliases << "[#{@alias}] #{command}"
|
122
|
+
else
|
123
|
+
io.log("You must provide a remote command for the alias.", true) if command.blank?
|
124
|
+
io.log("You already have an alias named [#{@alias}].", true) if exists?(@alias)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Removes the specified alias from the session_aliases if it exists
|
129
|
+
def remove_alias
|
130
|
+
tmp_aliases = @session_aliases
|
131
|
+
@session_aliases = Array.new
|
132
|
+
tmp_aliases.each do |a|
|
133
|
+
if a =~ /\[(.+)\] .+/
|
134
|
+
@session_aliases << a unless $1.eql?(@alias)
|
135
|
+
notify("Alias removed: [#{@alias}]") if $1.eql?(@alias)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Outputs every alias from the session_aliases to the user
|
141
|
+
def show_aliases
|
142
|
+
if session_aliases.any?
|
143
|
+
io.log "These are your current aliases.."
|
144
|
+
session_aliases.each do |a|
|
145
|
+
io.log a
|
146
|
+
end
|
147
|
+
else
|
148
|
+
io.log "You have not yet set up any aliases.", true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|