beaker-puppet 1.29.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/release.yml +2 -2
- data/.github/workflows/test.yml +28 -7
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +842 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +5 -20
- data/Rakefile +64 -169
- data/acceptance/config/acceptance-options.rb +3 -3
- data/acceptance/config/gem/acceptance-options.rb +8 -8
- data/acceptance/config/git/acceptance-options.rb +8 -8
- data/acceptance/config/pkg/acceptance-options.rb +7 -7
- data/acceptance/pre_suite/gem/install.rb +6 -6
- data/acceptance/pre_suite/git/install.rb +22 -22
- data/acceptance/pre_suite/pkg/install.rb +3 -3
- data/acceptance/tests/backwards_compatible.rb +6 -7
- data/acceptance/tests/clone_git_repo_on_test.rb +12 -13
- data/acceptance/tests/create_tmpdir_on_test.rb +13 -9
- data/acceptance/tests/install_smoke_test.rb +5 -4
- data/acceptance/tests/stub_host.rb +11 -10
- data/acceptance/tests/web_helpers_test.rb +11 -10
- data/beaker-puppet.gemspec +16 -23
- data/bin/beaker-puppet +2 -4
- data/lib/beaker-puppet/helpers/facter_helpers.rb +9 -7
- data/lib/beaker-puppet/helpers/host_helpers.rb +10 -7
- data/lib/beaker-puppet/helpers/puppet_helpers.rb +151 -160
- data/lib/beaker-puppet/helpers/rake_helpers.rb +1 -1
- data/lib/beaker-puppet/helpers/tk_helpers.rb +22 -28
- data/lib/beaker-puppet/install_utils/aio_defaults.rb +39 -43
- data/lib/beaker-puppet/install_utils/ezbake_utils.rb +34 -42
- data/lib/beaker-puppet/install_utils/foss_defaults.rb +134 -138
- data/lib/beaker-puppet/install_utils/foss_utils.rb +293 -320
- data/lib/beaker-puppet/install_utils/module_utils.rb +58 -70
- data/lib/beaker-puppet/install_utils/puppet5.rb +30 -35
- data/lib/beaker-puppet/install_utils/puppet_utils.rb +58 -68
- data/lib/beaker-puppet/install_utils/windows_utils.rb +34 -36
- data/lib/beaker-puppet/version.rb +1 -1
- data/lib/beaker-puppet/wrappers.rb +13 -14
- data/lib/beaker-puppet.rb +4 -5
- data/setup/aio/010_Install_Puppet_Agent.rb +5 -6
- data/setup/common/000-delete-puppet-when-none.rb +2 -4
- data/setup/common/003_solaris_cert_fix.rb +74 -70
- data/setup/common/005_redhat_subscription_fix.rb +3 -2
- data/setup/common/011_Install_Puppet_Server.rb +7 -9
- data/setup/common/012_Finalize_Installs.rb +5 -5
- data/setup/common/025_StopFirewall.rb +1 -1
- data/setup/common/030_StopSssd.rb +2 -2
- data/setup/common/040_ValidateSignCert.rb +10 -12
- data/setup/common/045_EnsureMasterStarted.rb +2 -2
- data/setup/gem/010_GemInstall.rb +5 -4
- data/setup/git/000_EnvSetup.rb +48 -48
- data/setup/git/010_TestSetup.rb +13 -12
- data/setup/git/020_PuppetUserAndGroup.rb +3 -2
- data/setup/git/060_InstallModules.rb +14 -14
- data/setup/git/070_InstallCACerts.rb +82 -82
- data/spec/beaker-puppet/helpers/facter_helpers_spec.rb +22 -24
- data/spec/beaker-puppet/helpers/host_helpers_spec.rb +10 -6
- data/spec/beaker-puppet/helpers/puppet_helpers_spec.rb +506 -517
- data/spec/beaker-puppet/helpers/tk_helpers_spec.rb +20 -24
- data/spec/beaker-puppet/install_utils/ezbake_utils_spec.rb +86 -90
- data/spec/beaker-puppet/install_utils/foss_utils_spec.rb +636 -599
- data/spec/beaker-puppet/install_utils/module_utils_spec.rb +125 -116
- data/spec/beaker-puppet/install_utils/puppet5_spec.rb +159 -165
- data/spec/beaker-puppet/install_utils/puppet_utils_spec.rb +92 -77
- data/spec/beaker-puppet/install_utils/windows_utils_spec.rb +101 -89
- data/spec/beaker-puppet/wrappers_spec.rb +10 -10
- data/spec/helpers.rb +85 -91
- data/tasks/ci.rake +171 -179
- metadata +33 -62
- data/setup/common/020_InstallCumulusModules.rb +0 -13
- data/setup/common/021_InstallAristaModuleMasters.rb +0 -12
- data/setup/common/022_InstallAristaModuleAgents.rb +0 -13
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
require 'hocon'
|
3
2
|
require 'hocon/config_error'
|
4
3
|
require 'inifile'
|
@@ -8,7 +7,6 @@ module Beaker
|
|
8
7
|
module Helpers
|
9
8
|
# Convenience methods for modifying and reading TrapperKeeper configs
|
10
9
|
module TKHelpers
|
11
|
-
|
12
10
|
# Modify the given TrapperKeeper config file.
|
13
11
|
#
|
14
12
|
# @param [Host] host A host object
|
@@ -25,20 +23,17 @@ module Beaker
|
|
25
23
|
# particularly care which of these the file named by `config_file_path` on
|
26
24
|
# the SUT actually is, just that the contents can be parsed into a map.
|
27
25
|
#
|
28
|
-
def modify_tk_config(host, config_file_path, options_hash, replace=false)
|
29
|
-
if options_hash.empty?
|
30
|
-
return nil
|
31
|
-
end
|
26
|
+
def modify_tk_config(host, config_file_path, options_hash, replace = false)
|
27
|
+
return nil if options_hash.empty?
|
32
28
|
|
33
29
|
new_hash = Beaker::Options::OptionsHash.new
|
34
30
|
|
35
31
|
if replace
|
36
32
|
new_hash.merge!(options_hash)
|
37
33
|
else
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
file_string = host.exec( Command.new( "cat #{config_file_path}" )).stdout
|
34
|
+
raise "Error: #{config_file_path} does not exist on #{host}" unless host.file_exist?(config_file_path)
|
35
|
+
|
36
|
+
file_string = host.exec(Command.new("cat #{config_file_path}")).stdout
|
42
37
|
|
43
38
|
begin
|
44
39
|
tk_conf_hash = read_tk_config_string(file_string)
|
@@ -61,29 +56,28 @@ module Beaker
|
|
61
56
|
# a RuntimeException if none of the parsers succeed.
|
62
57
|
#
|
63
58
|
# @!visibility private
|
64
|
-
def read_tk_config_string(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
59
|
+
def read_tk_config_string(string)
|
60
|
+
begin
|
61
|
+
return Hocon.parse(string)
|
62
|
+
rescue Hocon::ConfigError
|
63
|
+
nil
|
64
|
+
end
|
70
65
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
66
|
+
begin
|
67
|
+
return JSON.parse(string)
|
68
|
+
rescue JSON::JSONError
|
69
|
+
nil
|
70
|
+
end
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
72
|
+
begin
|
73
|
+
return IniFile.new(content: string)
|
74
|
+
rescue IniFile::Error
|
75
|
+
nil
|
76
|
+
end
|
82
77
|
|
83
|
-
|
78
|
+
raise 'Failed to read TrapperKeeper config!'
|
84
79
|
end
|
85
80
|
end
|
86
|
-
|
87
81
|
end
|
88
82
|
end
|
89
83
|
end
|
@@ -5,15 +5,14 @@ module Beaker
|
|
5
5
|
# This module contains default values for aio paths and directorys per-platform
|
6
6
|
#
|
7
7
|
module AIODefaults
|
8
|
-
|
9
|
-
#Here be the pathing and default values for AIO installs
|
8
|
+
# Here be the pathing and default values for AIO installs
|
10
9
|
#
|
11
10
|
AIO_DEFAULTS = {
|
12
11
|
'unix' => {
|
13
|
-
'puppetbindir'
|
14
|
-
'privatebindir'
|
15
|
-
'distmoduledir'
|
16
|
-
'sitemoduledir'
|
12
|
+
'puppetbindir' => '/opt/puppetlabs/bin',
|
13
|
+
'privatebindir' => '/opt/puppetlabs/puppet/bin',
|
14
|
+
'distmoduledir' => '/etc/puppetlabs/code/modules',
|
15
|
+
'sitemoduledir' => '/opt/puppetlabs/puppet/modules',
|
17
16
|
},
|
18
17
|
# sitemoduledir not included on Windows (check PUP-4049 for more info).
|
19
18
|
#
|
@@ -23,20 +22,20 @@ module Beaker
|
|
23
22
|
# include sys/ruby) and the newer versions, which have no custom ruby
|
24
23
|
# directory
|
25
24
|
'windows' => { # windows with cygwin
|
26
|
-
'puppetbindir'
|
27
|
-
'privatebindir'
|
28
|
-
'distmoduledir'
|
25
|
+
'puppetbindir' => '/cygdrive/c/Program Files (x86)/Puppet Labs/Puppet/bin',
|
26
|
+
'privatebindir' => '/cygdrive/c/Program Files (x86)/Puppet Labs/Puppet/puppet/bin:/cygdrive/c/Program Files (x86)/Puppet Labs/Puppet/sys/ruby/bin',
|
27
|
+
'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/code/modules',
|
29
28
|
},
|
30
29
|
'windows-64' => { # windows with cygwin
|
31
|
-
'puppetbindir'
|
32
|
-
'privatebindir'
|
33
|
-
'distmoduledir'
|
30
|
+
'puppetbindir' => '/cygdrive/c/Program Files/Puppet Labs/Puppet/bin',
|
31
|
+
'privatebindir' => '/cygdrive/c/Program Files/Puppet Labs/Puppet/puppet/bin:/cygdrive/c/Program Files/Puppet Labs/Puppet/sys/ruby/bin',
|
32
|
+
'distmoduledir' => '`cygpath -smF 35`/PuppetLabs/code/modules',
|
34
33
|
},
|
35
34
|
'pswindows' => { # pure windows
|
36
|
-
'puppetbindir'
|
37
|
-
'privatebindir'
|
38
|
-
'distmoduledir'
|
39
|
-
}
|
35
|
+
'puppetbindir' => '"C:\\Program Files (x86)\\Puppet Labs\\Puppet\\bin";"C:\\Program Files\\Puppet Labs\\Puppet\\bin"',
|
36
|
+
'privatebindir' => '"C:\\Program Files (x86)\\Puppet Labs\\Puppet\\puppet\\bin";"C:\\Program Files\\Puppet Labs\\Puppet\\puppet\\bin";"C:\\Program Files (x86)\\Puppet Labs\\Puppet\\sys\\ruby\\bin";"C:\\Program Files\\Puppet Labs\\Puppet\\sys\\ruby\\bin"',
|
37
|
+
'distmoduledir' => 'C:\\ProgramData\\PuppetLabs\\code\\modules',
|
38
|
+
},
|
40
39
|
}
|
41
40
|
|
42
41
|
# Add the appropriate aio defaults to the host object so that they can be accessed using host[option], set host[:type] = aio
|
@@ -47,31 +46,31 @@ module Beaker
|
|
47
46
|
host[key] = val
|
48
47
|
end
|
49
48
|
# add group and type here for backwards compatability
|
50
|
-
if host['platform'] =~ /windows/
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
host['group'] = if host['platform'] =~ /windows/
|
50
|
+
'Administrators'
|
51
|
+
else
|
52
|
+
'puppet'
|
53
|
+
end
|
55
54
|
end
|
56
55
|
|
57
56
|
# Add the appropriate aio defaults to an array of hosts
|
58
57
|
# @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
|
59
58
|
# or a role (String or Symbol) that identifies one or more hosts.
|
60
59
|
def add_aio_defaults_on(hosts)
|
61
|
-
block_on hosts do |
|
60
|
+
block_on hosts do |host|
|
62
61
|
if host.is_powershell?
|
63
62
|
platform = 'pswindows'
|
64
63
|
elsif host['platform'] =~ /windows/
|
65
|
-
if host[:ruby_arch] == 'x64'
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
if host['platform'] =~ ruby_arch
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
64
|
+
ruby_arch = if host[:ruby_arch] == 'x64'
|
65
|
+
/-64/
|
66
|
+
else
|
67
|
+
/-32/
|
68
|
+
end
|
69
|
+
platform = if host['platform'] =~ ruby_arch
|
70
|
+
'windows-64'
|
71
|
+
else
|
72
|
+
'windows'
|
73
|
+
end
|
75
74
|
else
|
76
75
|
platform = 'unix'
|
77
76
|
end
|
@@ -93,21 +92,18 @@ module Beaker
|
|
93
92
|
# @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
|
94
93
|
# or a role (String or Symbol) that identifies one or more hosts.
|
95
94
|
def remove_aio_defaults_on(hosts)
|
96
|
-
block_on hosts do |
|
97
|
-
if host.is_powershell?
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
95
|
+
block_on hosts do |host|
|
96
|
+
platform = if host.is_powershell?
|
97
|
+
'pswindows'
|
98
|
+
elsif host['platform'] =~ /windows/
|
99
|
+
'windows'
|
100
|
+
else
|
101
|
+
'unix'
|
102
|
+
end
|
104
103
|
remove_platform_aio_defaults(host, platform)
|
105
104
|
end
|
106
105
|
end
|
107
|
-
|
108
|
-
|
109
106
|
end
|
110
107
|
end
|
111
108
|
end
|
112
109
|
end
|
113
|
-
|
@@ -7,29 +7,28 @@ module Beaker
|
|
7
7
|
# that use ezbake for packaging.
|
8
8
|
#
|
9
9
|
module EZBakeUtils
|
10
|
-
|
11
10
|
# @!group Public DSL Methods
|
12
11
|
|
13
12
|
# Installs leiningen project with given name and version on remote host.
|
14
13
|
#
|
15
14
|
# @param [Host] host A single remote host on which to install the
|
16
15
|
# specified leiningen project.
|
17
|
-
def install_from_ezbake
|
16
|
+
def install_from_ezbake(host)
|
18
17
|
ezbake_validate_support host
|
19
18
|
ezbake_tools_available?
|
20
19
|
install_ezbake_tarball_on_host host
|
21
|
-
ezbake_installsh host,
|
20
|
+
ezbake_installsh host, 'service'
|
22
21
|
end
|
23
22
|
|
24
23
|
# Installs termini with given name and version on remote host.
|
25
24
|
#
|
26
25
|
# @param [Host] host A single remote host on which to install the
|
27
26
|
# specified leiningen project.
|
28
|
-
def install_termini_from_ezbake
|
27
|
+
def install_termini_from_ezbake(host)
|
29
28
|
ezbake_validate_support host
|
30
29
|
ezbake_tools_available?
|
31
30
|
install_ezbake_tarball_on_host host
|
32
|
-
ezbake_installsh host,
|
31
|
+
ezbake_installsh host, 'termini'
|
33
32
|
end
|
34
33
|
|
35
34
|
# Install a development version of ezbake into the local m2 repository
|
@@ -42,15 +41,15 @@ module Beaker
|
|
42
41
|
#
|
43
42
|
# @param url [String] git url
|
44
43
|
# @param branch [String] git branch
|
45
|
-
def ezbake_dev_build
|
46
|
-
branch =
|
44
|
+
def ezbake_dev_build(url = 'git@github.com:puppetlabs/ezbake.git',
|
45
|
+
branch = 'master')
|
47
46
|
ezbake_dir = 'tmp/ezbake'
|
48
47
|
conditionally_clone url, ezbake_dir, branch
|
49
48
|
lp = ezbake_lein_prefix
|
50
49
|
|
51
50
|
Dir.chdir(ezbake_dir) do
|
52
51
|
ezbake_local_cmd "#{lp} install",
|
53
|
-
|
52
|
+
throw_on_failure: true
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
@@ -67,25 +66,22 @@ module Beaker
|
|
67
66
|
# @param [Host] host host to check for support
|
68
67
|
# @raise [RuntimeError] if OS is not supported
|
69
68
|
# @api private
|
70
|
-
def ezbake_validate_support
|
71
|
-
variant, version,
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
69
|
+
def ezbake_validate_support(host)
|
70
|
+
variant, version, = host['platform'].to_array
|
71
|
+
return if variant =~ /^(fedora|el|redhat|centos|debian|ubuntu)$/
|
72
|
+
|
73
|
+
raise "No support for #{variant} within ezbake_utils ..."
|
76
74
|
end
|
77
75
|
|
78
76
|
# Build, copy & unpack tarball on remote host
|
79
77
|
#
|
80
78
|
# @param [Host] host installation destination
|
81
79
|
# @api private
|
82
|
-
def install_ezbake_tarball_on_host
|
83
|
-
|
84
|
-
ezbake_stage
|
85
|
-
end
|
80
|
+
def install_ezbake_tarball_on_host(host)
|
81
|
+
ezbake_stage unless ezbake_config
|
86
82
|
|
87
83
|
# Skip installation if the remote directory exists
|
88
|
-
result = on host, "test -d #{ezbake_install_dir}", :
|
84
|
+
result = on host, "test -d #{ezbake_install_dir}", acceptable_exit_codes: [0, 1]
|
89
85
|
return if result.exit_code == 0
|
90
86
|
|
91
87
|
ezbake_staging_dir = File.join('target', 'staging')
|
@@ -93,12 +89,12 @@ module Beaker
|
|
93
89
|
ezbake_local_cmd 'rake package:tar'
|
94
90
|
end
|
95
91
|
|
96
|
-
local_tarball = ezbake_staging_dir +
|
97
|
-
remote_tarball = ezbake_install_dir +
|
92
|
+
local_tarball = ezbake_staging_dir + '/pkg/' + ezbake_install_name + '.tar.gz'
|
93
|
+
remote_tarball = ezbake_install_dir + '.tar.gz'
|
98
94
|
scp_to host, local_tarball, remote_tarball
|
99
95
|
|
100
96
|
# untar tarball on host
|
101
|
-
on host,
|
97
|
+
on host, 'tar -xzf ' + remote_tarball
|
102
98
|
|
103
99
|
# Check to ensure directory exists
|
104
100
|
on host, "test -d #{ezbake_install_dir}"
|
@@ -107,7 +103,7 @@ module Beaker
|
|
107
103
|
LOCAL_COMMANDS_REQUIRED = [
|
108
104
|
['leiningen', 'lein --version', nil],
|
109
105
|
['lein-pprint', 'lein with-profile ci pprint :version',
|
110
|
-
|
106
|
+
'Must have lein-pprint installed under the :ci profile.',],
|
111
107
|
['java', 'java -version', nil],
|
112
108
|
['git', 'git --version', nil],
|
113
109
|
['rake', 'rake --version', nil],
|
@@ -120,13 +116,11 @@ module Beaker
|
|
120
116
|
# @api private
|
121
117
|
def ezbake_tools_available?
|
122
118
|
LOCAL_COMMANDS_REQUIRED.each do |software_name, command, additional_error_message|
|
123
|
-
if
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
raise RuntimeError, error_message
|
129
|
-
end
|
119
|
+
next if system command
|
120
|
+
|
121
|
+
error_message = "Must have #{software_name} installed on development system.\n"
|
122
|
+
error_message += additional_error_message if additional_error_message
|
123
|
+
raise error_message
|
130
124
|
end
|
131
125
|
end
|
132
126
|
|
@@ -156,14 +150,14 @@ module Beaker
|
|
156
150
|
def ezbake_stage
|
157
151
|
# Install the PuppetDB jar into the local repository
|
158
152
|
ezbake_local_cmd "#{ezbake_lein_prefix} install",
|
159
|
-
:
|
153
|
+
throw_on_failure: true
|
160
154
|
|
161
155
|
# Run ezbake stage
|
162
156
|
ezbake_local_cmd "#{ezbake_lein_prefix} with-profile ezbake ezbake stage",
|
163
|
-
:
|
157
|
+
throw_on_failure: true
|
164
158
|
|
165
159
|
# Boostrap packaging, and grab configuration info from project
|
166
|
-
staging_dir = File.join('target','staging')
|
160
|
+
staging_dir = File.join('target', 'staging')
|
167
161
|
Dir.chdir(staging_dir) do
|
168
162
|
ezbake_local_cmd 'rake package:bootstrap'
|
169
163
|
|
@@ -184,16 +178,15 @@ module Beaker
|
|
184
178
|
# @raise [RuntimeError] if :throw_on_failure is true and
|
185
179
|
# command fails
|
186
180
|
# @api private
|
187
|
-
def ezbake_local_cmd
|
181
|
+
def ezbake_local_cmd(cmd, opts = {})
|
188
182
|
opts = {
|
189
|
-
:
|
183
|
+
throw_on_failure: false,
|
190
184
|
}.merge(opts)
|
191
185
|
|
192
186
|
logger.notify "localhost $ #{cmd}"
|
193
187
|
result = system cmd
|
194
|
-
if opts[:throw_on_failure] && result == false
|
195
|
-
|
196
|
-
end
|
188
|
+
raise "Command failure #{cmd}" if opts[:throw_on_failure] && result == false
|
189
|
+
|
197
190
|
result
|
198
191
|
end
|
199
192
|
|
@@ -207,7 +200,7 @@ module Beaker
|
|
207
200
|
ezbake = ezbake_config
|
208
201
|
project_package_version = ezbake[:package_version]
|
209
202
|
project_name = ezbake[:project]
|
210
|
-
|
203
|
+
format('%s-%s', project_name, project_package_version)
|
211
204
|
end
|
212
205
|
|
213
206
|
# Returns the full path to the installed software on the remote host.
|
@@ -227,7 +220,7 @@ module Beaker
|
|
227
220
|
# @param [Host] host Host to run install.sh on
|
228
221
|
# @param [String] task Task to execute with install.sh
|
229
222
|
# @api private
|
230
|
-
def ezbake_installsh
|
223
|
+
def ezbake_installsh(host, task = '')
|
231
224
|
on host, "cd #{ezbake_install_dir}; bash install.sh #{task}"
|
232
225
|
end
|
233
226
|
|
@@ -238,7 +231,7 @@ module Beaker
|
|
238
231
|
# @param [String] local_path path to conditionally install to
|
239
232
|
# @param [String] branch to checkout
|
240
233
|
# @api private
|
241
|
-
def conditionally_clone
|
234
|
+
def conditionally_clone(upstream_uri, local_path, branch = 'origin/HEAD')
|
242
235
|
if ezbake_local_cmd "git --work-tree=#{local_path} --git-dir=#{local_path}/.git status"
|
243
236
|
ezbake_local_cmd "git --work-tree=#{local_path} --git-dir=#{local_path}/.git fetch origin"
|
244
237
|
ezbake_local_cmd "git --work-tree=#{local_path} --git-dir=#{local_path}/.git checkout #{branch}"
|
@@ -249,7 +242,6 @@ module Beaker
|
|
249
242
|
ezbake_local_cmd "git --work-tree=#{local_path} --git-dir=#{local_path}/.git checkout #{branch}"
|
250
243
|
end
|
251
244
|
end
|
252
|
-
|
253
245
|
end
|
254
246
|
end
|
255
247
|
end
|