vmit 0.0.3 → 0.0.3.99
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/vmit +89 -13
- data/lib/vmit.rb +25 -18
- data/lib/vmit/autoyast.rb +81 -22
- data/lib/vmit/debian_preseed.rb +35 -1
- data/lib/vmit/install_media.rb +224 -0
- data/lib/vmit/kickstart.rb +44 -8
- data/lib/vmit/libvirt_vm.rb +274 -0
- data/lib/vmit/plugins/bootstrap.rb +33 -17
- data/lib/vmit/registry.rb +208 -0
- data/lib/vmit/unattended_install.rb +43 -0
- data/lib/vmit/utils.rb +51 -1
- data/lib/vmit/version.rb +1 -1
- data/lib/vmit/workspace.rb +181 -0
- data/test/data/registry.yml +5 -0
- data/test/data/registry/key1 +1 -0
- data/test/data/registry/key2 +1 -0
- data/test/data/registry/key4/key1 +1 -0
- data/test/install_media_test.rb +67 -0
- data/test/registry_test.rb +118 -0
- data/test/{bootstrap_test.rb → workspace_test.rb} +23 -19
- data/vmit.gemspec +4 -1
- metadata +46 -19
- data/bin/vmit-ifdown +0 -10
- data/bin/vmit-ifup +0 -18
- data/lib/vmit/bootstrap.rb +0 -273
- data/lib/vmit/virtual_machine.rb +0 -299
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require 'erb'
|
22
|
+
require 'abstract_method'
|
23
|
+
require 'confstruct'
|
24
|
+
|
25
|
+
module Vmit
|
26
|
+
|
27
|
+
class UnattendedInstall
|
28
|
+
|
29
|
+
attr_accessor :config
|
30
|
+
attr_accessor :location
|
31
|
+
|
32
|
+
#abstract_method :execute_autoinstall
|
33
|
+
|
34
|
+
def initialize(location)
|
35
|
+
@config = Confstruct::Configuration.new({
|
36
|
+
:packages => []
|
37
|
+
})
|
38
|
+
@location = location
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/vmit/utils.rb
CHANGED
@@ -19,7 +19,6 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
require 'rubygems'
|
22
|
-
require 'open4'
|
23
22
|
require 'progressbar'
|
24
23
|
require 'digest/sha1'
|
25
24
|
|
@@ -46,6 +45,57 @@ module Vmit
|
|
46
45
|
("%02x"%((rand 64).to_i*4|2))+(0..4).inject(""){|s,x|s+":%02x"%(rand 256).to_i}
|
47
46
|
end
|
48
47
|
|
48
|
+
def self.arch
|
49
|
+
Cheetah.run('arch', :stdout => :capture).strip
|
50
|
+
end
|
51
|
+
|
52
|
+
# @returns [Boolean] wether the port is open
|
53
|
+
# @note uses nmap
|
54
|
+
def self.port_open?(host, port)
|
55
|
+
begin
|
56
|
+
# use logger => nil until a sane way of handling
|
57
|
+
# non zero return codes is implemented in cheetah
|
58
|
+
# https://github.com/openSUSE/cheetah/pull/19
|
59
|
+
Cheetah.run(['nmap', host, '-p',
|
60
|
+
port.to_s, '-sV', '--version-all', '-oG', '-'],
|
61
|
+
['grep', '-iq', "#{port}/open"], :logger => nil)
|
62
|
+
true
|
63
|
+
rescue Cheetah::ExecutionFailed
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Waits unntil that host port is open
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# Vmit::Utils.wait_for_port('192.168.0.1', 22) do
|
72
|
+
# # do something
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
#
|
76
|
+
def self.wait_for_port(host, port, &block)
|
77
|
+
chars = %w{ | / - \\ }
|
78
|
+
if block
|
79
|
+
thread = Thread.new(&block)
|
80
|
+
thread.abort_on_exception = true
|
81
|
+
end
|
82
|
+
|
83
|
+
Vmit.logger.info "Waiting for machine port #{port}..."
|
84
|
+
while true
|
85
|
+
print chars[0]
|
86
|
+
|
87
|
+
if port_open?(host, port)
|
88
|
+
Thread.kill(thread) if thread
|
89
|
+
break
|
90
|
+
end
|
91
|
+
break if thread && !thread.alive?
|
92
|
+
sleep(1)
|
93
|
+
print "\b"
|
94
|
+
chars.push chars.shift
|
95
|
+
end
|
96
|
+
puts
|
97
|
+
end
|
98
|
+
|
49
99
|
def self.uname(bzimage)
|
50
100
|
offset = 0
|
51
101
|
File.open(bzimage) do |f|
|
data/lib/vmit/version.rb
CHANGED
@@ -0,0 +1,181 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require 'cheetah'
|
22
|
+
require 'drb'
|
23
|
+
require 'fileutils'
|
24
|
+
require 'stringio'
|
25
|
+
require 'yaml'
|
26
|
+
require 'confstruct'
|
27
|
+
# confstruct uses autoload, Deferred is
|
28
|
+
# not defined until you use Configuration
|
29
|
+
require 'confstruct/configuration'
|
30
|
+
|
31
|
+
require 'vmit/utils'
|
32
|
+
|
33
|
+
module Vmit
|
34
|
+
|
35
|
+
class Workspace
|
36
|
+
|
37
|
+
attr_accessor :work_dir
|
38
|
+
|
39
|
+
VM_GLOBAL_DEFAULTS = {
|
40
|
+
:memory => '1G',
|
41
|
+
:kernel_cmdline => [],
|
42
|
+
:virtio => true
|
43
|
+
}
|
44
|
+
SWITCH = 'br0'
|
45
|
+
|
46
|
+
def self.from_pwd
|
47
|
+
Vmit::Workspace.new(File.expand_path(Dir.pwd))
|
48
|
+
end
|
49
|
+
|
50
|
+
def name
|
51
|
+
work_dir.downcase.gsub(/[^a-z\s]/, '_')
|
52
|
+
end
|
53
|
+
|
54
|
+
# Accessor to current options
|
55
|
+
def [](key)
|
56
|
+
@config[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
def config_file
|
60
|
+
File.join(work_dir, 'config.yml')
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(work_dir)
|
64
|
+
@config = Confstruct::Configuration.new(VM_GLOBAL_DEFAULTS.merge({
|
65
|
+
:uuid => File.read("/proc/sys/kernel/random/uuid").strip,
|
66
|
+
:mac_address => Vmit::Utils.random_mac_address
|
67
|
+
}))
|
68
|
+
@work_dir = work_dir
|
69
|
+
|
70
|
+
if File.exist?(config_file)
|
71
|
+
@config.configure(YAML::load(File.open(config_file)))
|
72
|
+
end
|
73
|
+
|
74
|
+
@network = config.lookup!('network', 'default')
|
75
|
+
Vmit.logger.info "Network: #{@network}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Array,<String>] sorted list of snapshots
|
79
|
+
def disk_images
|
80
|
+
Dir.glob(File.join(work_dir, '*.qcow2')).sort do |a,b|
|
81
|
+
File.ctime(a) <=> File.ctime(b)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Takes a disk snapshot
|
86
|
+
def disk_snapshot!
|
87
|
+
disk_image_shift!
|
88
|
+
end
|
89
|
+
|
90
|
+
def disk_image_init!(opts={})
|
91
|
+
disk_image_shift!(opts)
|
92
|
+
end
|
93
|
+
|
94
|
+
DISK_INIT_DEFAULTS = {:disk_size => '10G'}
|
95
|
+
|
96
|
+
# Shifts an image, adding a new one using the
|
97
|
+
# previous newest one as backing file
|
98
|
+
#
|
99
|
+
# @param [Hash] opts options for the disk shift
|
100
|
+
# @option opts [String] :disk_size Disk size. Only used for image creation
|
101
|
+
def disk_image_shift!(opts={})
|
102
|
+
disk_config = Confstruct::Configuration.new(DISK_INIT_DEFAULTS)
|
103
|
+
|
104
|
+
file_name = File.join(work_dir, "sda-#{Time.now.to_i}.qcow2")
|
105
|
+
images = disk_images
|
106
|
+
|
107
|
+
file_name = File.join(work_dir, 'base.qcow2') if images.size == 0
|
108
|
+
|
109
|
+
args = ['/usr/bin/qemu-img', 'create',
|
110
|
+
'-f', "qcow2"]
|
111
|
+
|
112
|
+
if not images.empty?
|
113
|
+
args << '-b'
|
114
|
+
args << images.last
|
115
|
+
end
|
116
|
+
|
117
|
+
args << file_name
|
118
|
+
if images.empty?
|
119
|
+
args << disk_config.disk_size
|
120
|
+
end
|
121
|
+
|
122
|
+
Vmit.logger.info "Shifted image. Current is '#{file_name}'."
|
123
|
+
Cheetah.run(*args)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Rolls back to the previous snapshot
|
127
|
+
def disk_rollback!
|
128
|
+
images = disk_images
|
129
|
+
|
130
|
+
return if images.empty?
|
131
|
+
|
132
|
+
if images.size == 1
|
133
|
+
Vmit.logger.fatal "Only the base snapshot left!"
|
134
|
+
return
|
135
|
+
end
|
136
|
+
Vmit.logger.info "Removing #{images.last}"
|
137
|
+
FileUtils.rm(images.last)
|
138
|
+
end
|
139
|
+
|
140
|
+
# @returns [String] The latest COW snapshot
|
141
|
+
def current_image
|
142
|
+
curr = disk_images.last
|
143
|
+
raise "No hard disk image available" if curr.nil?
|
144
|
+
curr
|
145
|
+
end
|
146
|
+
|
147
|
+
def options
|
148
|
+
raise 'Workspace#options is deprecated.'
|
149
|
+
end
|
150
|
+
|
151
|
+
# @return [Hash] Config of the virtual machine
|
152
|
+
# This is all options plus the defaults
|
153
|
+
def config
|
154
|
+
@config
|
155
|
+
end
|
156
|
+
|
157
|
+
# @return [Hash] config that differs from default
|
158
|
+
# and therefore relevant to be persisted in config.yml
|
159
|
+
def relevant_config
|
160
|
+
config.diff(config.default_values)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Saves the configuration in config.yml
|
164
|
+
def save_config!
|
165
|
+
if not relevant_config.empty?
|
166
|
+
Vmit.logger.info "Writing config.yml..."
|
167
|
+
File.open(config_file, 'w') do |f|
|
168
|
+
f.write(relevant_config.to_yaml)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_s
|
174
|
+
config.to_s
|
175
|
+
end
|
176
|
+
|
177
|
+
BINDIR = File.join(File.dirname(__FILE__), '../../bin')
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
val1
|
@@ -0,0 +1 @@
|
|
1
|
+
4
|
@@ -0,0 +1 @@
|
|
1
|
+
val1
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require File.join(File.dirname(__FILE__), "helper")
|
22
|
+
require 'test/unit'
|
23
|
+
require 'vmit/install_media'
|
24
|
+
require 'tmpdir'
|
25
|
+
|
26
|
+
class InstallMedia_test < Test::Unit::TestCase
|
27
|
+
|
28
|
+
def test_scan
|
29
|
+
|
30
|
+
['openSUSE 12.1', 'opensuse12.1', 'opensuse_12.1'].each do |key|
|
31
|
+
media = Vmit::InstallMedia.scan(key)
|
32
|
+
assert_kind_of(Vmit::SUSEInstallMedia, media)
|
33
|
+
assert_equal('http://download.opensuse.org/distribution/12.1/repo/oss/',
|
34
|
+
media.location)
|
35
|
+
end
|
36
|
+
|
37
|
+
['openSUSE Factory', 'factory', 'opensuse_factory'].each do |key|
|
38
|
+
media = Vmit::InstallMedia.scan(key)
|
39
|
+
assert_kind_of(Vmit::SUSEInstallMedia, media)
|
40
|
+
assert_equal('http://download.opensuse.org/factory/repo/oss/',
|
41
|
+
media.location)
|
42
|
+
end
|
43
|
+
|
44
|
+
['debian wheezy', 'Debian wheezy', 'debian wheezy'].each do |key|
|
45
|
+
media = Vmit::InstallMedia.scan(key)
|
46
|
+
assert_kind_of(Vmit::DebianInstallMedia, media)
|
47
|
+
assert_equal('http://cdn.debian.net/debian/dists/wheezy',
|
48
|
+
media.location)
|
49
|
+
end
|
50
|
+
|
51
|
+
['sles10sp1', 'sles-10sp1', 'SLES10 SP1', 'SLE10 SP1'].each do |key|
|
52
|
+
media = Vmit::InstallMedia.scan(key)
|
53
|
+
assert_kind_of(Vmit::SUSEInstallMedia, media)
|
54
|
+
assert_equal("http://schnell.suse.de/BY_PRODUCT/sle-server-10-sp1-#{Vmit::Utils.arch}/",
|
55
|
+
media.location)
|
56
|
+
end
|
57
|
+
|
58
|
+
['sles11', 'sles-11', 'SLES11', 'SLE11'].each do |key|
|
59
|
+
media = Vmit::InstallMedia.scan(key)
|
60
|
+
assert_kind_of(Vmit::SUSEInstallMedia, media)
|
61
|
+
assert_equal("http://schnell.suse.de/BY_PRODUCT/sle-server-11-sp0-#{Vmit::Utils.arch}/",
|
62
|
+
media.location)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
#
|
21
|
+
require File.join(File.dirname(__FILE__), "helper")
|
22
|
+
require 'test/unit'
|
23
|
+
require 'vmit/registry'
|
24
|
+
require 'tmpdir'
|
25
|
+
|
26
|
+
class MyTypedRegistry < Vmit::TypedRegistry
|
27
|
+
type :key1, String
|
28
|
+
type :key2, Fixnum
|
29
|
+
end
|
30
|
+
|
31
|
+
class Registry_test < Test::Unit::TestCase
|
32
|
+
|
33
|
+
def test_basic_yaml
|
34
|
+
dir = File.join(File.dirname(__FILE__), "data/registry.yml")
|
35
|
+
reg = Vmit::YamlRegistry.new(dir)
|
36
|
+
|
37
|
+
assert_equal '2G', reg[:memory]
|
38
|
+
assert_equal '7a:7f:c7:dd:5f:bb', reg[:mac_address]
|
39
|
+
assert_equal 'Hello', reg[:sym_key]
|
40
|
+
keys = reg.keys
|
41
|
+
assert_equal [], [:memory, :mac_address, :sym_key] - keys
|
42
|
+
|
43
|
+
reg.each do |k,v|
|
44
|
+
assert keys.include?(k)
|
45
|
+
assert_equal reg[k], v
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_basic_existing_registry
|
50
|
+
dir = File.join(File.dirname(__FILE__), "data/registry")
|
51
|
+
reg = Vmit::FilesystemRegistry.new(dir)
|
52
|
+
|
53
|
+
assert_equal 'val1', reg[:key1]
|
54
|
+
assert_equal '4', reg[:key2]
|
55
|
+
keys = reg.keys
|
56
|
+
assert_equal [], [:key2, :key4, :key3, :key1] - keys
|
57
|
+
|
58
|
+
reg.each do |k,v|
|
59
|
+
assert keys.include?(k)
|
60
|
+
assert_equal reg[k], v
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_basic_new_registry
|
65
|
+
Dir.mktmpdir do |dir|
|
66
|
+
reg = Vmit::FilesystemRegistry.new(dir)
|
67
|
+
|
68
|
+
assert_nil reg[:nonexisting_key]
|
69
|
+
|
70
|
+
reg[:hello] = "Hello"
|
71
|
+
reg[:bye] = "Bye"
|
72
|
+
|
73
|
+
assert_equal "Hello", reg[:hello]
|
74
|
+
assert_equal "Bye", reg[:bye]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_buffered
|
79
|
+
Dir.mktmpdir do |dir|
|
80
|
+
reg = Vmit::FilesystemRegistry.new(dir)
|
81
|
+
breg = Vmit::BufferedRegistry.new(reg)
|
82
|
+
|
83
|
+
reg[:hello] = "Hello"
|
84
|
+
reg[:bye] = "Bye"
|
85
|
+
|
86
|
+
breg[:bye] = "Bye 2"
|
87
|
+
|
88
|
+
assert_equal "Bye", reg[:bye]
|
89
|
+
assert_equal "Bye 2", breg[:bye]
|
90
|
+
breg.save!
|
91
|
+
assert_equal "Bye 2", reg[:bye]
|
92
|
+
|
93
|
+
assert_equal "Hello", breg[:hello]
|
94
|
+
reg[:hello] = "Hello 2"
|
95
|
+
assert_equal "Hello", breg[:hello]
|
96
|
+
breg.reload!
|
97
|
+
assert_equal "Hello 2", breg[:hello]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_typed
|
102
|
+
Dir.mktmpdir do |dir|
|
103
|
+
reg = Vmit::FilesystemRegistry.new(dir)
|
104
|
+
treg = MyTypedRegistry.new(reg)
|
105
|
+
|
106
|
+
reg[:key1] = "Hello"
|
107
|
+
reg[:key2] = "1"
|
108
|
+
|
109
|
+
assert_equal "Hello", treg[:key1]
|
110
|
+
assert_equal 1, treg[:key2]
|
111
|
+
|
112
|
+
assert_raise TypeError do
|
113
|
+
treg[:key2] = "1"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|