vagrant-node 1.0.0 → 1.1.1
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.
- data/.gitignore +1 -0
- data/lib/vagrant-node/actions/boxadd.rb +110 -0
- data/lib/vagrant-node/actions/snapshot.rb +1 -1
- data/lib/vagrant-node/api.rb +303 -270
- data/lib/vagrant-node/apidesc.rb +47 -21
- data/lib/vagrant-node/clientcontroller.rb +658 -415
- data/lib/vagrant-node/configmanager.rb +266 -85
- data/lib/vagrant-node/dbmanager.rb +300 -123
- data/lib/vagrant-node/exceptions.rb +15 -0
- data/lib/vagrant-node/nodeserverpasswd.rb +109 -34
- data/lib/vagrant-node/nodeserverstart.rb +4 -3
- data/lib/vagrant-node/nodeserverstop.rb +2 -1
- data/lib/vagrant-node/obmanager.rb +123 -0
- data/lib/vagrant-node/server.rb +27 -21
- data/lib/vagrant-node/util/downloader.rb +182 -0
- data/lib/vagrant-node/version.rb +1 -1
- data/vagrant-node.gemspec +17 -3
- metadata +65 -22
- data/lib/vagrant-node/actions/.svn/entries +0 -62
- data/lib/vagrant-node/actions/.svn/text-base/snapshot.rb.svn-base +0 -310
@@ -14,6 +14,21 @@ module Vagrant
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
class VMActionException < StandardError
|
18
|
+
def initialize(vmname,provider,msg)
|
19
|
+
super(msg)
|
20
|
+
@vmname = vmname
|
21
|
+
@provider = provider
|
22
|
+
end
|
23
|
+
def vmname
|
24
|
+
@vmname
|
25
|
+
end
|
26
|
+
|
27
|
+
def provider
|
28
|
+
@provider
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
17
32
|
class ExceptionMutator < RestException
|
18
33
|
include Vagrant::Errors
|
19
34
|
def initialize(exception)
|
@@ -6,6 +6,25 @@ require 'vagrant-node/dbmanager'
|
|
6
6
|
module Vagrant
|
7
7
|
module Node
|
8
8
|
class NodeServerPasswd < Vagrant.plugin(2, :command)
|
9
|
+
|
10
|
+
def ask_for_config
|
11
|
+
puts "Configuring Vagrant Node"
|
12
|
+
puts "Insert database user:"
|
13
|
+
user=STDIN.noecho(&:gets).chomp
|
14
|
+
print "\n"
|
15
|
+
print "Insert database password:"
|
16
|
+
password=STDIN.noecho(&:gets).chomp
|
17
|
+
print "\n"
|
18
|
+
print "Insert database name:"
|
19
|
+
database=STDIN.noecho(&:gets).chomp
|
20
|
+
print "\n"
|
21
|
+
|
22
|
+
DB::DBManager.create_config_file(@env.data_dir,'localhost',database,user,password)
|
23
|
+
|
24
|
+
DB::DBManager.check_bbdd_structure
|
25
|
+
|
26
|
+
end
|
27
|
+
|
9
28
|
def execute
|
10
29
|
options = {}
|
11
30
|
|
@@ -20,42 +39,98 @@ module Vagrant
|
|
20
39
|
|
21
40
|
# if (argv.length==0)
|
22
41
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
42
|
+
i=0
|
43
|
+
|
44
|
+
|
45
|
+
begin
|
46
|
+
|
47
|
+
if (!DB::DBManager.check_config_file(@env.data_dir))
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
puts "Configuring Vagrant Node"
|
52
|
+
print "Insert privileged database user: "
|
53
|
+
user=STDIN.cooked(&:gets).chomp
|
54
|
+
|
55
|
+
print "Insert privileged database user password: "
|
56
|
+
password=STDIN.noecho(&:gets).chomp
|
57
|
+
print "\n"
|
58
|
+
print "Insert database name: "
|
59
|
+
database=STDIN.cooked(&:gets).chomp
|
60
|
+
print "\n"
|
61
|
+
|
62
|
+
DB::DBManager.create_config_file(@env.data_dir,'localhost',database,user,password)
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
db = DB::DBManager.new(@env.data_dir)
|
69
|
+
|
70
|
+
rescue Exception => e
|
71
|
+
|
72
|
+
if (!DB::DBManager.check_config_file(@env.data_dir))
|
73
|
+
retry
|
74
|
+
end
|
75
|
+
|
76
|
+
if (e.class==Mysql2::Error)
|
77
|
+
print "Can't connect to mysql with current configuration, please review provided credentials. Please execute again this command to reconfigure"
|
78
|
+
DB::DBManager.delete_config_file(@env.data_dir)
|
79
|
+
end
|
80
|
+
|
81
|
+
# #if (exception.class==Mysql2::Error)
|
82
|
+
# if (i<1)
|
83
|
+
# puts "Configuring database connection "
|
84
|
+
# puts "Do you let us create "
|
85
|
+
# puts "Insert privilege database user:"
|
86
|
+
# user=STDIN.noecho(&:gets).chomp
|
87
|
+
# print "\n"
|
88
|
+
# print "Insert privilege database password:"
|
89
|
+
# password=STDIN.noecho(&:gets).chomp
|
90
|
+
# print "\n"
|
91
|
+
# print "Insert database name:"
|
92
|
+
# database=STDIN.noecho(&:gets).chomp
|
93
|
+
# print "\n"
|
94
|
+
# DB::DBManager.create_config_file(@env.data_dir,'localhost',database,user,password)
|
95
|
+
# i=i+1
|
96
|
+
# retry
|
97
|
+
# end
|
52
98
|
end
|
53
99
|
|
54
|
-
if (
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
100
|
+
if (!db.nil?)
|
101
|
+
#Checking if user knows the old password
|
102
|
+
if (db.node_password_set? && !db.node_default_password_set?)
|
103
|
+
print "Insert current password: "
|
104
|
+
old_password=STDIN.noecho(&:gets).chomp
|
105
|
+
print "\n"
|
106
|
+
if !db.node_check_password?(old_password)
|
107
|
+
@env.ui.error("Password failed!")
|
108
|
+
return 0
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
pass_m = "Insert your new password for this Node: "
|
113
|
+
confirm_m = "Please Insert again the new password: "
|
114
|
+
|
115
|
+
|
116
|
+
if STDIN.respond_to?(:noecho)
|
117
|
+
print pass_m
|
118
|
+
password=STDIN.noecho(&:gets).chomp
|
119
|
+
print "\n#{confirm_m}"
|
120
|
+
confirm=STDIN.noecho(&:gets).chomp
|
121
|
+
print "\n"
|
122
|
+
else
|
123
|
+
#FIXME Don't show password
|
124
|
+
password = @env.ui.ask(pass_m)
|
125
|
+
confirm = @env.ui.ask(confirm_m)
|
126
|
+
end
|
127
|
+
|
128
|
+
if (password==confirm)
|
129
|
+
db.node_password_set(password)
|
130
|
+
@env.ui.success("Password changed!")
|
131
|
+
else
|
132
|
+
@env.ui.error("Passwords does not match!")
|
133
|
+
end
|
59
134
|
end
|
60
135
|
|
61
136
|
|
@@ -20,12 +20,13 @@ module Vagrant
|
|
20
20
|
return if !argv
|
21
21
|
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length > 1
|
22
22
|
|
23
|
-
begin
|
24
|
-
ServerAPI::ServerManager.run(File.dirname(@env.lock_path),@env.data_dir,argv[0].to_i)
|
23
|
+
begin
|
24
|
+
#ServerAPI::ServerManager.run(File.dirname(@env.lock_path),@env.data_dir,@env,argv[0].to_i)
|
25
|
+
ServerAPI::ServerManager.run(@env.tmp_path,@env.data_dir,@env,argv[0].to_i)
|
25
26
|
rescue Exception => e
|
26
27
|
@env.ui.error(e.message)
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
0
|
30
31
|
end
|
31
32
|
|
@@ -20,7 +20,8 @@ module Vagrant
|
|
20
20
|
raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length > 1
|
21
21
|
|
22
22
|
begin
|
23
|
-
ServerAPI::ServerManager.stop(File.dirname(@env.lock_path),@env.data_dir)
|
23
|
+
#ServerAPI::ServerManager.stop(File.dirname(@env.lock_path),@env.data_dir)
|
24
|
+
ServerAPI::ServerManager.stop(@env.tmp_path,@env.data_dir)
|
24
25
|
rescue Exception => e
|
25
26
|
@env.ui.error(e.message)
|
26
27
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
require 'vagrant-node/dbmanager'
|
3
|
+
require 'vagrant-node/pwmanager'
|
4
|
+
require 'vagrant-node/exceptions.rb'
|
5
|
+
require 'singleton'
|
6
|
+
|
7
|
+
|
8
|
+
module Vagrant
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Vagrant
|
13
|
+
module Config
|
14
|
+
class Loader
|
15
|
+
def procs_for_path(path)
|
16
|
+
|
17
|
+
return Config.capture_configures do
|
18
|
+
begin
|
19
|
+
|
20
|
+
#Module hack to remove previous constans definitions
|
21
|
+
modaux=Module.new
|
22
|
+
res=modaux.module_eval(File.read(path))
|
23
|
+
obconstants = Object.constants
|
24
|
+
modaux.constants.each do |var|
|
25
|
+
Object.send(:remove_const,var) if Object.constants.include?(var)
|
26
|
+
end
|
27
|
+
############################33
|
28
|
+
|
29
|
+
|
30
|
+
Kernel.load path
|
31
|
+
rescue SyntaxError => e
|
32
|
+
# Report syntax errors in a nice way.
|
33
|
+
raise Errors::VagrantfileSyntaxError, :file => e.message
|
34
|
+
rescue SystemExit
|
35
|
+
# Continue raising that exception...
|
36
|
+
raise
|
37
|
+
rescue Vagrant::Errors::VagrantError
|
38
|
+
# Continue raising known Vagrant errors since they already
|
39
|
+
# contain well worded error messages and context.
|
40
|
+
raise
|
41
|
+
rescue Exception => e
|
42
|
+
@logger.error("Vagrantfile load error: #{e.message}")
|
43
|
+
@logger.error(e.backtrace.join("\n"))
|
44
|
+
|
45
|
+
# Report the generic exception
|
46
|
+
raise Errors::VagrantfileLoadError,
|
47
|
+
:path => path,
|
48
|
+
:message => e.message
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# class Environment
|
57
|
+
|
58
|
+
# def reload
|
59
|
+
|
60
|
+
# @config_global = nil
|
61
|
+
# @config_loader = nil
|
62
|
+
# @config_global = config_global
|
63
|
+
|
64
|
+
|
65
|
+
# @config_global
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
|
69
|
+
module Node
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
class ObManager
|
74
|
+
|
75
|
+
include Singleton
|
76
|
+
|
77
|
+
def initialize
|
78
|
+
@env = Environment.new
|
79
|
+
@db = DB::DBManager.new(@env.data_dir) if (!@db)
|
80
|
+
@pw = PwManager.new(@db) if (!@pw)
|
81
|
+
end
|
82
|
+
|
83
|
+
def env
|
84
|
+
if (!@env)
|
85
|
+
self.env=Environment.new
|
86
|
+
end
|
87
|
+
@env
|
88
|
+
end
|
89
|
+
|
90
|
+
def reload_env
|
91
|
+
|
92
|
+
if (@env)
|
93
|
+
@env.unload if (@env)
|
94
|
+
# @env.reload
|
95
|
+
@env = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
@env = Environment.new
|
99
|
+
self.env=@env
|
100
|
+
|
101
|
+
@env
|
102
|
+
end
|
103
|
+
|
104
|
+
def env=(environment)
|
105
|
+
@env=environment
|
106
|
+
@db=nil
|
107
|
+
@pw=nil
|
108
|
+
@db = DB::DBManager.new(@env.data_dir) if (!@db)
|
109
|
+
@pw = PwManager.new(@db) if (!@pw)
|
110
|
+
end
|
111
|
+
|
112
|
+
def dbmanager
|
113
|
+
@db
|
114
|
+
end
|
115
|
+
|
116
|
+
def pwmanager
|
117
|
+
@pw
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/vagrant-node/server.rb
CHANGED
@@ -16,12 +16,12 @@ module Vagrant
|
|
16
16
|
DEFAULT_BIND_PORT = 3333
|
17
17
|
BIND_ADDRESS = "0.0.0.0"
|
18
18
|
LOG_FILE = "webrick.log"
|
19
|
-
def self.run(pid_path,data_path,port=DEFAULT_BIND_PORT)
|
19
|
+
def self.run(pid_path,data_path,env,port=DEFAULT_BIND_PORT)
|
20
20
|
|
21
|
-
check_password(data_path)
|
21
|
+
check_password(data_path)
|
22
22
|
|
23
23
|
pid_file = File.join(pid_path,PIDFILENAME)
|
24
|
-
|
24
|
+
|
25
25
|
pid = fork do
|
26
26
|
|
27
27
|
|
@@ -42,12 +42,25 @@ module Vagrant
|
|
42
42
|
:AccessLog => access_log
|
43
43
|
}
|
44
44
|
|
45
|
+
|
45
46
|
#begin
|
46
47
|
server = WEBrick::HTTPServer.new(options)
|
47
48
|
|
48
49
|
server.mount "/", Rack::Handler::WEBrick,ServerAPI::API.new
|
49
|
-
|
50
|
-
|
50
|
+
|
51
|
+
trap("INT") { server.shutdown }
|
52
|
+
|
53
|
+
trap("USR1") {
|
54
|
+
|
55
|
+
#Stopping server
|
56
|
+
server.shutdown
|
57
|
+
|
58
|
+
#Restarting server
|
59
|
+
server = WEBrick::HTTPServer.new(options)
|
60
|
+
server.mount "/", Rack::Handler::WEBrick,ServerAPI::API.new
|
61
|
+
server.start
|
62
|
+
}
|
63
|
+
|
51
64
|
PidFile.create(pid_file,Process.pid)
|
52
65
|
|
53
66
|
server.start
|
@@ -93,24 +106,17 @@ module Vagrant
|
|
93
106
|
end
|
94
107
|
|
95
108
|
private
|
109
|
+
|
96
110
|
|
97
111
|
def self.check_password(data_path)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
# print "\n"
|
107
|
-
# end
|
108
|
-
# end
|
109
|
-
|
110
|
-
# if(!@db.node_check_password?(password))
|
111
|
-
# raise "Password failed!"
|
112
|
-
# end
|
113
|
-
end
|
112
|
+
|
113
|
+
begin
|
114
|
+
@db = DB::DBManager.new(data_path)
|
115
|
+
rescue
|
116
|
+
if (!@db || !@db.node_password_set? || @db.node_default_password_set?)
|
117
|
+
raise "Please, set first a password with the command \"vagrant nodeserver passwd\""
|
118
|
+
end
|
119
|
+
end
|
114
120
|
true
|
115
121
|
end
|
116
122
|
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require "vagrant/util/busy"
|
3
|
+
|
4
|
+
require "vagrant/util/subprocess"
|
5
|
+
|
6
|
+
module Vagrant
|
7
|
+
module Node
|
8
|
+
module Util
|
9
|
+
# This class downloads files using various protocols by subprocessing
|
10
|
+
# to cURL. cURL is a much more capable and complete download tool than
|
11
|
+
# a hand-rolled Ruby library, so we defer to it's expertise.
|
12
|
+
class Downloader
|
13
|
+
UPDATETIME = 10
|
14
|
+
|
15
|
+
def initialize(source, destination, options=nil)
|
16
|
+
@source = source.to_s
|
17
|
+
@destination = destination.to_s
|
18
|
+
|
19
|
+
# Get the various optional values
|
20
|
+
options ||= {}
|
21
|
+
#@callback = options[:callback]
|
22
|
+
@insecure = options[:insecure]
|
23
|
+
@ui = options[:ui]
|
24
|
+
@db = options[:db]
|
25
|
+
@box_name = options[:box_name]
|
26
|
+
end
|
27
|
+
|
28
|
+
# This executes the actual download, downloading the source file
|
29
|
+
# to the destination with the given opens used to initialize this
|
30
|
+
# class.
|
31
|
+
#
|
32
|
+
# If this method returns without an exception, the download
|
33
|
+
# succeeded. An exception will be raised if the download failed.
|
34
|
+
def download!
|
35
|
+
# Build the list of parameters to execute with cURL
|
36
|
+
options = [
|
37
|
+
"--fail",
|
38
|
+
"--location",
|
39
|
+
"--max-redirs", "10",
|
40
|
+
"--output", @destination
|
41
|
+
]
|
42
|
+
|
43
|
+
options << "--insecure" if @insecure
|
44
|
+
options << @source
|
45
|
+
|
46
|
+
# Specify some options for the subprocess
|
47
|
+
subprocess_options = {}
|
48
|
+
|
49
|
+
# If we're in Vagrant, then we use the packaged CA bundle
|
50
|
+
if Vagrant.in_installer?
|
51
|
+
subprocess_options[:env] ||= {}
|
52
|
+
subprocess_options[:env]["CURL_CA_BUNDLE"] =
|
53
|
+
File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
|
54
|
+
end
|
55
|
+
|
56
|
+
# This variable can contain the proc that'll be sent to
|
57
|
+
# the subprocess execute.
|
58
|
+
data_proc = nil
|
59
|
+
|
60
|
+
subprocess_options[:notify] = :stderr
|
61
|
+
|
62
|
+
progress_data = ""
|
63
|
+
progress_regexp = /(\r(.+?))\r/
|
64
|
+
|
65
|
+
# Setup the proc that'll receive the real-time data from
|
66
|
+
# the downloader.
|
67
|
+
last_id=@db.add_box_download_info(@box_name,@source)
|
68
|
+
|
69
|
+
comienzo = Time.now();
|
70
|
+
|
71
|
+
data_proc = Proc.new do |type, data|
|
72
|
+
# Type will always be "stderr" because that is the only
|
73
|
+
# type of data we're subscribed for notifications.
|
74
|
+
|
75
|
+
# Accumulate progress_data
|
76
|
+
progress_data << data
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
while true
|
81
|
+
# If we have a full amount of column data (two "\r") then
|
82
|
+
# we report new progress reports. Otherwise, just keep
|
83
|
+
# accumulating.
|
84
|
+
match = progress_regexp.match(progress_data)
|
85
|
+
break if !match
|
86
|
+
data = match[2]
|
87
|
+
progress_data.gsub!(match[1], "")
|
88
|
+
|
89
|
+
# Ignore the first \r and split by whitespace to grab the columns
|
90
|
+
columns = data.strip.split(/\s+/)
|
91
|
+
|
92
|
+
# COLUMN DATA:
|
93
|
+
#
|
94
|
+
# 0 - % total
|
95
|
+
# 1 - Total size
|
96
|
+
# 2 - % received
|
97
|
+
# 3 - Received size
|
98
|
+
# 4 - % transferred
|
99
|
+
# 5 - Transferred size
|
100
|
+
# 6 - Average download speed
|
101
|
+
# 7 - Average upload speed
|
102
|
+
# 9 - Total time
|
103
|
+
# 9 - Time spent
|
104
|
+
# 10 - Time left
|
105
|
+
# 11 - Current speed
|
106
|
+
|
107
|
+
if (Time.now()-comienzo > UPDATETIME)
|
108
|
+
@db.update_box_download_info(last_id,"#{columns[0]}%","#{columns[10]}")
|
109
|
+
comienzo = Time.now()
|
110
|
+
end
|
111
|
+
|
112
|
+
#db.execute("INSERT INTO #{PASSWORD_TABLE} VALUES (\"#{DEFAULT_NODE_PASSWORD}\");");
|
113
|
+
#pp "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})"
|
114
|
+
|
115
|
+
#output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})"
|
116
|
+
##@ui.clear_line
|
117
|
+
#@ui.info(output, :new_line => false)
|
118
|
+
end
|
119
|
+
|
120
|
+
#@db.update_box_download_info(last_id,"100%","--:--:--")
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Add the subprocess options onto the options we'll execute with
|
125
|
+
options << subprocess_options
|
126
|
+
|
127
|
+
# Create the callback that is called if we are interrupted
|
128
|
+
interrupted = false
|
129
|
+
int_callback = Proc.new do
|
130
|
+
interrupted = true
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Execute!
|
135
|
+
result = Vagrant::Util::Busy.busy(int_callback) do
|
136
|
+
Vagrant::Util::Subprocess.execute("curl", *options, &data_proc)
|
137
|
+
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
if ((!interrupted) && (result.exit_code==0))
|
143
|
+
# @db.update_box_download_info(last_id,"100%","--:--:--")
|
144
|
+
@db.delete_box_download(last_id)
|
145
|
+
else
|
146
|
+
@db.set_box_download_error(last_id)
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# If the download was interrupted, then raise a specific error
|
151
|
+
raise Errors::DownloaderInterrupted if interrupted
|
152
|
+
|
153
|
+
|
154
|
+
# If we're outputting to the UI, clear the output to
|
155
|
+
# avoid lingering progress meters.
|
156
|
+
@ui.clear_line if @ui
|
157
|
+
|
158
|
+
|
159
|
+
# If it didn't exit successfully, we need to parse the data and
|
160
|
+
# show an error message.
|
161
|
+
if result.exit_code != 0
|
162
|
+
|
163
|
+
@logger.warn("Downloader exit code: #{result.exit_code}") if @logger
|
164
|
+
|
165
|
+
parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
|
166
|
+
|
167
|
+
parts[1] ||= ""
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
raise Errors::DownloaderError, :message => parts[1].chomp
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
# Everything succeeded
|
177
|
+
true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|