automateit 0.70923
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -0
- data/CHANGES.txt +100 -0
- data/Hoe.rake +35 -0
- data/Manifest.txt +111 -0
- data/README.txt +44 -0
- data/Rakefile +284 -0
- data/TESTING.txt +57 -0
- data/TODO.txt +26 -0
- data/TUTORIAL.txt +390 -0
- data/bin/ai +3 -0
- data/bin/aifield +82 -0
- data/bin/aitag +128 -0
- data/bin/automateit +117 -0
- data/docs/friendly_errors.txt +50 -0
- data/docs/previews.txt +86 -0
- data/env.sh +4 -0
- data/examples/basic/Rakefile +26 -0
- data/examples/basic/config/automateit_env.rb +16 -0
- data/examples/basic/config/fields.yml +3 -0
- data/examples/basic/config/tags.yml +13 -0
- data/examples/basic/dist/README.txt +9 -0
- data/examples/basic/dist/myapp_server.erb +30 -0
- data/examples/basic/install.log +15 -0
- data/examples/basic/lib/README.txt +10 -0
- data/examples/basic/recipes/README.txt +4 -0
- data/examples/basic/recipes/install.rb +53 -0
- data/examples/basic/recipes/uninstall.rb +6 -0
- data/gpl.txt +674 -0
- data/lib/automateit.rb +66 -0
- data/lib/automateit/account_manager.rb +106 -0
- data/lib/automateit/account_manager/linux.rb +171 -0
- data/lib/automateit/account_manager/passwd.rb +69 -0
- data/lib/automateit/account_manager/portable.rb +136 -0
- data/lib/automateit/address_manager.rb +165 -0
- data/lib/automateit/address_manager/linux.rb +80 -0
- data/lib/automateit/address_manager/portable.rb +37 -0
- data/lib/automateit/cli.rb +80 -0
- data/lib/automateit/common.rb +65 -0
- data/lib/automateit/constants.rb +33 -0
- data/lib/automateit/edit_manager.rb +292 -0
- data/lib/automateit/error.rb +10 -0
- data/lib/automateit/field_manager.rb +103 -0
- data/lib/automateit/interpreter.rb +641 -0
- data/lib/automateit/package_manager.rb +242 -0
- data/lib/automateit/package_manager/apt.rb +63 -0
- data/lib/automateit/package_manager/egg.rb +64 -0
- data/lib/automateit/package_manager/gem.rb +179 -0
- data/lib/automateit/package_manager/portage.rb +69 -0
- data/lib/automateit/package_manager/yum.rb +65 -0
- data/lib/automateit/platform_manager.rb +47 -0
- data/lib/automateit/platform_manager/darwin.rb +30 -0
- data/lib/automateit/platform_manager/debian.rb +26 -0
- data/lib/automateit/platform_manager/freebsd.rb +25 -0
- data/lib/automateit/platform_manager/gentoo.rb +26 -0
- data/lib/automateit/platform_manager/lsb.rb +40 -0
- data/lib/automateit/platform_manager/struct.rb +78 -0
- data/lib/automateit/platform_manager/uname.rb +29 -0
- data/lib/automateit/platform_manager/windows.rb +33 -0
- data/lib/automateit/plugin.rb +7 -0
- data/lib/automateit/plugin/base.rb +32 -0
- data/lib/automateit/plugin/driver.rb +218 -0
- data/lib/automateit/plugin/manager.rb +232 -0
- data/lib/automateit/project.rb +460 -0
- data/lib/automateit/root.rb +14 -0
- data/lib/automateit/service_manager.rb +79 -0
- data/lib/automateit/service_manager/chkconfig.rb +39 -0
- data/lib/automateit/service_manager/rc_update.rb +37 -0
- data/lib/automateit/service_manager/sysv.rb +126 -0
- data/lib/automateit/service_manager/update_rcd.rb +35 -0
- data/lib/automateit/shell_manager.rb +261 -0
- data/lib/automateit/shell_manager/base_link.rb +67 -0
- data/lib/automateit/shell_manager/link.rb +24 -0
- data/lib/automateit/shell_manager/portable.rb +421 -0
- data/lib/automateit/shell_manager/symlink.rb +32 -0
- data/lib/automateit/shell_manager/which.rb +25 -0
- data/lib/automateit/tag_manager.rb +63 -0
- data/lib/automateit/tag_manager/struct.rb +101 -0
- data/lib/automateit/tag_manager/tag_parser.rb +91 -0
- data/lib/automateit/tag_manager/yaml.rb +29 -0
- data/lib/automateit/template_manager.rb +55 -0
- data/lib/automateit/template_manager/base.rb +172 -0
- data/lib/automateit/template_manager/erb.rb +17 -0
- data/lib/ext/metaclass.rb +17 -0
- data/lib/ext/object.rb +18 -0
- data/lib/hashcache.rb +22 -0
- data/lib/helpful_erb.rb +63 -0
- data/lib/nested_error.rb +33 -0
- data/lib/queued_logger.rb +68 -0
- data/lib/tempster.rb +239 -0
- data/misc/index_gem_repository.rb +303 -0
- data/misc/setup_egg.rb +12 -0
- data/misc/setup_gem_dependencies.sh +7 -0
- data/misc/setup_rubygems.sh +21 -0
- data/misc/which.cmd +6 -0
- data/spec/extras/automateit_service_sysv_test +50 -0
- data/spec/extras/scratch.rb +15 -0
- data/spec/extras/simple_recipe.rb +8 -0
- data/spec/integration/account_manager_spec.rb +218 -0
- data/spec/integration/address_manager_linux_spec.rb +119 -0
- data/spec/integration/address_manager_portable_spec.rb +30 -0
- data/spec/integration/cli_spec.rb +215 -0
- data/spec/integration/examples_spec.rb +54 -0
- data/spec/integration/examples_spec_editor.rb +71 -0
- data/spec/integration/package_manager_spec.rb +104 -0
- data/spec/integration/platform_manager_spec.rb +69 -0
- data/spec/integration/service_manager_sysv_spec.rb +115 -0
- data/spec/integration/shell_manager_spec.rb +471 -0
- data/spec/integration/template_manager_erb_spec.rb +31 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/edit_manager_spec.rb +162 -0
- data/spec/unit/field_manager_spec.rb +79 -0
- data/spec/unit/hashcache_spec.rb +28 -0
- data/spec/unit/interpreter_spec.rb +98 -0
- data/spec/unit/platform_manager_spec.rb +44 -0
- data/spec/unit/plugins_spec.rb +253 -0
- data/spec/unit/tag_manager_spec.rb +189 -0
- data/spec/unit/template_manager_erb_spec.rb +137 -0
- metadata +249 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
PACKAGE_FOUND_ERROR = %q{ERROR: Found the '%s' package installed for %s. You're probably not using this obscure package and should remove it so that this test can run. In the unlikely event that you actually rely on this package, change the spec to test with another unused package.}
|
4
|
+
PACKAGE_DRIVER_MISSING_ERROR = %{NOTE: Can't check %s on this platform, #{__FILE__}}
|
5
|
+
|
6
|
+
if not INTERPRETER.euid?
|
7
|
+
puts "NOTE: Can't check 'euid' on this platform, #{__FILE__}"
|
8
|
+
elsif not INTERPRETER.superuser?
|
9
|
+
puts "NOTE: Must be root to check #{__FILE__}"
|
10
|
+
else
|
11
|
+
describe "AutomateIt::PackageManager", :shared => true do
|
12
|
+
before(:all) do
|
13
|
+
@level = Logger::WARN
|
14
|
+
@a = AutomateIt.new(:verbosity => @level)
|
15
|
+
@m = @a.package_manager
|
16
|
+
@fake_package = "not_a_real_package"
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:all) do
|
20
|
+
@d.uninstall(@package, :quiet => true)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Some specs below leave side-effects which others depend on, although
|
24
|
+
# these are clearly documented within the specs. This is necessary
|
25
|
+
# because doing proper setup/teardown for each test would make it run 5x
|
26
|
+
# slower and take over a minute. Although this approach is problematic,
|
27
|
+
# the performance boost is worth it.
|
28
|
+
|
29
|
+
it "should not install an invalid package" do
|
30
|
+
@d.log.silence(Logger::FATAL) do
|
31
|
+
lambda{ @d.install(@fake_package, :quiet => true) }.should raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should install a package" do
|
36
|
+
@d.install(@package, :quiet => true).should be_true
|
37
|
+
# Leaves behind an installed package
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not re-install an installed package" do
|
41
|
+
# Expects package to be installed
|
42
|
+
@d.install(@package, :quiet => true).should be_false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should find an installed package" do
|
46
|
+
# Expects package to be installed
|
47
|
+
@d.installed?(@package).should be_true
|
48
|
+
@d.not_installed?(@package).should be_false
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should not find a package that's not installed" do
|
52
|
+
@d.installed?(@fake_package).should be_false
|
53
|
+
@d.not_installed?(@fake_package).should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should find group of packages" do
|
57
|
+
@d.installed?(@package, @fake_package, :details => true).should == [false, [@package]]
|
58
|
+
@d.not_installed?(@package, @fake_package, :details => true).should == [false, [@fake_package]]
|
59
|
+
# Leaves behind an installed package
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should uninstall a package" do
|
63
|
+
# Expects package to be installed
|
64
|
+
@d.uninstall(@package, :quiet => true).should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not uninstall a package that's not installed" do
|
68
|
+
@d.uninstall(@package, :quiet => true).should be_false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#-----------------------------------------------------------------------
|
73
|
+
|
74
|
+
targets = {
|
75
|
+
:apt => "nomarch", # Obscure package for extracting ARC files from the 80's
|
76
|
+
:yum => "nomarch", # Obscure package for extracting ARC files from the 80's
|
77
|
+
:portage => "arc", # Obscure package for extracting ARC files from the 80's
|
78
|
+
:gem => "s33r", # Alpha-grade package its author deprecated in favor of another
|
79
|
+
:egg => "_sre.py", # Slower reimplementation of ancient Python Regexps
|
80
|
+
### :cpan => "Acme::please", # Insane gimmick port of intercal's please statements
|
81
|
+
}
|
82
|
+
|
83
|
+
if INTERPRETER.tagged?(:centos)
|
84
|
+
# CentOS lacks "nomarch", so use a less obscure archiver from the early 90's.
|
85
|
+
targets[:yum] = "arj"
|
86
|
+
end
|
87
|
+
|
88
|
+
targets.each_pair do |driver_token, package|
|
89
|
+
driver = INTERPRETER.package_manager[driver_token]
|
90
|
+
if driver.available?
|
91
|
+
describe driver.class.to_s do
|
92
|
+
it_should_behave_like "AutomateIt::PackageManager"
|
93
|
+
|
94
|
+
before(:all) do
|
95
|
+
@d = @m[driver_token]
|
96
|
+
@package = package
|
97
|
+
raise PACKAGE_FOUND_ERROR % [@package, @d.class] if @d.installed?(@package)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
else
|
101
|
+
puts PACKAGE_DRIVER_MISSING_ERROR % driver.class
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
# TODO PlatformManager spec -- split entire spec into multiple, driver-specific ones.
|
4
|
+
|
5
|
+
begin
|
6
|
+
raise IndexError unless String === INTERPRETER.platform_manager.query("os")
|
7
|
+
|
8
|
+
describe "AutomateIt::PlatformManager" do
|
9
|
+
before(:all) do
|
10
|
+
@a = AutomateIt.new
|
11
|
+
@m = @a.platform_manager
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should query os" do
|
15
|
+
@m.query("os").should be_a_kind_of(String)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should query arch" do
|
19
|
+
@m.query(:arch).should be_a_kind_of(String)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should query os and arch" do
|
23
|
+
@m.query("os#arch").should be_a_kind_of(String)
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
raise IndexError unless String === INTERPRETER.platform_manager.query("distro")
|
28
|
+
|
29
|
+
it "should query distro" do
|
30
|
+
@m.query("distro").should be_a_kind_of(String)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should query release" do
|
34
|
+
@m.query(:release).should be_a_kind_of(String)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should fail on invalid LSB output" do
|
38
|
+
if AutomateIt::PlatformManager::LSB === @m.driver_for(:query, :release)
|
39
|
+
# XXX mocking a shared variable breaks it because the mock doesn't go away
|
40
|
+
m = AutomateIt.new.platform_manager
|
41
|
+
m[:lsb].send(:instance_eval, "@struct.delete(:release)")
|
42
|
+
m[:lsb].class.send(:class_eval, "@@struct_cache.delete(:release)")
|
43
|
+
m[:lsb].should_receive(:_read_lsb_release_output).and_return("not : valid : yaml")
|
44
|
+
callback = lambda{ m[:lsb].setup; m[:lsb].query(:release) }
|
45
|
+
if RUBY_PLATFORM == "java"
|
46
|
+
callback.should raise_error # YAML throws a native, internal error
|
47
|
+
else
|
48
|
+
callback.should raise_error(ArgumentError, /invalid YAML/)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should query combination of os, arch, distro and release" do
|
54
|
+
result = @m.query("os#arch#distro#release")
|
55
|
+
result.should be_a_kind_of(String)
|
56
|
+
elements = result.split(/_/)
|
57
|
+
elements.size.should >= 4
|
58
|
+
for element in elements
|
59
|
+
element.should be_a_kind_of(String)
|
60
|
+
element.size.should > 0
|
61
|
+
end
|
62
|
+
end
|
63
|
+
rescue NotImplementedError, IndexError
|
64
|
+
puts "NOTE: Can't check 'distro' query on this platform, #{__FILE__}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue NotImplementedError, IndexError
|
68
|
+
puts "NOTE: Can't check 'query' on this platform, #{__FILE__}"
|
69
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
if not INTERPRETER.euid?
|
4
|
+
puts "NOTE: Can't check 'euid' on this platform, #{__FILE__}"
|
5
|
+
elsif not INTERPRETER.superuser?
|
6
|
+
puts "NOTE: Must be root to check #{__FILE__}"
|
7
|
+
elsif not INTERPRETER.service_manager[:sysv].available?
|
8
|
+
puts "NOTE: Can't check ServiceManager::SYSV on this platform, #{__FILE__}"
|
9
|
+
else
|
10
|
+
describe "AutomateIt::ServiceManager::SYSV" do
|
11
|
+
begin
|
12
|
+
INTERPRETER.service_manager.driver_for(:enabled?, @service_name)
|
13
|
+
@has_enable = true
|
14
|
+
rescue NotImplementedError
|
15
|
+
@has_enable = false
|
16
|
+
puts "NOTE: Can't check 'enabled?' on this platform, #{__FILE__}"
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:all) do
|
20
|
+
@a = AutomateIt.new(:verbosity => Logger::WARN)
|
21
|
+
@m = @a.service_manager
|
22
|
+
|
23
|
+
@service_name = "automateit_service_sysv_test"
|
24
|
+
@service_file = "/etc/init.d/"+@service_name
|
25
|
+
@source_file = File.join(File.dirname(__FILE__), "..", "extras", @service_name)
|
26
|
+
|
27
|
+
FileUtils.cp(@source_file, @service_file)
|
28
|
+
FileUtils.chmod(0755, @service_file)
|
29
|
+
end
|
30
|
+
|
31
|
+
before(:each) do
|
32
|
+
@m.stop(@service_name, :quiet => true) if @m.running?(@service_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
after(:all) do
|
36
|
+
@m.stop(@service_name, :quiet => true) if @m.running?(@service_name)
|
37
|
+
FileUtils.rm(@service_file) if File.exists?(@service_file)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should start a service" do
|
41
|
+
@m.start(@service_name, :quiet => true).should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not start an already running service" do
|
45
|
+
@m.start(@service_name, :quiet => true).should be_true
|
46
|
+
@m.start(@service_name).should be_false
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should stop a service" do
|
50
|
+
@m.start(@service_name, :quiet => true).should be_true
|
51
|
+
@m.stop(@service_name, :quiet => true).should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should not stop a service that's not running" do
|
55
|
+
@m.stop(@service_name).should be_false
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should identify a non-running service" do
|
59
|
+
@m.running?(@service_name).should be_false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should identify a running service" do
|
63
|
+
@m.start(@service_name, :quiet => true).should be_true
|
64
|
+
@m.running?(@service_name).should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
if @has_enable
|
68
|
+
# It's more correct to disable the service using before/after, but the
|
69
|
+
# platform-specific scripts are ridiculously slow, so manually disabling
|
70
|
+
# the service only when necessary significantly speeds the test.
|
71
|
+
@disable_manually = true
|
72
|
+
|
73
|
+
before(:each) do
|
74
|
+
@m.disable(@service_name, :quiet => true) if not @disable_manually
|
75
|
+
end
|
76
|
+
|
77
|
+
after(:all) do
|
78
|
+
@m.disable(@service_name, :quiet => true) if not @disable_manually
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should enable a service" do
|
82
|
+
@m.enable(@service_name, :quiet => true).should be_true
|
83
|
+
# Tear down
|
84
|
+
@m.disable(@service_name, :quiet => true).should be_true if @disable_manually
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should not enable an enabled service" do
|
88
|
+
@m.enable(@service_name, :quiet => true).should be_true
|
89
|
+
@m.enable(@service_name, :quiet => true).should be_false
|
90
|
+
# Tear down
|
91
|
+
@m.disable(@service_name, :quiet => true).should be_true if @disable_manually
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should disable a service" do
|
95
|
+
@m.enable(@service_name, :quiet => true).should be_true
|
96
|
+
@m.disable(@service_name, :quiet => true).should be_true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not disable a disabled service" do
|
100
|
+
@m.disable(@service_name, :quiet => true).should be_false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should identify a disabled service" do
|
104
|
+
@m.enabled?(@service_name).should be_false
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should identify an enabled service" do
|
108
|
+
@m.enable(@service_name, :quiet => true).should be_true
|
109
|
+
@m.enabled?(@service_name).should be_true
|
110
|
+
# Tear down
|
111
|
+
@m.disable(@service_name, :quiet => true).should be_true if @disable_manually
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,471 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
describe AutomateIt::ShellManager, :shared => true do
|
4
|
+
before(:all) do
|
5
|
+
@a = AutomateIt.new(:verbosity => Logger::WARN)
|
6
|
+
@m = @a.shell_manager
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe AutomateIt::ShellManager, " with sh and which" do
|
11
|
+
it_should_behave_like "AutomateIt::ShellManager"
|
12
|
+
|
13
|
+
begin
|
14
|
+
INTERPRETER.which("true")
|
15
|
+
INTERPRETER.which("false")
|
16
|
+
|
17
|
+
it "should run shell commands and detect their exit status (sh)" do
|
18
|
+
@m.sh("true").should be_true
|
19
|
+
@m.sh("false").should be_false
|
20
|
+
end
|
21
|
+
rescue NotImplementedError
|
22
|
+
puts "NOTE: Can't check 'sh' on this platform, #{__FILE__}"
|
23
|
+
end
|
24
|
+
|
25
|
+
unless INTERPRETER.shell_manager.available?(:which)
|
26
|
+
puts "NOTE: Can't use 'which' on this platform in #{__FILE__}"
|
27
|
+
else
|
28
|
+
it "should find which program is in the path (which)" do
|
29
|
+
@m.which("sh").match(/.\/sh$/).nil?.should be_false
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not find programs that aren't in the path (which)" do
|
33
|
+
@m.which("not_a_real_program").should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should throw exception if command isn't in path (which!)" do
|
37
|
+
lambda{ @m.which!("not_a_real_program") }.should raise_error(ArgumentError, /not_a_real_program/)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe AutomateIt::ShellManager, " in general" do
|
43
|
+
it_should_behave_like "AutomateIt::ShellManager"
|
44
|
+
|
45
|
+
it "should change directories (cd)" do
|
46
|
+
before = Dir.pwd
|
47
|
+
target = Pathname.new("/").expand_path.to_s
|
48
|
+
@m.cd(target)
|
49
|
+
Dir.pwd.should == target
|
50
|
+
@m.cd(before)
|
51
|
+
Dir.pwd.should == before
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should change directories using a block (cd)" do
|
55
|
+
before = Dir.pwd
|
56
|
+
target = Pathname.new("/").expand_path.to_s
|
57
|
+
@m.cd(target) do
|
58
|
+
Dir.pwd.should == target
|
59
|
+
end
|
60
|
+
Dir.pwd.should == before
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should locate the current directory (pwd)" do
|
64
|
+
@m.pwd.should == Dir.pwd
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create a directory when needed (mkdir)" do
|
68
|
+
@m.mktempdircd do
|
69
|
+
target = "foo"
|
70
|
+
File.directory?(target).should be_false
|
71
|
+
|
72
|
+
@m.mkdir(target).should == [target]
|
73
|
+
File.directory?(target).should be_true
|
74
|
+
|
75
|
+
@m.mkdir(target).should be_false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should create nested directories when needed (mkdir_p)" do
|
80
|
+
@m.mktempdircd do
|
81
|
+
target = "foo/bar/baz"
|
82
|
+
File.directory?(target).should be_false
|
83
|
+
|
84
|
+
@m.mkdir_p(target).should == [target]
|
85
|
+
File.directory?(target).should be_true
|
86
|
+
|
87
|
+
@m.mkdir_p(target).should be_false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should remove directory when needed (rmdir)" do
|
92
|
+
@m.mktempdircd do
|
93
|
+
target = "foo"
|
94
|
+
@m.mkdir(target).should == [target]
|
95
|
+
|
96
|
+
@m.rmdir(target).should == [target]
|
97
|
+
File.directory?(target).should be_false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should install a file to a file (install)" do
|
102
|
+
@m.mktempdircd do
|
103
|
+
source = "foo"
|
104
|
+
target = "bar"
|
105
|
+
mode1 = 0640 if @m.provides_mode?
|
106
|
+
mode2 = 0100640
|
107
|
+
|
108
|
+
@a.render(:text => "Hello", :to => source)
|
109
|
+
File.exists?(source).should be_true
|
110
|
+
File.exists?(target).should be_false
|
111
|
+
|
112
|
+
@a.install(source, target, mode1).should == source
|
113
|
+
File.exists?(target).should be_true
|
114
|
+
File.stat(target).mode.should == mode2 if @m.provides_mode?
|
115
|
+
|
116
|
+
@a.install(source, target, mode1).should be_false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should copy a file to file (cp)" do
|
121
|
+
@m.mktempdircd do
|
122
|
+
source = "foo"
|
123
|
+
target = "bar"
|
124
|
+
|
125
|
+
@a.render(:text => "Hello", :to => source)
|
126
|
+
File.exists?(source).should be_true
|
127
|
+
File.exists?(target).should be_false
|
128
|
+
|
129
|
+
@a.cp(source, target).should == source
|
130
|
+
File.exists?(target).should be_true
|
131
|
+
|
132
|
+
@a.cp(source, target).should be_false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should copy a file to a directory (cp)" do
|
137
|
+
@m.mktempdircd do
|
138
|
+
source = "foo"
|
139
|
+
target = "bar"
|
140
|
+
|
141
|
+
@a.render(:text => "Hello", :to => source)
|
142
|
+
@a.mkdir(target)
|
143
|
+
File.exists?(source).should be_true
|
144
|
+
File.exists?(target).should be_true
|
145
|
+
|
146
|
+
@a.cp(source, target).should == source
|
147
|
+
File.exists?("bar/foo").should be_true
|
148
|
+
|
149
|
+
@a.cp(source, target).should be_false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should copy files to a directory (cp)" do
|
154
|
+
@m.mktempdircd do
|
155
|
+
source1 = "foo"
|
156
|
+
source2 = "bar"
|
157
|
+
target = "baz"
|
158
|
+
|
159
|
+
@a.render(:text => "Hello", :to => source1)
|
160
|
+
@a.render(:text => "Hello", :to => source2)
|
161
|
+
@a.mkdir(target)
|
162
|
+
|
163
|
+
@a.cp([source1, source2], target).should == [source1, source2]
|
164
|
+
File.exists?("baz/foo").should be_true
|
165
|
+
File.exists?("baz/bar").should be_true
|
166
|
+
|
167
|
+
@a.cp([source1, source2], target).should be_false
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should copy directory to a directory (cp)" do
|
172
|
+
@m.mktempdircd do
|
173
|
+
source_dir = "feh"
|
174
|
+
source_file1 = "feh/file1"
|
175
|
+
source_file2 = "feh/file2"
|
176
|
+
target_dir = "meh"
|
177
|
+
|
178
|
+
@a.mkdir(source_dir)
|
179
|
+
@a.render(:text => "Hello", :to => source_file1)
|
180
|
+
@a.render(:text => "Hello", :to => source_file2)
|
181
|
+
@a.mkdir(target_dir)
|
182
|
+
|
183
|
+
@a.cp(source_dir, target_dir).should == source_dir
|
184
|
+
File.exists?("meh/feh/file1").should be_true
|
185
|
+
File.exists?("meh/feh/file2").should be_true
|
186
|
+
|
187
|
+
@a.cp(source_dir, target_dir).should be_false
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# TODO implement umask spec
|
192
|
+
|
193
|
+
# TODO implement gap
|
194
|
+
|
195
|
+
it "should move files (mv)" do
|
196
|
+
@m.mktempdircd do
|
197
|
+
file1 = "foo"
|
198
|
+
file2 = "bar"
|
199
|
+
@m.touch(file1)
|
200
|
+
File.exists?(file1).should be_true
|
201
|
+
|
202
|
+
@m.mv(file1, file2).should == file1
|
203
|
+
File.exists?(file1).should be_false
|
204
|
+
File.exists?(file2).should be_true
|
205
|
+
|
206
|
+
@m.mv(file1, file2).should be_false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should delete files (rm)" do
|
211
|
+
@m.mktempdircd do
|
212
|
+
file1 = "foo"
|
213
|
+
file2 = "bar"
|
214
|
+
@m.touch(file1)
|
215
|
+
@m.touch(file2)
|
216
|
+
File.exists?(file1).should be_true
|
217
|
+
File.exists?(file2).should be_true
|
218
|
+
|
219
|
+
@m.rm([file1, file2]) == [file1, file2]
|
220
|
+
File.exists?(file1).should be_false
|
221
|
+
File.exists?(file2).should be_false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should delete recursively (rm_r)" do
|
226
|
+
@m.mktempdircd do
|
227
|
+
dir = "foo/bar"
|
228
|
+
file = dir+"/baz"
|
229
|
+
@m.mkdir_p(dir)
|
230
|
+
@m.touch(file)
|
231
|
+
File.exists?(file).should be_true
|
232
|
+
File.exists?(dir).should be_true
|
233
|
+
|
234
|
+
@m.rm_r(dir) == [dir, file]
|
235
|
+
File.exists?(file).should be_false
|
236
|
+
File.exists?(dir).should be_false
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should delete recursively and forcefully (rm_rf)" do
|
241
|
+
@m.mktempdircd do
|
242
|
+
dir = "foo/bar"
|
243
|
+
file = dir+"/baz"
|
244
|
+
@m.mkdir_p(dir)
|
245
|
+
@m.touch(file)
|
246
|
+
File.exists?(file).should be_true
|
247
|
+
File.exists?(dir).should be_true
|
248
|
+
|
249
|
+
@m.rm_rf(dir) == [file, dir]
|
250
|
+
File.exists?(file).should be_false
|
251
|
+
File.exists?(dir).should be_false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
unless INTERPRETER.shell_manager.provides_mode?
|
256
|
+
puts "NOTE: Can't check permission modes on this platform, #{__FILE__}"
|
257
|
+
else
|
258
|
+
it "should change the permissions of files (chmod)" do
|
259
|
+
@m.mktempdircd do
|
260
|
+
target = "foo"
|
261
|
+
mode = 0654
|
262
|
+
@m.touch(target)
|
263
|
+
(File.stat(target).mode ^ (mode | 0100000)).zero?.should be_false
|
264
|
+
|
265
|
+
@m.chmod(mode, target).should == target
|
266
|
+
(File.stat(target).mode ^ (mode | 0100000)).zero?.should be_true
|
267
|
+
|
268
|
+
@m.chmod(mode, target).should be_false
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should change the permissions of files recursively (chmod_R)" do
|
273
|
+
@m.mktempdircd do
|
274
|
+
mode = 0754
|
275
|
+
dir = "foo/bar"
|
276
|
+
file = dir+"/baz"
|
277
|
+
@m.mkdir_p(dir)
|
278
|
+
@m.touch(file)
|
279
|
+
File.exists?(file).should be_true
|
280
|
+
File.exists?(dir).should be_true
|
281
|
+
(File.stat(file).mode ^ (mode | 0100000)).zero?.should be_false
|
282
|
+
(File.stat(dir).mode ^ (mode | 040000)).zero?.should be_false
|
283
|
+
|
284
|
+
@m.chmod_R(mode, dir).should == dir
|
285
|
+
(File.stat(file).mode ^ (mode | 0100000)).zero?.should be_true
|
286
|
+
(File.stat(dir).mode ^ (mode | 04000)).zero?.should be_false
|
287
|
+
|
288
|
+
@m.chmod_R(mode, dir).should be_false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should create files and change their timestamps (touch)" do
|
294
|
+
@m.mktempdircd do
|
295
|
+
target = "foo"
|
296
|
+
File.exists?(target).should be_false
|
297
|
+
|
298
|
+
@m.touch(target)
|
299
|
+
File.exists?(target).should be_true
|
300
|
+
before = File.mtime(target)
|
301
|
+
|
302
|
+
@m.touch(target)
|
303
|
+
after = File.mtime(target)
|
304
|
+
before.should <= after
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe AutomateIt::ShellManager, " when managing permissions" do
|
310
|
+
if not INTERPRETER.euid?
|
311
|
+
puts "NOTE: Can't check 'euid' on this platform, #{__FILE__}"
|
312
|
+
elsif not INTERPRETER.shell_manager.provides_ownership?
|
313
|
+
puts "NOTE: Can't check ownership on this platform, #{__FILE__}"
|
314
|
+
elsif INTERPRETER.superuser?
|
315
|
+
it_should_behave_like "AutomateIt::ShellManager"
|
316
|
+
|
317
|
+
before(:all) do
|
318
|
+
@pwent, @grent = find_mortal_pwent_and_grent
|
319
|
+
@user = @pwent.name
|
320
|
+
@uid = @pwent.uid
|
321
|
+
@group = @grent.name
|
322
|
+
@gid = @grent.gid
|
323
|
+
end
|
324
|
+
|
325
|
+
# Return a pwent and grent of a non-root user
|
326
|
+
def find_mortal_pwent_and_grent
|
327
|
+
while true
|
328
|
+
pwent = Etc.getpwent
|
329
|
+
# Root is usually 0, but Darwin's users can have negative UIDs
|
330
|
+
break if pwent.uid > 0
|
331
|
+
end
|
332
|
+
Etc.endpwent
|
333
|
+
grent = Etc.getgrgid(pwent.gid)
|
334
|
+
return [pwent, grent]
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should change the ownership of files (chown)" do
|
338
|
+
@m.mktempdircd do
|
339
|
+
target = "foo"
|
340
|
+
|
341
|
+
@m.touch(target)
|
342
|
+
stat = File.stat(target)
|
343
|
+
(stat.uid == @uid).should be_false
|
344
|
+
(stat.gid == @gid).should be_false
|
345
|
+
|
346
|
+
@m.chown(@user, @group, target).should == target
|
347
|
+
stat = File.stat(target)
|
348
|
+
(stat.uid == @uid).should be_true
|
349
|
+
(stat.gid == @gid).should be_true
|
350
|
+
|
351
|
+
@m.chown(@uid, @group, target).should be_false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should change the ownership of files recursively (chown_R)" do
|
356
|
+
@m.mktempdircd do
|
357
|
+
dir = "foo/bar"
|
358
|
+
file = dir+"/baz"
|
359
|
+
|
360
|
+
@m.mkdir_p(dir)
|
361
|
+
@m.touch(file)
|
362
|
+
File.exists?(file).should be_true
|
363
|
+
File.exists?(dir).should be_true
|
364
|
+
stat = File.stat(file)
|
365
|
+
(stat.uid == @uid).should be_false
|
366
|
+
(stat.gid == @gid).should be_false
|
367
|
+
stat = File.stat(dir)
|
368
|
+
(stat.uid == @uid).should be_false
|
369
|
+
(stat.gid == @gid).should be_false
|
370
|
+
|
371
|
+
@m.chown_R(@uid, @group, dir).should == dir
|
372
|
+
stat = File.stat(file)
|
373
|
+
(stat.uid == @uid).should be_true
|
374
|
+
(stat.gid == @gid).should be_true
|
375
|
+
stat = File.stat(dir)
|
376
|
+
(stat.uid == @uid).should be_true
|
377
|
+
(stat.gid == @gid).should be_true
|
378
|
+
|
379
|
+
@m.chown_R(@uid, @group, dir).should be_false
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should translate :owner to :user for Cfengine refugees" do
|
384
|
+
@m.mktempdircd do
|
385
|
+
dir = "foo/bar"
|
386
|
+
file = dir+"/baz"
|
387
|
+
|
388
|
+
@m.mkdir_p(dir)
|
389
|
+
@m.touch(file)
|
390
|
+
|
391
|
+
@m.chperm(dir, :recursive => true, :owner => @user, :group => @group).should == dir
|
392
|
+
|
393
|
+
stat = File.stat(file)
|
394
|
+
(stat.uid == @uid).should be_true
|
395
|
+
(stat.gid == @gid).should be_true
|
396
|
+
stat = File.stat(dir)
|
397
|
+
(stat.uid == @uid).should be_true
|
398
|
+
(stat.gid == @gid).should be_true
|
399
|
+
end
|
400
|
+
end
|
401
|
+
else
|
402
|
+
puts "NOTE: Must be root to check 'chown' in #{__FILE__}"
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
describe AutomateIt::ShellManager, " when managing hard links" do
|
407
|
+
if not INTERPRETER.shell_manager.available?(:ln)
|
408
|
+
puts "NOTE: Can't check hard links on this platform, #{__FILE__}"
|
409
|
+
else
|
410
|
+
it_should_behave_like "AutomateIt::ShellManager"
|
411
|
+
|
412
|
+
it "should create hard links when needed (ln)" do
|
413
|
+
@m.mktempdircd do
|
414
|
+
source = "foo"
|
415
|
+
target = "bar"
|
416
|
+
@m.touch(source)
|
417
|
+
File.exists?(source).should be_true
|
418
|
+
File.exists?(target).should be_false
|
419
|
+
|
420
|
+
@m.ln(source, target).should == source
|
421
|
+
File.stat(target).nlink.should > 1
|
422
|
+
|
423
|
+
@m.ln(source, target).should be_false
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
describe AutomateIt::ShellManager, " when managing symbolic links" do
|
430
|
+
if not INTERPRETER.shell_manager.available?(:ln_s)
|
431
|
+
puts "NOTE: Can't check symbolic links on this platform, #{__FILE__}"
|
432
|
+
else
|
433
|
+
it_should_behave_like "AutomateIt::ShellManager"
|
434
|
+
|
435
|
+
it "should create symlinks when needed (ln_s)" do
|
436
|
+
@m.mktempdircd do
|
437
|
+
source = "foo"
|
438
|
+
target = "bar"
|
439
|
+
@m.touch(source)
|
440
|
+
File.exists?(source).should be_true
|
441
|
+
File.exists?(target).should be_false
|
442
|
+
|
443
|
+
@m.ln_s(source, target).should == source
|
444
|
+
File.symlink?(target).should be_true
|
445
|
+
Pathname.new(target).realpath == Pathname.new(source).realpath
|
446
|
+
|
447
|
+
@m.ln_s(source, target).should be_false
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should create symlinks that replace existing entry (ln_sf)" do
|
452
|
+
@m.mktempdircd do
|
453
|
+
source = "foo"
|
454
|
+
intermediate = "baz"
|
455
|
+
target = "bar"
|
456
|
+
@m.touch(source)
|
457
|
+
@m.touch(intermediate)
|
458
|
+
File.exists?(source).should be_true
|
459
|
+
File.exists?(intermediate).should be_true
|
460
|
+
File.exists?(target).should be_false
|
461
|
+
|
462
|
+
@m.ln_s(intermediate, target).should == intermediate
|
463
|
+
File.symlink?(target).should be_true
|
464
|
+
|
465
|
+
@m.ln_sf(source, target).should == source
|
466
|
+
File.symlink?(target).should be_true
|
467
|
+
Pathname.new(target).realpath == Pathname.new(source).realpath
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|