cucumber-chef 2.0.4 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,12 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - ree-1.8.7
5
- - 1.8.7
6
4
  - 1.9.2
7
5
  - 1.9.3
8
6
 
9
7
  branches:
10
8
  only:
11
9
  - master
12
- - 2.0.0.rc1
13
10
 
14
11
  notifications:
15
12
  irc:
data/TODO.md CHANGED
@@ -5,21 +5,21 @@
5
5
  * Various OS baseboxes
6
6
  * Make it easy to use OSC server
7
7
  * Provide a libvirt capability
8
- * Ship a release candidate
9
8
  * Write test for cucumber-chef ssh
10
9
  * Write tests for the stuff pulled from cucumber-nagios
11
10
  * Make recipes food-critic compliant
12
11
  * Fork ubuntu ami to 'amy'
13
- * Merge Zach's changes
14
12
  * Get cukes passing
15
13
  * Improve cuke coverage to include all functionality described in the help task
16
14
  * Plug into Relish
17
- * Refactor ssh and connect into one ssh task that takes an optional container
18
15
 
19
16
  ## DONE
20
17
 
21
18
  * RC branch
22
19
  * Go through the bugs
20
+ * Merge Zach's changes
21
+ * Ship a release candidate
22
+ * Refactor ssh and connect into one ssh task that takes an optional container
23
23
 
24
24
  ## IDEAS
25
25
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'cucumber-chef'
3
3
 
4
- $logger = Cucumber::Chef::Logger.new
5
- $logger.level = (Cucumber::Chef.is_rc? ? Cucumber::Chef::Logger::DEBUG : Cucumber::Chef::Logger::INFO)
4
+ $logger = ZTK::Logger.new(Cucumber::Chef.log_file)
5
+ Cucumber::Chef.is_rc? and ($logger.level = ZTK::Logger::DEBUG)
6
6
 
7
7
  # if we have bundler binstubs use that; otherwise attempt to detect
8
8
  knife = (Cucumber::Chef.locate(:file, "bin", "knife") rescue nil)
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ require 'cucumber-chef'
3
+
4
+ def run(command)
5
+ puts "Executing: '#{command}'"
6
+ @ssh.exec(command)
7
+ end
8
+
9
+ $logger = ZTK::Logger.new(Cucumber::Chef.log_file)
10
+ Cucumber::Chef.is_rc? and ($logger.level = ZTK::Logger::DEBUG)
11
+
12
+ message = "cc-push v#{Cucumber::Chef::VERSION}"
13
+ puts(message)
14
+ $logger.info { message }
15
+
16
+ Cucumber::Chef::Config.load
17
+ if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
18
+ @ssh = ZTK::SSH.new
19
+ @ssh.config.host_name = test_lab.labs_running.first.public_ip_address
20
+ @ssh.config.user = "ubuntu"
21
+ @ssh.config.keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{@ssh.config.user}")
22
+
23
+ gem_name = "cucumber-chef-#{Cucumber::Chef::VERSION}.gem"
24
+
25
+ puts %x(cd #{Cucumber::Chef.root} && gem build cucumber-chef.gemspec -V)
26
+
27
+ local_file = File.join(Cucumber::Chef.root, gem_name)
28
+ remote_file = File.join("/", "home", @ssh.config.user, gem_name)
29
+ puts("#{local_file} -> #{@ssh.config.user}@#{test_lab.labs_running.first.public_ip_address}:#{remote_file}")
30
+ @ssh.upload(local_file, remote_file)
31
+ FileUtils.rm_f(File.join(Cucumber::Chef.root, "*.gem"))
32
+
33
+ run("cd #{File.dirname(remote_file)}; ls -la | grep 'cucumber-chef-'; sudo gem uninstall cucumber-chef -a -I -x -V; rm -f /usr/lib/ruby/gems/1.8/cache/#{gem_name}; sudo gem install #{gem_name} -l -V; rm -f *.gem")
34
+
35
+ else
36
+ puts("No running cucumber-chef test labs to connect to!")
37
+ exit(1)
38
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if RUBY_VERSION < "1.9"
4
+ exit if fork
5
+ Process.setsid
6
+ exit if fork
7
+ Dir.chdir "/"
8
+ STDIN.reopen "/dev/null"
9
+ STDOUT.reopen "/dev/null", "a"
10
+ STDERR.reopen "/dev/null", "a"
11
+ else
12
+ Process.daemon
13
+ end
14
+
15
+ Dir.chdir(File.join("/home", "ubuntu"))
16
+
17
+ require 'drb/drb'
18
+ require 'drb/acl'
19
+ require 'cucumber-chef'
20
+ require 'cucumber/chef/helpers'
21
+
22
+ class FrontObject
23
+ attr_accessor :servers
24
+
25
+ include Cucumber::Chef
26
+ include Cucumber::Chef::Helpers
27
+
28
+ def initialize
29
+ knife_rb = Cucumber::Chef.locate(:file, ".chef", "knife.rb")
30
+ Chef::Config.from_file(knife_rb)
31
+ end
32
+
33
+ def shutdown
34
+ DRb.stop_service
35
+ end
36
+
37
+ end
38
+
39
+ hostname = %x( hostname -f ).chomp
40
+
41
+ list = %w( deny all allow 127.0.0.1 )
42
+ ARGV[0] and (list += [ 'allow', ARGV[0] ])
43
+ acl = ACL.new(list)
44
+
45
+ # This will break everything:
46
+ # $SAFE = 1
47
+
48
+ DRb.start_service("druby://:8787", FrontObject.new)
49
+ DRb.thread.join
@@ -57,8 +57,8 @@ class CucumberChef < Thor
57
57
  exit(255)
58
58
  end
59
59
 
60
- $logger = Cucumber::Chef::Logger.new
61
- Cucumber::Chef.is_rc? and ($logger.level = Cucumber::Chef::Logger::DEBUG)
60
+ $logger = ZTK::Logger.new(Cucumber::Chef.log_file)
61
+ Cucumber::Chef.is_rc? and ($logger.level = ZTK::Logger::DEBUG)
62
62
 
63
63
  message = "cucumber-chef v#{Cucumber::Chef::VERSION}"
64
64
  puts(set_color(message, :green, true))
@@ -259,26 +259,25 @@ class CucumberChef < Thor
259
259
 
260
260
  puts
261
261
  if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
262
- ssh = Cucumber::Chef::SSH.new
262
+ ssh = ZTK::SSH.new
263
263
 
264
264
  if args.size == 0
265
- ssh.config[:host] = test_lab.labs_running.first.public_ip_address
266
- ssh.config[:ssh_user] = "ubuntu"
267
- ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:ssh_user]}")
265
+ ssh.config.host_name = test_lab.labs_running.first.public_ip_address
266
+ ssh.config.user = "ubuntu"
267
+ ssh.config.keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.user}")
268
268
 
269
269
  puts([set_color("Attempting SSH connection to cucumber-chef '", :blue, true), set_color("test lab", :cyan, true), set_color("'...", :blue, true)].join)
270
270
  ssh.console
271
271
  else
272
272
  container = args[0]
273
273
 
274
- ssh.config[:proxy] = true
275
- ssh.config[:proxy_host] = test_lab.labs_running.first.public_ip_address
276
- ssh.config[:proxy_ssh_user] = "ubuntu"
277
- ssh.config[:proxy_identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
274
+ ssh.config.proxy_host_name = test_lab.labs_running.first.public_ip_address
275
+ ssh.config.proxy_user = "ubuntu"
276
+ ssh.config.proxy_keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.proxy_user}")
278
277
 
279
- ssh.config[:host] = container
280
- ssh.config[:ssh_user] = "root"
281
- ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
278
+ ssh.config.host_name = container
279
+ ssh.config.user = "root"
280
+ ssh.config.keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.proxy_user}")
282
281
 
283
282
  puts([set_color("Attempting SSH connection to cucumber-chef container '", :blue, true), set_color(container, :cyan, true), set_color("'...", :blue, true)].join)
284
283
  ssh.console
@@ -305,16 +304,15 @@ class CucumberChef < Thor
305
304
 
306
305
  puts
307
306
  if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
308
- ssh = Cucumber::Chef::SSH.new
307
+ ssh = ZTK::SSH.new
309
308
 
310
- ssh.config[:proxy] = true
311
- ssh.config[:proxy_host] = test_lab.labs_running.first.public_ip_address
312
- ssh.config[:proxy_ssh_user] = "ubuntu"
313
- ssh.config[:proxy_identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
309
+ ssh.config.proxy_host_name = test_lab.labs_running.first.public_ip_address
310
+ ssh.config.proxy_user = "ubuntu"
311
+ ssh.config.proxy_keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.proxy_user}")
314
312
 
315
- ssh.config[:host] = container
316
- ssh.config[:ssh_user] = "root"
317
- ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
313
+ ssh.config.host_name = container
314
+ ssh.config.user = "root"
315
+ ssh.config.keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.proxy_user}")
318
316
 
319
317
  puts([set_color("Attempting to collect diagnostic information on cucumber-chef container '", :blue, true), set_color(container, :cyan, true), set_color("'...", :blue, true)].join)
320
318
  if @options.strace?
@@ -362,11 +360,11 @@ class CucumberChef < Thor
362
360
 
363
361
  puts
364
362
  if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
365
- ssh = Cucumber::Chef::SSH.new
363
+ ssh = ZTK::SSH.new
366
364
 
367
- ssh.config[:host] = test_lab.labs_running.first.public_ip_address
368
- ssh.config[:ssh_user] = "ubuntu"
369
- ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:ssh_user]}")
365
+ ssh.config.host_name = test_lab.labs_running.first.public_ip_address
366
+ ssh.config.user = "ubuntu"
367
+ ssh.config.keys = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config.user}")
370
368
 
371
369
  puts(set_color("Getting container processes from cucumber-chef test lab...", :blue, true))
372
370
  puts
@@ -420,32 +418,6 @@ class CucumberChef < Thor
420
418
  fatal(e)
421
419
  end
422
420
 
423
- ################################################################################
424
-
425
- desc "test [cucumber-options]", "Test a project using the cucumber-chef test suite."
426
- method_option :destroy, :type => :boolean, :desc => "destroy all containers before test run", :aliases => "-z", :default => false
427
- def test(*args)
428
- load_config
429
-
430
- puts
431
- root_path = Cucumber::Chef.locate_parent(".cucumber-chef")
432
- features_path = File.expand_path(File.join(root_path, "features"))
433
-
434
- unless (File.exists?(features_path) && File.directory?(features_path))
435
- raise Error, "Features directory '#{features_path}' does not exist."
436
- else
437
- puts("Using features directory: #{features_path}")
438
- end
439
-
440
- runner = Cucumber::Chef::TestRunner.new(features_path)
441
- runner.run(@options.destroy?, args)
442
- puts
443
-
444
- rescue Cucumber::Chef::Error => e
445
- $logger.fatal { e.backtrace.join("\n") }
446
- fatal(e)
447
- end
448
-
449
421
  ################################################################################
450
422
 
451
423
  end
@@ -36,18 +36,18 @@ Gem::Specification.new do |s|
36
36
 
37
37
  s.add_dependency("chef", ">= 0.10.10")
38
38
  s.add_dependency("cucumber", ">= 1.2.0")
39
- s.add_dependency("erubis", ">= 2.7.0")
40
39
  s.add_dependency("fog", ">= 1.3.1")
41
- s.add_dependency("net-sftp", ">= 2.0.5")
42
- s.add_dependency("net-ssh", ">= 2.2.2")
43
40
  s.add_dependency("mixlib-config", ">= 1.1.2")
44
41
  s.add_dependency("thor", ">= 0.15.2")
45
42
  s.add_dependency("rake", ">= 0.9.2")
46
43
  s.add_dependency("ubuntu_ami", ">= 0.4.0")
44
+ s.add_dependency("rspec", ">= 2.10.0")
45
+ s.add_dependency("ztk")
47
46
 
48
- s.add_development_dependency("rspec", ">= 2.10.0")
49
47
  s.add_development_dependency("simplecov", ">= 0.6.4")
50
48
  s.add_development_dependency("pry")
49
+ s.add_development_dependency("yard")
50
+ s.add_development_dependency("redcarpet")
51
51
 
52
52
  s.files = `git ls-files`.split("\n")
53
53
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -27,14 +27,11 @@ require 'stringio'
27
27
 
28
28
  require 'chef'
29
29
  require 'chef/cookbook_uploader'
30
- require 'erubis'
31
30
  require 'fog'
32
31
  require 'json'
33
32
  require 'mixlib/config'
34
- require 'net/ssh'
35
- require 'net/ssh/proxy/command'
36
- require 'net/sftp'
37
33
  require 'ubuntu_ami'
34
+ require 'ztk'
38
35
 
39
36
  ################################################################################
40
37
 
@@ -46,16 +43,9 @@ module Cucumber
46
43
  class Error < StandardError; end
47
44
 
48
45
  autoload :Bootstrap, 'cucumber/chef/bootstrap'
49
- autoload :Bootstrap, 'cucumber/chef/bootstrap'
50
- autoload :Command, 'cucumber/chef/command'
51
46
  autoload :Config, 'cucumber/chef/config'
52
- autoload :Logger, 'cucumber/chef/logger'
53
47
  autoload :Provisioner, 'cucumber/chef/provisioner'
54
- autoload :SSH, 'cucumber/chef/ssh'
55
- autoload :TCPSocket, 'cucumber/chef/tcp_socket'
56
- autoload :Template, 'cucumber/chef/template'
57
48
  autoload :TestLab, 'cucumber/chef/test_lab'
58
- autoload :TestRunner, 'cucumber/chef/test_runner'
59
49
 
60
50
  require 'cucumber/chef/utility'
61
51
  extend(Cucumber::Chef::Utility)
@@ -33,7 +33,7 @@ module Cucumber
33
33
  @stdout, @stderr, @stdin = stdout, stderr, stdin
34
34
  @stdout.sync = true if @stdout.respond_to?(:sync=)
35
35
 
36
- @ssh = Cucumber::Chef::SSH.new(@stdout, @stderr, @stdin)
36
+ @ssh = ZTK::SSH.new(:stdout => @stdout, :stderr => @stderr, :stdin => @stdin)
37
37
  @config = Hash.new(nil)
38
38
  @config[:context] = Hash.new(nil)
39
39
  end
@@ -69,14 +69,14 @@ module Cucumber
69
69
 
70
70
  $logger.debug { "prepare(#{@config[:host]})" }
71
71
 
72
- @ssh.config[:host] = @config[:host]
73
- @ssh.config[:ssh_user] = @config[:ssh_user]
74
- @ssh.config[:ssh_password] = @config[:ssh_password]
75
- @ssh.config[:identity_file] = @config[:identity_file]
76
- @ssh.config[:timeout] = 5
72
+ @ssh.config.host_name = @config[:host]
73
+ @ssh.config.user = @config[:ssh_user]
74
+ @ssh.config.password = @config[:ssh_password]
75
+ @ssh.config.keys = @config[:identity_file]
76
+ @ssh.config.timeout = 5
77
77
 
78
78
  $logger.debug { "template_file(#{@config[:template_file]})" }
79
- command = Cucumber::Chef::Template.render(@config[:template_file], @config[:context])
79
+ command = ZTK::Template.render(@config[:template_file], @config[:context])
80
80
  command = "sudo #{command}" if @config[:use_sudo]
81
81
 
82
82
  $logger.debug { "begin(#{@config[:host]})" }
@@ -22,10 +22,10 @@
22
22
  module Cucumber
23
23
  module Chef
24
24
 
25
+ # This module includes all of the helper methods meant to drive step
26
+ # definitions.
25
27
  module Helpers
26
28
 
27
- ################################################################################
28
-
29
29
  require 'cucumber/chef/helpers/chef_client'
30
30
  require 'cucumber/chef/helpers/chef_server'
31
31
  require 'cucumber/chef/helpers/command'
@@ -34,8 +34,6 @@ module Cucumber
34
34
  require 'cucumber/chef/helpers/test_lab'
35
35
  require 'cucumber/chef/helpers/utility'
36
36
 
37
- ################################################################################
38
-
39
37
  def self.included(base)
40
38
  base.send(:include, Cucumber::Chef::Helpers::ChefClient)
41
39
  base.send(:include, Cucumber::Chef::Helpers::ChefServer)
@@ -46,11 +44,7 @@ module Cucumber
46
44
  base.send(:include, Cucumber::Chef::Helpers::Utility)
47
45
  end
48
46
 
49
- ################################################################################
50
-
51
47
  end
52
48
 
53
49
  end
54
50
  end
55
-
56
- ################################################################################
@@ -43,6 +43,7 @@ module Cucumber::Chef::Helpers::ChefServer
43
43
  end
44
44
  cookbook_repo = ::Chef::CookbookLoader.new(cookbook_path)
45
45
  cookbook_repo.each do |name, cbook|
46
+ next if name != cookbook
46
47
  ::Chef::CookbookUploader.new(cbook, cookbook_path, :force => true).upload_cookbooks
47
48
  log("chef-server", "uploaded cookbook '#{cookbook}' from path '#{cookbook_path}'")
48
49
  end
@@ -25,6 +25,9 @@ module Cucumber::Chef::Helpers::Container
25
25
 
26
26
  def container_create(name, distro, release, arch)
27
27
  unless container_exists?(name)
28
+ chef_server_node_destroy(name)
29
+ chef_server_client_destroy(name)
30
+
28
31
  cache_rootfs = container_cache_root(name, distro, release, arch)
29
32
  log(name, "has triggered first time lxc distro cache build; this will take a while") if !File.exists?(cache_rootfs)
30
33
 
@@ -34,9 +37,9 @@ module Cucumber::Chef::Helpers::Container
34
37
  omnibus_chef_client = File.join("/", "opt", "opscode", "bin", "chef-client")
35
38
  if !File.exists?(File.join(cache_rootfs, omnibus_chef_client))
36
39
  case distro.downcase
37
- when "ubuntu":
38
- %x(chroot #{cache_rootfs} /bin/bash -c 'apt-get -y --force-yes install wget' 2>&1)
39
- when "fedora":
40
+ when "ubuntu" then
41
+ %x( chroot #{cache_rootfs} /bin/bash -c 'apt-get -y --force-yes install wget' 2>&1 )
42
+ when "fedora" then
40
43
  %x( yum --nogpgcheck --installroot=#{cache_rootfs} -y install wget openssh-server )
41
44
  end
42
45
  %x( chroot #{cache_rootfs} /bin/bash -c 'wget http://opscode.com/chef/install.sh -O - | bash' 2>&1 )
@@ -110,7 +113,7 @@ module Cucumber::Chef::Helpers::Container
110
113
  f.puts("lxc.network.flags = up")
111
114
  f.puts("lxc.network.link = br0")
112
115
  f.puts("lxc.network.name = eth0")
113
- f.puts("lxc.network.hwaddr = #{$servers[name][:mac]}")
116
+ f.puts("lxc.network.hwaddr = #{@servers[name][:mac]}")
114
117
  f.puts("lxc.network.ipv4 = 0.0.0.0")
115
118
  end
116
119
  end
@@ -131,18 +134,18 @@ module Cucumber::Chef::Helpers::Container
131
134
 
132
135
  def container_cache_root(name, distro, release, arch)
133
136
  case distro.downcase
134
- when "ubuntu":
137
+ when "ubuntu" then
135
138
  cache_root = File.join("/", "var", "cache", "lxc", release, "rootfs-#{arch}")
136
- when "fedora":
139
+ when "fedora" then
137
140
  cache_root = File.join("/", "var", "cache", "lxc", distro, arch, release, "rootfs")
138
141
  end
139
142
  end
140
143
 
141
144
  def container_create_command(name, distro, release, arch)
142
145
  case distro.downcase
143
- when "ubuntu":
146
+ when "ubuntu" then
144
147
  "lxc-create -n #{name} -f /etc/lxc/#{name} -t #{distro} -- --release #{release} --arch #{arch}"
145
- when "fedora":
148
+ when "fedora" then
146
149
  "lxc-create -n #{name} -f /etc/lxc/#{name} -t #{distro} -- --release #{release}"
147
150
  end
148
151
  end
@@ -25,16 +25,16 @@ module Cucumber::Chef::Helpers::Server
25
25
 
26
26
  def detect_arch(distro)
27
27
  case distro.downcase
28
- when "ubuntu":
28
+ when "ubuntu" then
29
29
  ((RUBY_PLATFORM =~ /x86_64/) ? "amd64" : "i386")
30
- when "fedora":
30
+ when "fedora" then
31
31
  ((RUBY_PLATFORM =~ /x86_64/) ? "amd64" : "i686")
32
32
  end
33
33
  end
34
34
 
35
35
  def server_create(name, attributes={})
36
- if ((attributes[:persist] && $servers[name]) || ($servers[name] && $servers[name][:persist]))
37
- attributes = $servers[name]
36
+ if ((attributes[:persist] && @servers[name]) || (@servers[name] && @servers[name][:persist]))
37
+ attributes = @servers[name]
38
38
  else
39
39
  server_destroy(name) if (container_exists?(name) && (ENV['DESTROY'] == "1"))
40
40
  attributes = { :ip => generate_ip,
@@ -44,20 +44,20 @@ module Cucumber::Chef::Helpers::Server
44
44
  :release => "lucid",
45
45
  :arch => detect_arch(attributes[:distro] || "ubuntu") }.merge(attributes)
46
46
  end
47
- $servers = ($servers || Hash.new(nil)).merge(name => attributes)
48
- $current_server = $servers[name][:ip]
47
+ @servers = (@servers || Hash.new(nil)).merge(name => attributes)
48
+ $current_server = @servers[name][:ip]
49
49
  if !server_running?(name)
50
- log(name, "is being provisioned") if $servers[name]
50
+ log(name, "is being provisioned") if @servers[name]
51
51
 
52
52
  test_lab_config_dhcpd
53
53
  container_config_network(name)
54
- container_create(name, $servers[name][:distro], $servers[name][:release], $servers[name][:arch])
55
- Cucumber::Chef::TCPSocket.new($servers[name][:ip], 22).wait
54
+ container_create(name, @servers[name][:distro], @servers[name][:release], @servers[name][:arch])
55
+ ZTK::TCPSocketCheck.new(:host => @servers[name][:ip], :port => 22).wait
56
56
  end
57
57
  end
58
58
 
59
59
  def server_destroy(name)
60
- log(name, "is being destroyed") if $servers[name]
60
+ log(name, "is being destroyed") if @servers[name]
61
61
 
62
62
  container_destroy(name)
63
63
  end