automateit 0.70923

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data.tar.gz.sig +1 -0
  2. data/CHANGES.txt +100 -0
  3. data/Hoe.rake +35 -0
  4. data/Manifest.txt +111 -0
  5. data/README.txt +44 -0
  6. data/Rakefile +284 -0
  7. data/TESTING.txt +57 -0
  8. data/TODO.txt +26 -0
  9. data/TUTORIAL.txt +390 -0
  10. data/bin/ai +3 -0
  11. data/bin/aifield +82 -0
  12. data/bin/aitag +128 -0
  13. data/bin/automateit +117 -0
  14. data/docs/friendly_errors.txt +50 -0
  15. data/docs/previews.txt +86 -0
  16. data/env.sh +4 -0
  17. data/examples/basic/Rakefile +26 -0
  18. data/examples/basic/config/automateit_env.rb +16 -0
  19. data/examples/basic/config/fields.yml +3 -0
  20. data/examples/basic/config/tags.yml +13 -0
  21. data/examples/basic/dist/README.txt +9 -0
  22. data/examples/basic/dist/myapp_server.erb +30 -0
  23. data/examples/basic/install.log +15 -0
  24. data/examples/basic/lib/README.txt +10 -0
  25. data/examples/basic/recipes/README.txt +4 -0
  26. data/examples/basic/recipes/install.rb +53 -0
  27. data/examples/basic/recipes/uninstall.rb +6 -0
  28. data/gpl.txt +674 -0
  29. data/lib/automateit.rb +66 -0
  30. data/lib/automateit/account_manager.rb +106 -0
  31. data/lib/automateit/account_manager/linux.rb +171 -0
  32. data/lib/automateit/account_manager/passwd.rb +69 -0
  33. data/lib/automateit/account_manager/portable.rb +136 -0
  34. data/lib/automateit/address_manager.rb +165 -0
  35. data/lib/automateit/address_manager/linux.rb +80 -0
  36. data/lib/automateit/address_manager/portable.rb +37 -0
  37. data/lib/automateit/cli.rb +80 -0
  38. data/lib/automateit/common.rb +65 -0
  39. data/lib/automateit/constants.rb +33 -0
  40. data/lib/automateit/edit_manager.rb +292 -0
  41. data/lib/automateit/error.rb +10 -0
  42. data/lib/automateit/field_manager.rb +103 -0
  43. data/lib/automateit/interpreter.rb +641 -0
  44. data/lib/automateit/package_manager.rb +242 -0
  45. data/lib/automateit/package_manager/apt.rb +63 -0
  46. data/lib/automateit/package_manager/egg.rb +64 -0
  47. data/lib/automateit/package_manager/gem.rb +179 -0
  48. data/lib/automateit/package_manager/portage.rb +69 -0
  49. data/lib/automateit/package_manager/yum.rb +65 -0
  50. data/lib/automateit/platform_manager.rb +47 -0
  51. data/lib/automateit/platform_manager/darwin.rb +30 -0
  52. data/lib/automateit/platform_manager/debian.rb +26 -0
  53. data/lib/automateit/platform_manager/freebsd.rb +25 -0
  54. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  55. data/lib/automateit/platform_manager/lsb.rb +40 -0
  56. data/lib/automateit/platform_manager/struct.rb +78 -0
  57. data/lib/automateit/platform_manager/uname.rb +29 -0
  58. data/lib/automateit/platform_manager/windows.rb +33 -0
  59. data/lib/automateit/plugin.rb +7 -0
  60. data/lib/automateit/plugin/base.rb +32 -0
  61. data/lib/automateit/plugin/driver.rb +218 -0
  62. data/lib/automateit/plugin/manager.rb +232 -0
  63. data/lib/automateit/project.rb +460 -0
  64. data/lib/automateit/root.rb +14 -0
  65. data/lib/automateit/service_manager.rb +79 -0
  66. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  67. data/lib/automateit/service_manager/rc_update.rb +37 -0
  68. data/lib/automateit/service_manager/sysv.rb +126 -0
  69. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  70. data/lib/automateit/shell_manager.rb +261 -0
  71. data/lib/automateit/shell_manager/base_link.rb +67 -0
  72. data/lib/automateit/shell_manager/link.rb +24 -0
  73. data/lib/automateit/shell_manager/portable.rb +421 -0
  74. data/lib/automateit/shell_manager/symlink.rb +32 -0
  75. data/lib/automateit/shell_manager/which.rb +25 -0
  76. data/lib/automateit/tag_manager.rb +63 -0
  77. data/lib/automateit/tag_manager/struct.rb +101 -0
  78. data/lib/automateit/tag_manager/tag_parser.rb +91 -0
  79. data/lib/automateit/tag_manager/yaml.rb +29 -0
  80. data/lib/automateit/template_manager.rb +55 -0
  81. data/lib/automateit/template_manager/base.rb +172 -0
  82. data/lib/automateit/template_manager/erb.rb +17 -0
  83. data/lib/ext/metaclass.rb +17 -0
  84. data/lib/ext/object.rb +18 -0
  85. data/lib/hashcache.rb +22 -0
  86. data/lib/helpful_erb.rb +63 -0
  87. data/lib/nested_error.rb +33 -0
  88. data/lib/queued_logger.rb +68 -0
  89. data/lib/tempster.rb +239 -0
  90. data/misc/index_gem_repository.rb +303 -0
  91. data/misc/setup_egg.rb +12 -0
  92. data/misc/setup_gem_dependencies.sh +7 -0
  93. data/misc/setup_rubygems.sh +21 -0
  94. data/misc/which.cmd +6 -0
  95. data/spec/extras/automateit_service_sysv_test +50 -0
  96. data/spec/extras/scratch.rb +15 -0
  97. data/spec/extras/simple_recipe.rb +8 -0
  98. data/spec/integration/account_manager_spec.rb +218 -0
  99. data/spec/integration/address_manager_linux_spec.rb +119 -0
  100. data/spec/integration/address_manager_portable_spec.rb +30 -0
  101. data/spec/integration/cli_spec.rb +215 -0
  102. data/spec/integration/examples_spec.rb +54 -0
  103. data/spec/integration/examples_spec_editor.rb +71 -0
  104. data/spec/integration/package_manager_spec.rb +104 -0
  105. data/spec/integration/platform_manager_spec.rb +69 -0
  106. data/spec/integration/service_manager_sysv_spec.rb +115 -0
  107. data/spec/integration/shell_manager_spec.rb +471 -0
  108. data/spec/integration/template_manager_erb_spec.rb +31 -0
  109. data/spec/spec_helper.rb +23 -0
  110. data/spec/unit/edit_manager_spec.rb +162 -0
  111. data/spec/unit/field_manager_spec.rb +79 -0
  112. data/spec/unit/hashcache_spec.rb +28 -0
  113. data/spec/unit/interpreter_spec.rb +98 -0
  114. data/spec/unit/platform_manager_spec.rb +44 -0
  115. data/spec/unit/plugins_spec.rb +253 -0
  116. data/spec/unit/tag_manager_spec.rb +189 -0
  117. data/spec/unit/template_manager_erb_spec.rb +137 -0
  118. metadata +249 -0
  119. 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