engineyard-serverside 1.3.7 → 1.4.0

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.
@@ -7,4 +7,4 @@ end
7
7
  $LOAD_PATH.push(File.expand_path("../lib", File.dirname(__FILE__)))
8
8
  require 'engineyard-serverside'
9
9
 
10
- EY::CLI.start
10
+ EY::Serverside::CLI.start
@@ -1,4 +1,3 @@
1
- $LOAD_PATH.push(File.expand_path("engineyard-serverside", File.dirname(__FILE__)))
2
1
  $LOAD_PATH.unshift File.expand_path('vendor/thor/lib', File.dirname(__FILE__))
3
2
  $LOAD_PATH.unshift File.expand_path('vendor/open4/lib', File.dirname(__FILE__))
4
3
  $LOAD_PATH.unshift File.expand_path('vendor/escape/lib', File.dirname(__FILE__))
@@ -9,44 +8,49 @@ require 'escape'
9
8
  require 'json'
10
9
  require 'dataflow'
11
10
 
12
- require 'strategies/git'
13
- require 'task'
14
- require 'server'
15
- require 'deploy'
16
- require 'deploy_hook'
17
- require 'lockfile_parser'
18
- require 'bundle_installer'
19
- require 'cli'
20
- require 'configuration'
11
+ require 'engineyard-serverside/version'
12
+ require 'engineyard-serverside/strategies/git'
13
+ require 'engineyard-serverside/task'
14
+ require 'engineyard-serverside/server'
15
+ require 'engineyard-serverside/deploy'
16
+ require 'engineyard-serverside/deploy_hook'
17
+ require 'engineyard-serverside/lockfile_parser'
18
+ require 'engineyard-serverside/bundle_installer'
19
+ require 'engineyard-serverside/cli'
20
+ require 'engineyard-serverside/configuration'
21
+ require 'engineyard-serverside/deprecation'
21
22
 
22
23
  module EY
23
- def self.node
24
- @node ||= deep_indifferentize(JSON.parse(dna_json))
25
- end
24
+ module Serverside
25
+
26
+ def self.node
27
+ @node ||= deep_indifferentize(JSON.parse(dna_json))
28
+ end
26
29
 
27
- def self.dna_json
28
- @dna_json ||= if File.exist?('/etc/chef/dna.json')
29
- `sudo cat /etc/chef/dna.json`
30
- else
31
- {}.to_json
32
- end
33
- end
30
+ def self.dna_json
31
+ @dna_json ||= if File.exist?('/etc/chef/dna.json')
32
+ `sudo cat /etc/chef/dna.json`
33
+ else
34
+ {}.to_json
35
+ end
36
+ end
34
37
 
35
- RemoteFailure = Class.new StandardError
38
+ RemoteFailure = Class.new StandardError
36
39
 
37
- private
38
- def self.deep_indifferentize(thing)
39
- if thing.kind_of?(Hash)
40
- indifferent_hash = Thor::CoreExt::HashWithIndifferentAccess.new
41
- thing.each do |k, v|
42
- indifferent_hash[k] = deep_indifferentize(v)
40
+ private
41
+ def self.deep_indifferentize(thing)
42
+ if thing.kind_of?(Hash)
43
+ indifferent_hash = Thor::CoreExt::HashWithIndifferentAccess.new
44
+ thing.each do |k, v|
45
+ indifferent_hash[k] = deep_indifferentize(v)
46
+ end
47
+ indifferent_hash
48
+ elsif thing.kind_of?(Array)
49
+ thing.map {|x| deep_indifferentize(x)}
50
+ else
51
+ thing
43
52
  end
44
- indifferent_hash
45
- elsif thing.kind_of?(Array)
46
- thing.map {|x| deep_indifferentize(x)}
47
- else
48
- thing
49
53
  end
54
+
50
55
  end
51
-
52
56
  end
@@ -1,4 +1,6 @@
1
1
  module EY
2
- class BundleInstaller < Struct.new(:version, :options)
2
+ module Serverside
3
+ class BundleInstaller < Struct.new(:version, :options)
4
+ end
3
5
  end
4
6
  end
@@ -2,255 +2,257 @@ require 'thor'
2
2
  require 'pathname'
3
3
 
4
4
  module EY
5
- class CLI < Thor
6
- include Dataflow
5
+ module Serverside
6
+ class CLI < Thor
7
+ include Dataflow
8
+
9
+ def self.start(*)
10
+ super
11
+ rescue RemoteFailure
12
+ exit(1)
13
+ end
7
14
 
8
- def self.start(*)
9
- super
10
- rescue RemoteFailure
11
- exit(1)
12
- end
15
+ method_option :migrate, :type => :string,
16
+ :desc => "Run migrations with this deploy",
17
+ :aliases => ["-m"]
13
18
 
14
- method_option :migrate, :type => :string,
15
- :desc => "Run migrations with this deploy",
16
- :aliases => ["-m"]
19
+ method_option :branch, :type => :string,
20
+ :desc => "Git ref to deploy, defaults to master. May be a branch, a tag, or a SHA",
21
+ :aliases => %w[-b --ref --tag]
17
22
 
18
- method_option :branch, :type => :string,
19
- :desc => "Git ref to deploy, defaults to master. May be a branch, a tag, or a SHA",
20
- :aliases => %w[-b --ref --tag]
23
+ method_option :repo, :type => :string,
24
+ :desc => "Remote repo to deploy",
25
+ :aliases => ["-r"]
21
26
 
22
- method_option :repo, :type => :string,
23
- :desc => "Remote repo to deploy",
24
- :aliases => ["-r"]
27
+ method_option :app, :type => :string,
28
+ :required => true,
29
+ :desc => "Application to deploy",
30
+ :aliases => ["-a"]
25
31
 
26
- method_option :app, :type => :string,
27
- :required => true,
28
- :desc => "Application to deploy",
29
- :aliases => ["-a"]
32
+ method_option :framework_env, :type => :string,
33
+ :desc => "Ruby web framework environment",
34
+ :aliases => ["-e"]
30
35
 
31
- method_option :framework_env, :type => :string,
32
- :desc => "Ruby web framework environment",
33
- :aliases => ["-e"]
36
+ method_option :config, :type => :string,
37
+ :desc => "Additional configuration"
34
38
 
35
- method_option :config, :type => :string,
36
- :desc => "Additional configuration"
39
+ method_option :stack, :type => :string,
40
+ :desc => "Web stack (so we can restart it correctly)"
37
41
 
38
- method_option :stack, :type => :string,
39
- :desc => "Web stack (so we can restart it correctly)"
42
+ method_option :instances, :type => :array,
43
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
40
44
 
41
- method_option :instances, :type => :array,
42
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
45
+ method_option :instance_roles, :type => :hash,
46
+ :default => {},
47
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
43
48
 
44
- method_option :instance_roles, :type => :hash,
45
- :default => {},
46
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
49
+ method_option :instance_names, :type => :hash,
50
+ :default => {},
51
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
47
52
 
48
- method_option :instance_names, :type => :hash,
49
- :default => {},
50
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
53
+ method_option :verbose, :type => :boolean,
54
+ :default => false,
55
+ :desc => "Verbose output",
56
+ :aliases => ["-v"]
51
57
 
52
- method_option :verbose, :type => :boolean,
53
- :default => false,
54
- :desc => "Verbose output",
55
- :aliases => ["-v"]
58
+ desc "deploy", "Deploy code from /data/<app>"
59
+ def deploy(default_task=:deploy)
60
+ config = EY::Serverside::Deploy::Configuration.new(options)
61
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
56
62
 
57
- desc "deploy", "Deploy code from /data/<app>"
58
- def deploy(default_task=:deploy)
59
- config = EY::Deploy::Configuration.new(options)
60
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
63
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
64
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
61
65
 
62
- EY::LoggedOutput.verbose = options[:verbose]
63
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
66
+ invoke :propagate
64
67
 
65
- invoke :propagate
68
+ EY::Serverside::Deploy.new(config).send(default_task)
69
+ end
66
70
 
67
- EY::Deploy.new(config).send(default_task)
68
- end
71
+ method_option :app, :type => :string,
72
+ :required => true,
73
+ :desc => "Which application's hooks to run",
74
+ :aliases => ["-a"]
69
75
 
70
- method_option :app, :type => :string,
71
- :required => true,
72
- :desc => "Which application's hooks to run",
73
- :aliases => ["-a"]
76
+ method_option :release_path, :type => :string,
77
+ :desc => "Value for #release_path in hooks (mostly for internal coordination)",
78
+ :aliases => ["-r"]
74
79
 
75
- method_option :release_path, :type => :string,
76
- :desc => "Value for #release_path in hooks (mostly for internal coordination)",
77
- :aliases => ["-r"]
80
+ method_option :current_roles, :type => :array,
81
+ :desc => "Value for #current_roles in hooks"
78
82
 
79
- method_option :current_roles, :type => :array,
80
- :desc => "Value for #current_roles in hooks"
83
+ method_option :framework_env, :type => :string,
84
+ :required => true,
85
+ :desc => "Ruby web framework environment",
86
+ :aliases => ["-e"]
81
87
 
82
- method_option :framework_env, :type => :string,
83
- :required => true,
84
- :desc => "Ruby web framework environment",
85
- :aliases => ["-e"]
88
+ method_option :config, :type => :string,
89
+ :desc => "Additional configuration"
86
90
 
87
- method_option :config, :type => :string,
88
- :desc => "Additional configuration"
91
+ method_option :current_name, :type => :string,
92
+ :desc => "Value for #current_name in hooks"
89
93
 
90
- method_option :current_name, :type => :string,
91
- :desc => "Value for #current_name in hooks"
94
+ desc "hook [NAME]", "Run a particular deploy hook"
95
+ def hook(hook_name)
96
+ EY::Serverside::DeployHook.new(options).run(hook_name)
97
+ end
92
98
 
93
- desc "hook [NAME]", "Run a particular deploy hook"
94
- def hook(hook_name)
95
- EY::DeployHook.new(options).run(hook_name)
96
- end
97
99
 
100
+ method_option :app, :type => :string,
101
+ :required => true,
102
+ :desc => "Application to deploy",
103
+ :aliases => ["-a"]
98
104
 
99
- method_option :app, :type => :string,
100
- :required => true,
101
- :desc => "Application to deploy",
102
- :aliases => ["-a"]
105
+ method_option :framework_env, :type => :string,
106
+ :required => true,
107
+ :desc => "Ruby web framework environment",
108
+ :aliases => ["-e"]
103
109
 
104
- method_option :framework_env, :type => :string,
105
- :required => true,
106
- :desc => "Ruby web framework environment",
107
- :aliases => ["-e"]
110
+ method_option :stack, :type => :string,
111
+ :desc => "Web stack (so we can restart it correctly)"
108
112
 
109
- method_option :stack, :type => :string,
110
- :desc => "Web stack (so we can restart it correctly)"
113
+ method_option :instances, :type => :array,
114
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
111
115
 
112
- method_option :instances, :type => :array,
113
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
116
+ method_option :instance_roles, :type => :hash,
117
+ :default => {},
118
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
114
119
 
115
- method_option :instance_roles, :type => :hash,
116
- :default => {},
117
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
120
+ method_option :instance_names, :type => :hash,
121
+ :default => {},
122
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
118
123
 
119
- method_option :instance_names, :type => :hash,
120
- :default => {},
121
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
124
+ method_option :verbose, :type => :boolean,
125
+ :default => false,
126
+ :desc => "Verbose output",
127
+ :aliases => ["-v"]
128
+ desc "integrate", "Integrate other instances into this cluster"
129
+ def integrate
130
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
131
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-integrate.log")
122
132
 
123
- method_option :verbose, :type => :boolean,
124
- :default => false,
125
- :desc => "Verbose output",
126
- :aliases => ["-v"]
127
- desc "integrate", "Integrate other instances into this cluster"
128
- def integrate
129
- EY::LoggedOutput.verbose = options[:verbose]
130
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-integrate.log")
133
+ app_dir = Pathname.new "/data/#{options[:app]}"
134
+ current_app_dir = app_dir + "current"
131
135
 
132
- app_dir = Pathname.new "/data/#{options[:app]}"
133
- current_app_dir = app_dir + "current"
136
+ # so that we deploy to the same place there that we have here
137
+ integrate_options = options.dup
138
+ integrate_options[:release_path] = current_app_dir.realpath.to_s
134
139
 
135
- # so that we deploy to the same place there that we have here
136
- integrate_options = options.dup
137
- integrate_options[:release_path] = current_app_dir.realpath.to_s
140
+ # we have to deploy the same SHA there as here
141
+ integrate_options[:branch] = (current_app_dir + 'REVISION').read.strip
138
142
 
139
- # we have to deploy the same SHA there as here
140
- integrate_options[:branch] = (current_app_dir + 'REVISION').read.strip
143
+ config = EY::Serverside::Deploy::Configuration.new(integrate_options)
141
144
 
142
- config = EY::Deploy::Configuration.new(integrate_options)
145
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
143
146
 
144
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
147
+ invoke :propagate
145
148
 
146
- invoke :propagate
149
+ EY::Serverside::Server.all.each do |server|
150
+ server.sync_directory app_dir
151
+ # we're just about to recreate this, so it has to be gone
152
+ # first. otherwise, non-idempotent deploy hooks could screw
153
+ # things up, and since we don't control deploy hooks, we must
154
+ # assume the worst.
155
+ server.run("rm -rf #{current_app_dir}")
156
+ end
147
157
 
148
- EY::Server.all.each do |server|
149
- server.sync_directory app_dir
150
- # we're just about to recreate this, so it has to be gone
151
- # first. otherwise, non-idempotent deploy hooks could screw
152
- # things up, and since we don't control deploy hooks, we must
153
- # assume the worst.
154
- server.run("rm -rf #{current_app_dir}")
158
+ # deploy local-ref to other instances into /data/$app/local-current
159
+ EY::Serverside::Deploy.new(config).cached_deploy
155
160
  end
156
161
 
157
- # deploy local-ref to other instances into /data/$app/local-current
158
- EY::Deploy.new(config).cached_deploy
159
- end
160
-
161
- method_option :app, :type => :string,
162
- :required => true,
163
- :desc => "Application to deploy",
164
- :aliases => ["-a"]
162
+ method_option :app, :type => :string,
163
+ :required => true,
164
+ :desc => "Application to deploy",
165
+ :aliases => ["-a"]
165
166
 
166
- method_option :stack, :type => :string,
167
- :desc => "Web stack (so we can restart it correctly)"
167
+ method_option :stack, :type => :string,
168
+ :desc => "Web stack (so we can restart it correctly)"
168
169
 
169
- method_option :instances, :type => :array,
170
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
170
+ method_option :instances, :type => :array,
171
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
171
172
 
172
- method_option :instance_roles, :type => :hash,
173
- :default => {},
174
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
173
+ method_option :instance_roles, :type => :hash,
174
+ :default => {},
175
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
175
176
 
176
- method_option :instance_names, :type => :hash,
177
- :default => {},
178
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
177
+ method_option :instance_names, :type => :hash,
178
+ :default => {},
179
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
179
180
 
180
- method_option :verbose, :type => :boolean,
181
- :default => false,
182
- :desc => "Verbose output",
183
- :aliases => ["-v"]
184
- desc "restart", "Restart app servers, conditionally enabling maintenance page"
185
- def restart
186
- EY::LoggedOutput.verbose = options[:verbose]
187
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-restart.log")
181
+ method_option :verbose, :type => :boolean,
182
+ :default => false,
183
+ :desc => "Verbose output",
184
+ :aliases => ["-v"]
185
+ desc "restart", "Restart app servers, conditionally enabling maintenance page"
186
+ def restart
187
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
188
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-restart.log")
188
189
 
189
- config = EY::Deploy::Configuration.new(options)
190
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
190
+ config = EY::Serverside::Deploy::Configuration.new(options)
191
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
191
192
 
192
- invoke :propagate
193
+ invoke :propagate
193
194
 
194
- EY::Deploy.new(config).restart_with_maintenance_page
195
- end
195
+ EY::Serverside::Deploy.new(config).restart_with_maintenance_page
196
+ end
196
197
 
197
- desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
198
- def install_bundler(version)
199
- egrep_escaped_version = version.gsub(/\./, '\.')
200
- # the grep "bundler " is so that gems like bundler08 don't get
201
- # their versions considered too
202
- #
203
- # the [,$] is to stop us from looking for e.g. 0.9.2, seeing
204
- # 0.9.22, and mistakenly thinking 0.9.2 is there
205
- has_bundler_cmd = "gem list bundler | grep \"bundler \" | egrep -q '#{egrep_escaped_version}[,)]'"
206
-
207
- unless system(has_bundler_cmd)
208
- system("gem install bundler -q --no-rdoc --no-ri -v '#{version}'")
198
+ desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
199
+ def install_bundler(version)
200
+ egrep_escaped_version = version.gsub(/\./, '\.')
201
+ # the grep "bundler " is so that gems like bundler08 don't get
202
+ # their versions considered too
203
+ #
204
+ # the [,$] is to stop us from looking for e.g. 0.9.2, seeing
205
+ # 0.9.22, and mistakenly thinking 0.9.2 is there
206
+ has_bundler_cmd = "gem list bundler | grep \"bundler \" | egrep -q '#{egrep_escaped_version}[,)]'"
207
+
208
+ unless system(has_bundler_cmd)
209
+ system("gem install bundler -q --no-rdoc --no-ri -v '#{version}'")
210
+ end
209
211
  end
210
- end
211
212
 
212
- desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{VERSION}."
213
- def propagate
214
- config = EY::Deploy::Configuration.new
215
- gem_filename = "engineyard-serverside-#{VERSION}.gem"
216
- local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
217
- remote_gem_file = File.join(Dir.tmpdir, gem_filename)
218
- gem_binary = File.join(Gem.default_bindir, 'gem')
219
-
220
- barrier(*(EY::Server.all.find_all do |server|
221
- !server.local? # of course this machine has it
222
- end.map do |server|
223
- need_later do
224
- egrep_escaped_version = VERSION.gsub(/\./, '\.')
225
- # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
226
- # 0.5.11, and mistakenly thinking 0.5.1 is there
227
- has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
228
-
229
- if !server.run(has_gem_cmd) # doesn't have this exact version
230
- puts "~> Installing engineyard-serverside on #{server.hostname}"
231
-
232
- system(Escape.shell_command([
233
- 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
234
- "-o", "StrictHostKeyChecking=no",
235
- local_gem_file,
236
- "#{config.user}@#{server.hostname}:#{remote_gem_file}",
237
- ]))
238
- server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
213
+ desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{EY::Serverside::VERSION}."
214
+ def propagate
215
+ config = EY::Serverside::Deploy::Configuration.new
216
+ gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
217
+ local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
218
+ remote_gem_file = File.join(Dir.tmpdir, gem_filename)
219
+ gem_binary = File.join(Gem.default_bindir, 'gem')
220
+
221
+ barrier(*(EY::Serverside::Server.all.find_all do |server|
222
+ !server.local? # of course this machine has it
223
+ end.map do |server|
224
+ need_later do
225
+ egrep_escaped_version = EY::Serverside::VERSION.gsub(/\./, '\.')
226
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
227
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
228
+ has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
229
+
230
+ if !server.run(has_gem_cmd) # doesn't have this exact version
231
+ puts "~> Installing engineyard-serverside on #{server.hostname}"
232
+
233
+ system(Escape.shell_command([
234
+ 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
235
+ "-o", "StrictHostKeyChecking=no",
236
+ local_gem_file,
237
+ "#{config.user}@#{server.hostname}:#{remote_gem_file}",
238
+ ]))
239
+ server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
240
+ end
239
241
  end
240
- end
241
- end))
242
- end
242
+ end))
243
+ end
243
244
 
244
- private
245
+ private
245
246
 
246
- def assemble_instance_hashes(config)
247
- options[:instances].collect { |hostname|
248
- { :hostname => hostname,
249
- :roles => options[:instance_roles][hostname].to_s.split(','),
250
- :name => options[:instance_names][hostname],
251
- :user => config.user,
247
+ def assemble_instance_hashes(config)
248
+ options[:instances].collect { |hostname|
249
+ { :hostname => hostname,
250
+ :roles => options[:instance_roles][hostname].to_s.split(','),
251
+ :name => options[:instance_names][hostname],
252
+ :user => config.user,
253
+ }
252
254
  }
253
- }
255
+ end
254
256
  end
255
257
  end
256
258
  end