kompanee-recipes 0.0.10 → 0.1.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.
Potentially problematic release.
This version of kompanee-recipes might be problematic. Click here for more details.
- data/README.md +182 -0
- data/lib/kompanee-recipes/apache.rb +241 -0
- data/lib/kompanee-recipes/bash.rb +44 -0
- data/lib/kompanee-recipes/bundler.rb +24 -0
- data/lib/kompanee-recipes/deploy.rb +56 -247
- data/lib/kompanee-recipes/environment.rb +213 -0
- data/lib/kompanee-recipes/git.rb +40 -0
- data/lib/kompanee-recipes/heroku.rb +243 -0
- data/lib/kompanee-recipes/mysql.rb +128 -0
- data/lib/kompanee-recipes/passenger.rb +124 -0
- data/lib/kompanee-recipes/php.rb +16 -0
- data/lib/kompanee-recipes/phpmyadmin.rb +39 -0
- data/lib/kompanee-recipes/rackspace.rb +35 -0
- data/lib/kompanee-recipes/rvm.rb +99 -0
- data/lib/kompanee-recipes/sqlite3.rb +9 -0
- data/lib/kompanee-recipes/subversion.rb +20 -0
- data/lib/kompanee-recipes/ubuntu.rb +235 -0
- metadata +22 -17
- data/History.txt +0 -4
- data/Manifest.txt +0 -12
- data/PostInstall.txt +0 -7
- data/README.rdoc +0 -51
- data/Rakefile +0 -24
- data/lib/kompanee-recipes.rb +0 -6
- data/lib/kompanee-recipes/app_setup.rb +0 -92
- data/lib/kompanee-recipes/bash_environment.rb +0 -31
- data/lib/kompanee-recipes/ubuntu_server_setup.rb +0 -277
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# kompanee-recipes #
|
2
|
+
|
3
|
+
[http://github.com/thekompanee/kompanee-recipes](http://github.com/thekompanee/kompanee-recipes)
|
4
|
+
|
5
|
+
## Description ##
|
6
|
+
|
7
|
+
These are the common recipes we've been using here at The Kompanee. Packaged as a gem.
|
8
|
+
|
9
|
+
## Features/Problems ##
|
10
|
+
|
11
|
+
None yet
|
12
|
+
|
13
|
+
## Synopsis ##
|
14
|
+
|
15
|
+
To the top of your Capfile add:
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'kompanee-recipes/deploy'
|
19
|
+
|
20
|
+
## Requirements ##
|
21
|
+
|
22
|
+
Capistrano (Obv)
|
23
|
+
|
24
|
+
## Install ##
|
25
|
+
|
26
|
+
gem install kompanee-recipes
|
27
|
+
|
28
|
+
## Usage ##
|
29
|
+
|
30
|
+
### New Server Setup ###
|
31
|
+
|
32
|
+
#### Initial Connection ####
|
33
|
+
You need to add the server to your hosts file so you'll want to do an
|
34
|
+
initial SSH connection before we get into our Capistrano configuration.
|
35
|
+
|
36
|
+
ssh root@<server_name>
|
37
|
+
|
38
|
+
Accept the authenticity of the host.
|
39
|
+
|
40
|
+
#### User Creation ####
|
41
|
+
By default, the Kompanee recipes will install a `deploy` user and a
|
42
|
+
`manager` user.
|
43
|
+
|
44
|
+
##### Installation #####
|
45
|
+
cap <environment> os:users:create
|
46
|
+
|
47
|
+
##### Manual Steps As `Root` #####
|
48
|
+
Log in as `root`
|
49
|
+
|
50
|
+
ssh root@<server_name>
|
51
|
+
|
52
|
+
###### Password Assignment ######
|
53
|
+
Log in to each of these and give them their custom passwords. On
|
54
|
+
Ubuntu this is done by loggin in as root and typing:
|
55
|
+
|
56
|
+
passwd deploy
|
57
|
+
passwd manager
|
58
|
+
|
59
|
+
At this point you'll probably want to change the root password to
|
60
|
+
something more secure as well.
|
61
|
+
|
62
|
+
passwd
|
63
|
+
|
64
|
+
###### Sudo Assignment ######
|
65
|
+
Finally, let's add the `manager` user to the `sudoers` file so we
|
66
|
+
no longer need to log in as `root`
|
67
|
+
|
68
|
+
visudo
|
69
|
+
|
70
|
+
Add a line like so:
|
71
|
+
|
72
|
+
manager ALL=(ALL) ALL
|
73
|
+
|
74
|
+
And a line like this:
|
75
|
+
|
76
|
+
Defaults visiblepw
|
77
|
+
|
78
|
+
From the documentation:
|
79
|
+
|
80
|
+
**visiblepw**
|
81
|
+
By default, sudo will refuse to run if the user must enter
|
82
|
+
a password but it is not possible to disable echo on the
|
83
|
+
terminal. If the `visiblepw` flag is set, sudo will prompt
|
84
|
+
for a password even when it would be visible on the screen.
|
85
|
+
This makes it possible to run things like:
|
86
|
+
|
87
|
+
rsh somehost sudo ls
|
88
|
+
|
89
|
+
since rsh(1) does not allocate a tty. This flag is off by
|
90
|
+
default.
|
91
|
+
|
92
|
+
##### Authorize Public Key #####
|
93
|
+
Once that's finished, go back to Capistrano and type:
|
94
|
+
|
95
|
+
cap <environment> os:users:authorize_public_keys
|
96
|
+
|
97
|
+
That will give you the ability to log into your accounts with no password required.
|
98
|
+
|
99
|
+
#### Note on Using Github ####
|
100
|
+
If you're using Github to deploy your app, you're going to need to add the
|
101
|
+
public key of the deployment user to your Github account.
|
102
|
+
|
103
|
+
cap <environment> os:users:deploy:public_key:create
|
104
|
+
|
105
|
+
View the public key by logging in as `deploy` and typing:
|
106
|
+
|
107
|
+
cat /home/deploy/.ssh/id_rsa.pub
|
108
|
+
|
109
|
+
Copy and paste the output into Github's "Public Keys" section of the Github at:
|
110
|
+
|
111
|
+
[https://github.com/account](https://github.com/account)
|
112
|
+
|
113
|
+
Log in as `deploy` and test it out by typing:
|
114
|
+
|
115
|
+
ssh git@github.com
|
116
|
+
|
117
|
+
#### Setup Deployment Server ####
|
118
|
+
Now that we have our public keys installed, we can do a relatively simple installation.
|
119
|
+
|
120
|
+
cap <environment> server_setup
|
121
|
+
|
122
|
+
### Application Use ###
|
123
|
+
When using with an application, you'll need to create both a `staging` and
|
124
|
+
a `production` task in the application's `deploy.rb` file.
|
125
|
+
|
126
|
+
This will allow you to set specific environment variables for your application
|
127
|
+
and stage.
|
128
|
+
|
129
|
+
#### Shared Configuration Files ####
|
130
|
+
Shared configuration files should be added to your local machine but not committed to
|
131
|
+
source control.
|
132
|
+
|
133
|
+
They can be added by appending the stage name to the file's name. For example if
|
134
|
+
you're wanting to share your `config/database.yml` file in your `production` stage,
|
135
|
+
you should have a file called `config/database.yml.production` which contains your
|
136
|
+
sensitive production logins.
|
137
|
+
|
138
|
+
When you deploy, if you have a file that meets these requirements, it will be copied
|
139
|
+
to the proper location on the server.
|
140
|
+
|
141
|
+
#### Initial Deployment ####
|
142
|
+
In order to perform the initial deployment, type:
|
143
|
+
|
144
|
+
cap <environment> deploy:initial
|
145
|
+
|
146
|
+
This will:
|
147
|
+
|
148
|
+
* Set up the directory structure for the app
|
149
|
+
* Create the database
|
150
|
+
* Install the website on the web server
|
151
|
+
* Updates the code from the Source Control system
|
152
|
+
* Installs all gems
|
153
|
+
* Runs all pending migrations
|
154
|
+
* Tags the commit with a deployment tag
|
155
|
+
* Cleans up all extra deployment versions
|
156
|
+
* Enables the website
|
157
|
+
* Restarts the server
|
158
|
+
|
159
|
+
## License ##
|
160
|
+
|
161
|
+
(The MIT License)
|
162
|
+
|
163
|
+
Copyright (c) 2010 FIXME full name
|
164
|
+
|
165
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
166
|
+
a copy of this software and associated documentation files (the
|
167
|
+
'Software'), to deal in the Software without restriction, including
|
168
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
169
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
170
|
+
permit persons to whom the Software is furnished to do so, subject to
|
171
|
+
the following conditions:
|
172
|
+
|
173
|
+
The above copyright notice and this permission notice shall be
|
174
|
+
included in all copies or substantial portions of the Software.
|
175
|
+
|
176
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
177
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
178
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
179
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
180
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
181
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
182
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,241 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
set :web_server_name, "apache"
|
3
|
+
|
4
|
+
run_task "web_server:install", :as => "manager"
|
5
|
+
run_task "web_server:stop", :as => "manager"
|
6
|
+
run_task "web_server:start", :as => "manager"
|
7
|
+
run_task "web_server:restart", :as => "manager"
|
8
|
+
|
9
|
+
run_task "website:install", :as => "manager"
|
10
|
+
run_task "website:remove", :as => "manager"
|
11
|
+
run_task "website:enable", :as => "manager"
|
12
|
+
run_task "website:disable", :as => "manager"
|
13
|
+
|
14
|
+
run_task "apache:modules:ssl:install", :as => "manager"
|
15
|
+
|
16
|
+
namespace :deploy do
|
17
|
+
namespace :web do
|
18
|
+
desc <<-DESC
|
19
|
+
Enables the website's application by removing the maintenance page.
|
20
|
+
DESC
|
21
|
+
task :enable do
|
22
|
+
website.maintenance_mode.disable
|
23
|
+
end
|
24
|
+
|
25
|
+
desc <<-DESC
|
26
|
+
Disables the website's application by installing the maintenance page.
|
27
|
+
DESC
|
28
|
+
task :disable do
|
29
|
+
website.maintenance_mode.enable
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace :web_server do
|
35
|
+
desc "Install Apache"
|
36
|
+
task :install do
|
37
|
+
apache.send("install_on_#{os_type}")
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Stop Apache"
|
41
|
+
task :stop do
|
42
|
+
run "#{sudo} /etc/init.d/apache2 stop"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Start Apache"
|
46
|
+
task :start do
|
47
|
+
run "#{sudo} /etc/init.d/apache2 start"
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Restart Apache"
|
51
|
+
task :restart do
|
52
|
+
run "#{sudo} /etc/init.d/apache2 restart"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
namespace :website do
|
57
|
+
desc "Installs the site configuration for the files."
|
58
|
+
task :install do
|
59
|
+
apache.virtual_host.install
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Completely removes the site configuration from the server (but leaves the files.)"
|
63
|
+
task :remove do
|
64
|
+
apache.virtual_host.remove
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Enable Site"
|
68
|
+
task :enable do
|
69
|
+
run "#{sudo} a2ensite #{deploy_name}"
|
70
|
+
run "#{sudo} /etc/init.d/apache2 reload"
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Disable Site"
|
74
|
+
task :disable do
|
75
|
+
run "#{sudo} a2dissite #{deploy_name}"
|
76
|
+
run "#{sudo} /etc/init.d/apache2 reload"
|
77
|
+
end
|
78
|
+
|
79
|
+
namespace :maintenance_mode do
|
80
|
+
desc <<-DESC
|
81
|
+
Makes the application web-accessible again. Removes the \
|
82
|
+
"maintenance.html" page generated by deploy:web:disable, which (if your \
|
83
|
+
web servers are configured correctly) will make your application \
|
84
|
+
web-accessible again.
|
85
|
+
DESC
|
86
|
+
task :disable, :except => { :no_release => true } do
|
87
|
+
run "rm #{shared_path}/system/maintenance.html"
|
88
|
+
end
|
89
|
+
|
90
|
+
desc <<-DESC
|
91
|
+
Present a maintenance page to visitors. Disables your application's web \
|
92
|
+
interface by writing a "maintenance.html" file to each web server. The \
|
93
|
+
servers must be configured to detect the presence of this file, and if \
|
94
|
+
it is present, always display it instead of performing the request.
|
95
|
+
|
96
|
+
By default, the maintenance page will just say the site is down for \
|
97
|
+
"maintenance", and will be back "shortly", but you can customize the \
|
98
|
+
page by specifying the REASON and UNTIL environment variables:
|
99
|
+
|
100
|
+
$ cap deploy:web:disable \\
|
101
|
+
REASON="hardware upgrade" \\
|
102
|
+
UNTIL="12pm Central Time"
|
103
|
+
|
104
|
+
Further customization will require that you write your own task.
|
105
|
+
DESC
|
106
|
+
task :enable, :except => { :no_release => true } do
|
107
|
+
on_rollback { rm "#{shared_path}/system/maintenance.html" }
|
108
|
+
|
109
|
+
require 'erb'
|
110
|
+
deadline, reason = ENV['UNTIL'], ENV['REASON']
|
111
|
+
|
112
|
+
template = File.read("./app/views/pages/maintenance.html.erb")
|
113
|
+
maintenance_page = ERB.new(template).result(binding)
|
114
|
+
|
115
|
+
put maintenance_page, "#{shared_path}/system/maintenance.html", :mode => 0644
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
namespace :apache do
|
121
|
+
namespace :virtual_host do
|
122
|
+
desc "Install Virtual Host"
|
123
|
+
task :install do
|
124
|
+
virtual_host_config = <<-VHOST
|
125
|
+
<VirtualHost #{web_server_ip}:443>
|
126
|
+
ServerName #{deploy_name}
|
127
|
+
DocumentRoot #{deploy_to}/current/public
|
128
|
+
|
129
|
+
SSLEngine on
|
130
|
+
SSLCertificateFile /etc/ssl/certs/#{domain}.crt
|
131
|
+
SSLCertificateKeyFile /etc/ssl/certs/#{domain}.key
|
132
|
+
|
133
|
+
<Directory "#{deploy_to}/current/public">
|
134
|
+
Options FollowSymLinks -MultiViews
|
135
|
+
AllowOverride all
|
136
|
+
Order allow,deny
|
137
|
+
Allow from all
|
138
|
+
</Directory>
|
139
|
+
|
140
|
+
RewriteEngine On
|
141
|
+
|
142
|
+
ErrorDocument 503 /system/maintenance.html
|
143
|
+
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
|
144
|
+
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
|
145
|
+
RewriteCond %{REQUEST_URI} !^/images/
|
146
|
+
RewriteCond %{REQUEST_URI} !^/robots.txt
|
147
|
+
RewriteCond %{REQUEST_URI} !^/sitemap
|
148
|
+
RewriteRule ^.*$ - [redirect=503,last]
|
149
|
+
|
150
|
+
ErrorLog /var/log/apache2/#{application}-errors.log
|
151
|
+
|
152
|
+
LogLevel warn
|
153
|
+
|
154
|
+
CustomLog /var/log/apache2/#{application}-access.log combined
|
155
|
+
ServerSignature On
|
156
|
+
</VirtualHost>
|
157
|
+
|
158
|
+
<VirtualHost 174.143.210.66:80>
|
159
|
+
ServerName #{deploy_name}
|
160
|
+
|
161
|
+
Redirect permanent / https://#{deploy_name}
|
162
|
+
</VirtualHost>
|
163
|
+
VHOST
|
164
|
+
|
165
|
+
run "#{sudo} a2enmod rewrite"
|
166
|
+
put virtual_host_config, "#{user_home}/#{deploy_name}"
|
167
|
+
run "#{sudo} mv #{user_home}/#{deploy_name} /etc/apache2/sites-available"
|
168
|
+
run "#{sudo} /etc/init.d/apache2 reload"
|
169
|
+
end
|
170
|
+
|
171
|
+
desc "Remove Virtual Host"
|
172
|
+
task :remove do
|
173
|
+
run "#{sudo} rm /etc/apache2/sites-available/#{deploy_name}"
|
174
|
+
run "#{sudo} /etc/init.d/apache2 reload"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
desc "Install Apache"
|
179
|
+
task :install_on_ubuntu do
|
180
|
+
set :dependencies, %w{apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 apache2-prefork-dev libapr1-dev}
|
181
|
+
os.package_manager.install
|
182
|
+
run "#{sudo} chown -R #{deployment_username}:#{deployment_username} #{deploy_dir}"
|
183
|
+
|
184
|
+
default_httpd_conf = <<-HTTPDCONF
|
185
|
+
# =========================================================
|
186
|
+
# Basic Settings
|
187
|
+
# =========================================================
|
188
|
+
|
189
|
+
Listen #{web_server_ip}:80
|
190
|
+
Listen #{web_server_ip}:443
|
191
|
+
|
192
|
+
ServerName #{web_server_name}
|
193
|
+
|
194
|
+
# =========================================================
|
195
|
+
# Access control
|
196
|
+
# =========================================================
|
197
|
+
|
198
|
+
#<Directory />
|
199
|
+
# Options FollowSymLinks
|
200
|
+
# AllowOverride None
|
201
|
+
# Order deny,allow
|
202
|
+
# Deny from all
|
203
|
+
#</Directory>
|
204
|
+
|
205
|
+
# =========================================================
|
206
|
+
# Virtual Host Directives
|
207
|
+
# =========================================================
|
208
|
+
|
209
|
+
NameVirtualHost #{web_server_ip}:80
|
210
|
+
NameVirtualHost #{web_server_ip}:443
|
211
|
+
HTTPDCONF
|
212
|
+
|
213
|
+
put default_httpd_conf, "#{user_home}/default_httpd_conf"
|
214
|
+
run "#{sudo} mv #{user_home}/default_httpd_conf /etc/apache2/httpd.conf"
|
215
|
+
run "#{sudo} mv /etc/apache2/ports.conf /etc/apache2/ports.conf.bak"
|
216
|
+
run "#{sudo} touch /etc/apache2/ports.conf"
|
217
|
+
|
218
|
+
run "#{sudo} rm #{deploy_dir}/index.html"
|
219
|
+
run "#{sudo} rm -rf /etc/apache2/sites-enabled/*"
|
220
|
+
run "#{sudo} rm -rf /etc/apache2/sites-available/*"
|
221
|
+
|
222
|
+
run "#{sudo} a2enmod rewrite"
|
223
|
+
end
|
224
|
+
|
225
|
+
namespace :modules do
|
226
|
+
namespace :ssl do
|
227
|
+
desc "Install SSL Module"
|
228
|
+
task :install do
|
229
|
+
certs_dir = "/etc/ssl/certs"
|
230
|
+
|
231
|
+
run "#{sudo} a2enmod ssl"
|
232
|
+
run "#{sudo} bash -c \"openssl genrsa 2048 > #{certs_dir}/#{domain}.key 2> /dev/null\""
|
233
|
+
run "#{sudo} bash -c \"openssl req -new -x509 -days 3650 -subj '/C=US/ST=Tennessee/L=Nashville/O=The Kompanee/CN=*.#{domain}/emailAddress=webmaster@#{domain}' -key #{certs_dir}/#{domain}.key > #{certs_dir}/#{domain}.crt\""
|
234
|
+
run "#{sudo} bash -c \"openssl x509 -noout -fingerprint -text < #{certs_dir}/#{domain}.crt > #{certs_dir}/#{domain}.info\""
|
235
|
+
run "#{sudo} bash -c \"cat #{certs_dir}/#{domain}.crt #{certs_dir}/#{domain}.key > #{certs_dir}/#{domain}.pem\""
|
236
|
+
run "#{sudo} chmod 400 #{certs_dir}/#{domain}.pem #{certs_dir}/#{domain}.key"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
run_task 'bash:environment:prepare', :as => "manager"
|
3
|
+
run_task 'bash:environment:enable_safer_sshd_config', :as => "manager"
|
4
|
+
|
5
|
+
namespace :bash do
|
6
|
+
namespace :environment do
|
7
|
+
desc "Clones the common Kompanee Common Bash Environment from The Kompanee's Github repository."
|
8
|
+
task :prepare do
|
9
|
+
run "#{sudo} git clone git://github.com/thekompanee/kompanee-common.git /usr/share/kompanee-common"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Install The Kompanee Common Bash Environment for a specific user"
|
13
|
+
task :install do
|
14
|
+
run "if [ -f #{user_home}/.bashrc ]; then mv #{user_home}/.bashrc #{user_home}/.bashrc.bak; fi"
|
15
|
+
run "if [ -f #{user_home}/.inputrc ]; then mv #{user_home}/.inputrc #{user_home}/.inputrc.bak; fi"
|
16
|
+
run "ln -fs /usr/share/kompanee-common/bash_customizations #{user_home}/.bash"
|
17
|
+
run "ln -fs /usr/share/kompanee-common/bash_customizations/bashrc #{user_home}/.bashrc"
|
18
|
+
run "ln -fs /usr/share/kompanee-common/bash_customizations/inputrc #{user_home}/.inputrc"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc <<-DESC
|
22
|
+
This will install a more secure SSH environment.
|
23
|
+
|
24
|
+
Among other things, it will disable password authentication and change
|
25
|
+
the default port.
|
26
|
+
DESC
|
27
|
+
task :enable_safer_sshd_config do
|
28
|
+
run "#{sudo} mv /etc/ssh/sshd_config /etc/ssh/sshd_config.bak"
|
29
|
+
run "#{sudo} ln -fs /usr/share/kompanee-common/ssh/sshd_config /etc/ssh/sshd_config"
|
30
|
+
run "#{sudo} /etc/init.d/ssh restart"
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Perform full Bash setup process"
|
34
|
+
task :setup do
|
35
|
+
bash.environment.prepare
|
36
|
+
|
37
|
+
run_task "bash:environment:install", :as => "manager", :now => true
|
38
|
+
run_task "bash:environment:install", :as => "deploy", :now => true
|
39
|
+
|
40
|
+
bash.environment.enable_safer_sshd_config
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|