rspec-puppet-augeas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ pkg/
2
+ *.gem
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gem 'rspec-puppet'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Dominic Cleal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,122 @@
1
+ # RSpec tests for Augeas resources inside Puppet manifests
2
+
3
+ ## Summary
4
+
5
+ rspec-puppet-augeas is an extension of rodjek's popular rspec-puppet tool. It
6
+ adds to your RSpec tests for a single class or define (or anything resulting in
7
+ a catalog) and allows you to run and test individual Augeas resources within it.
8
+
9
+ It takes a set of input files (fixtures) that the resource(s) will modify, runs
10
+ the resource, can verify it changed and is idempotent, then provides
11
+ Augeas-based tools to help verify the modification was made.
12
+
13
+ ## Setting up
14
+
15
+ Install the gem first:
16
+
17
+ gem install rspec-puppet-augeas
18
+
19
+ Extend your usual rspec-puppet class test, e.g. for the 'sshd' class:
20
+
21
+ $ cat spec/classes/sshd_config_spec.rb
22
+ describe 'sshd' do
23
+ it 'should have an augeas resource' do
24
+ should contain_augeas('root login')
25
+ end
26
+
27
+ # Expects Augeas['root login']
28
+ describe_augeas 'root login' do
29
+ it 'should change PermitRootLogin' do
30
+ # Run the resource against the fixtures, check it changed
31
+ should execute.with_change
32
+
33
+ # Check changes in the file with aug_get and aug_match
34
+ aug_get('PermitRootLogin').should == 'no'
35
+
36
+ # Verify idempotence last to prevent false positive
37
+ should execute.idempotently
38
+ end
39
+ end
40
+ end
41
+
42
+ Copy original input files to `spec/fixtures/augeas` using the same filesystem
43
+ layout that the resource expects:
44
+
45
+ $ tree spec/fixtures/augeas/
46
+ spec/fixtures/augeas/
47
+ `-- etc
48
+ `-- ssh
49
+ `-- sshd_config
50
+
51
+
52
+ Lastly, in your `spec/spec_helper.rb`, load ruby-puppet-augeas and configure the
53
+ fixtures directory.
54
+
55
+ require 'rspec-puppet-augeas'
56
+ RSpec.configure do |c|
57
+ c.augeas_fixtures = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'augeas')
58
+ end
59
+
60
+ ## Usage
61
+
62
+ ### describe\_augeas example group
63
+
64
+ `describe_augeas` adds an example group, like describe/context, but that describes
65
+ an Augeas resource from the catalog. The description given to run\_augeas must
66
+ match the resource title. `run_augeas` is a synonym.
67
+
68
+ It takes optional hash arguments:
69
+
70
+ * `:fixtures` manages the files to run the resource against
71
+ * a hash of fixtures to copy from the source (under augeas\_fixtures) to a
72
+ destination path, e.g. `{ 'dest/file/location' => 'src/file/location' }`
73
+ * a string of the source path, copied to the path given by the resource's
74
+ `incl` parameter (TODO: or `:target`)
75
+ * nil, by default copies all fixtures
76
+ * `:target` is the path of the file that the resource should modify
77
+ * `:lens` is the lens to use when opening the target file (for `aug_*` etc.)
78
+
79
+ It sets the following variables inside examples:
80
+
81
+ * `subject` (used implicitly) to an object representing the resource
82
+ * `output_root` to the path of the fixtures directory after one run
83
+
84
+ ### execute matcher
85
+
86
+ The `execute` matcher is used to check how a resource has run, e.g.
87
+
88
+ subject.should execute
89
+
90
+ (subject is implicit and so can be left out)
91
+
92
+ It has methods to add to the checks it performs:
93
+
94
+ * `with_change` ensures the resource was "applied" and didn't no-op
95
+ * `idempotently` runs the resource again to ensure it only applies once
96
+
97
+ ### Test utilities
98
+
99
+ Helpers are provided to check the modifications made to files after applying
100
+ the resource. Some require certain options, which can be supplied in the
101
+ `describe_augeas` statement or as arguments to the method.
102
+
103
+ * `output_root` returns the root directory of the modified fixtures
104
+ * `open_target` opens the target file and returns the handle, closes it too if
105
+ given a block (expects `:target` option)
106
+ * `aug_open` opens the target file and returns an Augeas object, closes it too
107
+ if given a block (expects `:target` and `:lens`)
108
+ * `aug_get(path)` runs `Augeas#get(path)` against the target file (expects
109
+ `:target` and `:lens`), returns the value of the node
110
+ * `aug_match(path)` runs `Augeas#match(path)` against the target file (expects
111
+ `:target` and `:lens`), returns an array of matches
112
+
113
+ ### RSpec configuration
114
+
115
+ New RSpec configuration options:
116
+
117
+ * `augeas_fixtures` is the path to the root of the fixtures directory
118
+ containing source files
119
+
120
+ ## Issues
121
+
122
+ Please file any issues or suggestions [on GitHub](https://github.com/domcleal/rspec-puppet-augeas/issues).
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => :test
5
+ task :spec => :test
6
+
7
+ RSpec::Core::RakeTask.new(:test)
@@ -0,0 +1,15 @@
1
+ require 'rspec-puppet'
2
+
3
+ module RSpec::Puppet::Augeas
4
+ class Error < StandardError
5
+ end
6
+ end
7
+
8
+ require 'rspec-puppet-augeas/example'
9
+ require 'rspec-puppet-augeas/matchers'
10
+ require 'rspec-puppet-augeas/test_utils'
11
+
12
+ RSpec.configure do |c|
13
+ c.add_setting :augeas_fixtures, :default => nil
14
+ c.include RSpec::Puppet::Augeas::TestUtils, :type => :augeas
15
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec-puppet-augeas/example/run_augeas_example_group'
2
+
3
+ RSpec::configure do |c|
4
+ c.extend RSpec::Puppet::Augeas::RunAugeasExampleGroup::ClassMethods
5
+ c.include RSpec::Puppet::Augeas::RunAugeasExampleGroup::InstanceMethods, :type => :augeas
6
+ end
@@ -0,0 +1,65 @@
1
+ require 'rspec-puppet-augeas/resource'
2
+
3
+ module RSpec::Puppet::Augeas
4
+ module RunAugeasExampleGroup
5
+
6
+ module ClassMethods
7
+ # new example group, much like 'describe'
8
+ # title (arg #1) must match title of Augeas resource
9
+ # args may be hash containing:
10
+ # :fixture =>
11
+ # String -> relative path of source fixture file
12
+ # Hash -> { "/dest/path" => "source/fixture/path", ... }
13
+ # :target => path of destination file to be modified
14
+ # :lens => lens used for opening target
15
+ def run_augeas(*args, &block)
16
+ options = args.last.is_a?(::Hash) ? args.pop : {}
17
+
18
+ title = "Augeas[#{args.shift}]"
19
+ describe(title, *args, :type => :augeas) do
20
+ # inside here (the type augeas block), subject will be initialised
21
+ # to the augeas resource object
22
+
23
+ # initialise arguments passed into the run_augeas block
24
+ # TODO: target can be initialised from incl if available
25
+ target = options.delete(:target)
26
+ let(:target) { target }
27
+
28
+ # TODO: ditto
29
+ lens = options.delete(:lens)
30
+ let(:lens) { lens }
31
+
32
+ fixture = options.delete(:fixture)
33
+ fixture = { target => fixture } if fixture.is_a? String and target
34
+ let(:fixture) { fixture }
35
+
36
+ class_exec(&block)
37
+ end
38
+ end
39
+
40
+ # Synonym for run_augeas
41
+ def describe_augeas(*args, &block)
42
+ run_augeas(*args, &block)
43
+ end
44
+ end
45
+
46
+ module InstanceMethods
47
+ # Initialises the implicit example group 'subject' to an Augeas resource
48
+ #
49
+ # Requires that the title of this example group is the resource title and
50
+ # that the parent example group subject is a catalog (use rspec-puppet)
51
+ def subject
52
+ unless @resource
53
+ title = self.class.description
54
+ title = $1 if title =~ /^Augeas\[(.*)\]$/
55
+ @resource = Resource.new(catalogue.resource('Augeas', title), fixture)
56
+ end
57
+ @resource
58
+ end
59
+
60
+ def output_root
61
+ subject.root
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,59 @@
1
+ require 'augeas'
2
+ require 'tempfile'
3
+ require 'tmpdir'
4
+
5
+ module RSpec::Puppet::Augeas
6
+ module Fixtures
7
+ # Copies test fixtures to a temporary directory
8
+ # If file is nil, copies the entire augeas_fixtures directory
9
+ # If file is a string, it copies that file from augeas_fixtures
10
+ # to the path being edited by the resource
11
+ # If file is a hash, it copies the "value" from augeas_fixtures
12
+ # to each "key" path
13
+ def load_fixtures(resource, file)
14
+ if block_given?
15
+ Dir.mktmpdir("rspec-puppet-augeas") do |dir|
16
+ prepare_fixtures(dir, resource, file)
17
+ yield dir
18
+ end
19
+ else
20
+ dir = Dir.mktmpdir("rspec-puppet-augeas")
21
+ prepare_fixtures(dir, resource, file)
22
+ dir
23
+ end
24
+ end
25
+
26
+ def prepare_fixtures(dir, resource, file)
27
+ if file.nil?
28
+ FileUtils.cp_r File.join(RSpec.configuration.augeas_fixtures, "."), dir
29
+ elsif file.is_a? Hash
30
+ file.each do |dest,src|
31
+ FileUtils.mkdir_p File.join(dir, File.dirname(dest))
32
+ src = File.join(RSpec.configuration.augeas_fixtures, src) unless src.start_with? File::SEPARATOR
33
+ FileUtils.cp_r src, File.join(dir, dest)
34
+ end
35
+ elsif file.respond_to? :to_s
36
+ target = get_resource_target(resource)
37
+ raise ArgumentError, "Unable to determine file being edited from #{resource.name}. Supply :fixtures as a hash of { '/dest/path' => 'source/fixture/path' } instead." unless target
38
+ FileUtils.mkdir_p File.join(dir, File.dirname(target))
39
+ FileUtils.cp File.join(RSpec.configuration.augeas_fixtures, file.to_s), File.join(dir, target)
40
+ end
41
+ end
42
+
43
+ # Take a best guess at the file the user's editing
44
+ def get_resource_target(resource)
45
+ return resource[:incl] if resource[:incl]
46
+ # TODO: make reliable
47
+ #return $1 if resource[:context] and resource[:context] =~ %r{/files(/.*)}
48
+ nil
49
+ end
50
+
51
+ # Runs a particular resource via a catalog
52
+ def apply(resource)
53
+ catalog = Puppet::Resource::Catalog.new
54
+ catalog.add_resource resource
55
+ catalog = catalog.to_ral if resource.is_a? Puppet::Resource
56
+ catalog.apply
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec-puppet-augeas/matchers/execute'
2
+
3
+ RSpec::configure do |c|
4
+ c.include RSpec::Puppet::Augeas::Matchers, :type => :augeas
5
+ end
@@ -0,0 +1,70 @@
1
+ require 'rspec-puppet-augeas/fixtures'
2
+
3
+ module RSpec::Puppet::Augeas::Matchers
4
+ # <subject>.should execute()
5
+ # where subject must be an Augeas resource
6
+ class Execute
7
+ attr_reader :resource, :idempotent, :change
8
+
9
+ def initialize
10
+ @change = false
11
+ @idempotent = false
12
+ end
13
+
14
+ def matches?(resource)
15
+ @resource = resource
16
+ return false if resource.txn.any_failed?
17
+ return false if change and !resource.txn.changed?.any?
18
+ return false if idempotent and resource.idempotent.changed?.any?
19
+ true
20
+ end
21
+
22
+ # verifies the resource was 'applied'
23
+ def with_change
24
+ @change = true
25
+ self
26
+ end
27
+
28
+ # verifies the resource only applies once
29
+ def idempotently
30
+ @change = true
31
+ @idempotent = true
32
+ self
33
+ end
34
+
35
+ def description
36
+ if idempotent
37
+ "should change once only (idempotently)"
38
+ elsif change
39
+ "should change successfully at least once"
40
+ else
41
+ "should execute without failure"
42
+ end
43
+ end
44
+
45
+ def failure_message_for_should
46
+ # FIXME: the branch should depend on outcome, not chaining
47
+ if idempotent
48
+ "#{resource} isn't idempotent, it changes on every run"
49
+ elsif change
50
+ "#{resource} doesn't change when executed"
51
+ else
52
+ "#{resource} fails when executed"
53
+ end
54
+ end
55
+
56
+ def failure_message_for_should_not
57
+ if idempotent
58
+ "#{resource} is idempotent, it doesn't change on every run"
59
+ elsif change
60
+ "#{resource} changes when executed"
61
+ else
62
+ "#{resource} succeeds when executed"
63
+ end
64
+ end
65
+ end
66
+
67
+ def execute
68
+ Execute.new
69
+ end
70
+ end
@@ -0,0 +1,45 @@
1
+ require 'rspec-puppet-augeas/fixtures'
2
+
3
+ module RSpec::Puppet::Augeas
4
+ class Resource
5
+ attr_reader :resource, :txn, :txn_idempotent, :root
6
+
7
+ def initialize(resource, fixtures)
8
+ @resource = resource
9
+
10
+ # The directory where the resource has run will be valuable, so keep it
11
+ # for analysis and tests by the user
12
+ @root = load_fixtures(resource, fixtures)
13
+ ObjectSpace.define_finalizer(self, self.class.finalize(@root))
14
+
15
+ resource[:root] = @root
16
+ @txn = apply(resource)
17
+ end
18
+
19
+ def self.finalize(root)
20
+ proc { FileUtils.rm_rf root }
21
+ end
22
+
23
+ # Run the resource a second time, against the output dir from the first
24
+ #
25
+ # @return [Puppet::Transaction] repeated transaction
26
+ def idempotent
27
+ root = load_fixtures(resource, {"." => "#{@root}/."})
28
+
29
+ oldroot = resource[:root]
30
+ resource[:root] = root
31
+ @txn_idempotent = apply(resource)
32
+ FileUtils.rm_r root
33
+ resource[:root] = oldroot
34
+
35
+ @txn_idempotent
36
+ end
37
+
38
+ def to_s
39
+ resource.to_s
40
+ end
41
+
42
+ private
43
+ include RSpec::Puppet::Augeas::Fixtures
44
+ end
45
+ end
@@ -0,0 +1,148 @@
1
+ require 'augeas'
2
+ require 'tempfile'
3
+
4
+ module RSpec::Puppet::Augeas
5
+ module TestUtils
6
+ def open_target(opts = {})
7
+ file = opts[:target] || self.target or raise ArgumentError, ":target must be supplied"
8
+ f = File.open(File.join(self.output_root, file))
9
+ return f unless block_given?
10
+ yield f
11
+ f.close
12
+ end
13
+
14
+ def aug_get(path, opts = {})
15
+ aug_open(opts) do |aug|
16
+ aug.get(path)
17
+ end
18
+ end
19
+
20
+ def aug_match(path, opts = {})
21
+ aug_open(opts) do |aug|
22
+ aug.match(path)
23
+ end
24
+ end
25
+
26
+ # Open Augeas on a given file, by default the target / lens specified in
27
+ # options to the run_augeas block
28
+ def aug_open(opts = {})
29
+ file = opts[:target] || self.target or raise ArgumentError, ":target must be supplied"
30
+ file = "/#{file}" unless file.start_with? '/'
31
+ lens = opts[:lens] || self.lens or raise ArgumentError, ":lens must be supplied"
32
+ lens = "#{lens}.lns" unless lens.include? '.'
33
+
34
+ aug = Augeas.open(self.output_root, nil, Augeas::NO_MODL_AUTOLOAD)
35
+ begin
36
+ aug.transform(
37
+ :lens => lens,
38
+ :name => lens.split(".")[0],
39
+ :incl => file
40
+ )
41
+ aug.set("/augeas/context", "/files#{file}")
42
+ aug.load!
43
+ raise RSpec::Puppet::Augeas::Error, "Augeas didn't load #{file}" if aug.match(".").empty?
44
+ yield aug
45
+ rescue Augeas::Error
46
+ errors = []
47
+ aug.match("/augeas//error").each do |errnode|
48
+ aug.match("#{errnode}/*").each do |subnode|
49
+ subvalue = aug.get(subnode)
50
+ errors << "#{subnode} = #{subvalue}"
51
+ end
52
+ end
53
+ raise RSpec::Puppet::Augeas::Error, errors.join("\n")
54
+ ensure
55
+ aug.close
56
+ end
57
+ end
58
+
59
+ # Creates a simple test file, reads in a fixture (that's been modified by
60
+ # the provider) and runs augparse against the expected tree.
61
+ def augparse(opts = {})
62
+ file = opts[:target] || self.target or raise ArgumentError, ":target must be supplied"
63
+ file = File.join(self.output_path, file) unless file.start_with? '/'
64
+ lens = opts[:lens] || self.lens or raise ArgumentError, ":lens must be supplied"
65
+ lens = "#{lens}.lns" unless lens.include? '.'
66
+ result = opts[:result] || "?"
67
+
68
+ Dir.mktmpdir { |dir|
69
+ # Augeas always starts with a blank line when creating new files, so
70
+ # reprocess file and remove it to make writing tests easier
71
+ File.open("#{dir}/input", "w") do |finput|
72
+ File.open(file, "r") do |ffile|
73
+ line = ffile.readline
74
+ finput.write line unless line == "\n"
75
+ ffile.each {|line| finput.write line }
76
+ end
77
+ end
78
+
79
+ # Test module, Augeas reads back in the input file
80
+ testaug = "#{dir}/test_augeasproviders.aug"
81
+ File.open(testaug, "w") { |tf|
82
+ tf.write(<<eos)
83
+ module Test_Augeasproviders =
84
+ test #{lens} get Sys.read_file "#{dir}/input" =
85
+ #{result}
86
+ eos
87
+ }
88
+
89
+ output = %x(augparse #{testaug} 2>&1)
90
+ raise RSpec::Puppet::Augeas::Error, "augparse failed:\n#{output}" unless $? == 0 && output.empty?
91
+ }
92
+ end
93
+
94
+ # Takes a full fixture file, loads it in Augeas, uses the relative path
95
+ # and/or filter and saves just that part in a new file. That's then passed
96
+ # into augparse and compared against the expected tree. Saves creating a
97
+ # tree of the entire file.
98
+ #
99
+ # Because the filtered fragment is saved in a new file, seq labels will reset
100
+ # too, so it'll be "1" rather than what it was in the original fixture.
101
+ def augparse_filter(opts = {})
102
+ file = opts[:target] || self.target or raise ArgumentError, ":target must be supplied"
103
+ file = File.join(self.output_path, file) unless file.start_with? '/'
104
+ lens = opts[:lens] || self.lens or raise ArgumentError, ":lens must be supplied"
105
+ lens = "#{lens}.lns" unless lens.include? '.'
106
+ filter = opts[:filter] or raise ArgumentError, ":filter must be supplied"
107
+ result = opts[:result] || "?"
108
+
109
+ # duplicate the original since we use aug.mv
110
+ tmpin = Tempfile.new("original")
111
+ tmpin.write(File.read(file))
112
+ tmpin.close
113
+
114
+ tmpout = Tempfile.new("filtered")
115
+ tmpout.close
116
+
117
+ aug_open(tmpin.path, lens) do |aug|
118
+ # Load a transform of the target, so Augeas can write into it
119
+ aug.transform(
120
+ :lens => lens,
121
+ :name => lens.split(".")[0],
122
+ :incl => tmpout.path
123
+ )
124
+ aug.load!
125
+ tmpaug = "/files#{tmpout.path}"
126
+ raise RSpec::Puppet::Augeas::Error, "Augeas didn't load empty file #{tmpout.path}" if aug.match(tmpaug).empty?
127
+
128
+ # Check the filter matches something and move it
129
+ ftmatch = aug.match(filter)
130
+ raise RSpec::Puppet::Augeas::Error, "Filter #{filter} within #{file} matched #{ftmatch.size} nodes, should match at least one" if ftmatch.empty?
131
+
132
+ begin
133
+ # Loop on aug_match as path indexes will change as we move nodes
134
+ fp = ftmatch.first
135
+ aug.mv(fp, "#{tmpaug}/#{fp.split(/\//)[-1]}")
136
+ ftmatch = aug.match(filter)
137
+ end while not ftmatch.empty?
138
+
139
+ aug.save!
140
+ end
141
+
142
+ augparse(tmpout.path, lens, result)
143
+ ensure
144
+ tmpin.unlink
145
+ tmpout.unlink
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,35 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'rspec-puppet-augeas'
3
+ s.version = '0.1.0'
4
+ s.homepage = 'https://github.com/domcleal/rspec-puppet-augeas/'
5
+ s.summary = 'RSpec tests for Augeas resources in Puppet manifests'
6
+ s.description = 'RSpec tests for Augeas resources in Puppet manifests'
7
+
8
+ s.files = [
9
+ '.gitignore',
10
+ 'Gemfile',
11
+ 'LICENSE',
12
+ 'README.md',
13
+ 'Rakefile',
14
+ 'lib/rspec-puppet-augeas.rb',
15
+ 'lib/rspec-puppet-augeas/example.rb',
16
+ 'lib/rspec-puppet-augeas/example/run_augeas_example_group.rb',
17
+ 'lib/rspec-puppet-augeas/fixtures.rb',
18
+ 'lib/rspec-puppet-augeas/matchers.rb',
19
+ 'lib/rspec-puppet-augeas/matchers/execute.rb',
20
+ 'lib/rspec-puppet-augeas/resource.rb',
21
+ 'lib/rspec-puppet-augeas/test_utils.rb',
22
+ 'rspec-puppet-augeas.gemspec',
23
+ 'spec/classes/sshd_config_spec.rb',
24
+ 'spec/fixtures/augeas/etc/ssh/sshd_config',
25
+ 'spec/fixtures/augeas/etc/ssh/sshd_config_2',
26
+ 'spec/fixtures/manifests/site.pp',
27
+ 'spec/fixtures/modules/sshd/manifests/init.pp',
28
+ 'spec/spec_helper.rb'
29
+ ]
30
+
31
+ s.add_dependency 'rspec-puppet'
32
+
33
+ s.authors = ['Dominic Cleal']
34
+ s.email = 'dcleal@redhat.com'
35
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'sshd' do
4
+ it 'should have an augeas resource' do
5
+ should contain_augeas('root login')
6
+ end
7
+
8
+ describe 'specify target+lens upfront, use all fixtures' do
9
+ describe_augeas 'root login', :lens => 'Sshd', :target => 'etc/ssh/sshd_config' do
10
+ it 'should test resource' do
11
+ aug_get('#comment[1]').should =~ /OpenBSD/
12
+ open_target { |f| f.readline.should =~ /OpenBSD/ }
13
+
14
+ should execute.with_change
15
+ aug_get('PermitRootLogin').should == 'yes'
16
+ open_target { |f| f.read.should =~ /^PermitRootLogin\s+yes$/ }
17
+
18
+ should execute.idempotently
19
+ end
20
+ end
21
+ end
22
+
23
+ describe 'specify fixtures as a hash' do
24
+ describe_augeas 'root login', :fixture => { 'etc/ssh/sshd_config' => 'etc/ssh/sshd_config_2' } do
25
+ it 'should test resource with second fixture' do
26
+ aug_get('#comment[1]', :lens => 'Sshd', :target => 'etc/ssh/sshd_config').should == 'Fixture 2'
27
+ should execute.with_change
28
+ aug_get('PermitRootLogin', :lens => 'Sshd', :target => 'etc/ssh/sshd_config').should == 'yes'
29
+ should execute.idempotently
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'specify target and non-standard fixture' do
35
+ describe_augeas 'root login', :lens => 'Sshd', :target => 'etc/ssh/sshd_config', :fixture => 'etc/ssh/sshd_config_2' do
36
+ it 'should test resource with second fixture' do
37
+ aug_get('#comment[1]').should == 'Fixture 2'
38
+ should execute.with_change
39
+ aug_get('PermitRootLogin').should == 'yes'
40
+ should execute.idempotently
41
+ end
42
+ end
43
+ end
44
+
45
+ describe_augeas 'fail to add root login' do
46
+ it 'should fail to run entirely' do
47
+ should_not execute
48
+ end
49
+ end
50
+
51
+ run_augeas 'add root login', :lens => 'Sshd', :target => 'etc/ssh/sshd_config' do
52
+ it 'should fail on idempotency' do
53
+ should execute.with_change
54
+ aug_match('PermitRootLogin').size.should == 2
55
+ should_not execute.idempotently
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,138 @@
1
+ # $OpenBSD: sshd_config,v 1.81 2009/10/08 14:03:41 markus Exp $
2
+
3
+ # This is the sshd server system-wide configuration file. See
4
+ # sshd_config(5) for more information.
5
+
6
+ # This sshd was compiled with PATH=/usr/local/bin:/bin:/usr/bin
7
+
8
+ # The strategy used for options in the default sshd_config shipped with
9
+ # OpenSSH is to specify options with their default value where
10
+ # possible, but leave them commented. Uncommented options change a
11
+ # default value.
12
+
13
+ #Port 22
14
+ #AddressFamily any
15
+ #ListenAddress 0.0.0.0
16
+ #ListenAddress ::
17
+
18
+ # The default requires explicit activation of protocol 1
19
+ #Protocol 2
20
+
21
+ # HostKey for protocol version 1
22
+ #HostKey /etc/ssh/ssh_host_key
23
+ # HostKeys for protocol version 2
24
+ #HostKey /etc/ssh/ssh_host_rsa_key
25
+ #HostKey /etc/ssh/ssh_host_dsa_key
26
+
27
+ # Lifetime and size of ephemeral version 1 server key
28
+ #KeyRegenerationInterval 1h
29
+ #ServerKeyBits 1024
30
+
31
+ # Logging
32
+ # obsoletes QuietMode and FascistLogging
33
+ #SyslogFacility AUTH
34
+ SyslogFacility AUTHPRIV
35
+ #LogLevel INFO
36
+
37
+ # Authentication:
38
+
39
+ AllowGroups sshusers
40
+
41
+ #LoginGraceTime 2m
42
+ PermitRootLogin without-password
43
+ #StrictModes yes
44
+ #MaxAuthTries 6
45
+ #MaxSessions 10
46
+
47
+ #RSAAuthentication yes
48
+ #PubkeyAuthentication yes
49
+ #AuthorizedKeysFile .ssh/authorized_keys
50
+ #AuthorizedKeysCommand none
51
+ #AuthorizedKeysCommandRunAs nobody
52
+
53
+ # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
54
+ #RhostsRSAAuthentication no
55
+ # similar for protocol version 2
56
+ #HostbasedAuthentication no
57
+ # Change to yes if you don't trust ~/.ssh/known_hosts for
58
+ # RhostsRSAAuthentication and HostbasedAuthentication
59
+ #IgnoreUserKnownHosts no
60
+ # Don't read the user's ~/.rhosts and ~/.shosts files
61
+ #IgnoreRhosts yes
62
+
63
+ # To disable tunneled clear text passwords, change to no here!
64
+ #PasswordAuthentication yes
65
+ #PermitEmptyPasswords no
66
+ PasswordAuthentication yes
67
+
68
+ # Change to no to disable s/key passwords
69
+ #ChallengeResponseAuthentication yes
70
+ ChallengeResponseAuthentication no
71
+
72
+ # Kerberos options
73
+ #KerberosAuthentication no
74
+ #KerberosOrLocalPasswd yes
75
+ #KerberosTicketCleanup yes
76
+ #KerberosGetAFSToken no
77
+ #KerberosUseKuserok yes
78
+
79
+ # GSSAPI options
80
+ #GSSAPIAuthentication no
81
+ GSSAPIAuthentication yes
82
+ #GSSAPICleanupCredentials yes
83
+ GSSAPICleanupCredentials yes
84
+ #GSSAPIStrictAcceptorCheck yes
85
+ #GSSAPIKeyExchange no
86
+
87
+ # Set this to 'yes' to enable PAM authentication, account processing,
88
+ # and session processing. If this is enabled, PAM authentication will
89
+ # be allowed through the ChallengeResponseAuthentication and
90
+ # PasswordAuthentication. Depending on your PAM configuration,
91
+ # PAM authentication via ChallengeResponseAuthentication may bypass
92
+ # the setting of "PermitRootLogin without-password".
93
+ # If you just want the PAM account and session checks to run without
94
+ # PAM authentication, then enable this but set PasswordAuthentication
95
+ # and ChallengeResponseAuthentication to 'no'.
96
+ #UsePAM no
97
+ UsePAM yes
98
+
99
+ # Accept locale-related environment variables
100
+ AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
101
+ AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
102
+ AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
103
+ AcceptEnv XMODIFIERS
104
+
105
+ #AllowAgentForwarding yes
106
+ #AllowTcpForwarding yes
107
+ #GatewayPorts no
108
+ #X11Forwarding no
109
+ X11Forwarding yes
110
+ #X11DisplayOffset 10
111
+ #X11UseLocalhost yes
112
+ #PrintMotd yes
113
+ #PrintLastLog yes
114
+ #TCPKeepAlive yes
115
+ #UseLogin no
116
+ #UsePrivilegeSeparation yes
117
+ #PermitUserEnvironment no
118
+ #Compression delayed
119
+ #ClientAliveInterval 0
120
+ #ClientAliveCountMax 3
121
+ #ShowPatchLevel no
122
+ #UseDNS yes
123
+ #PidFile /var/run/sshd.pid
124
+ #MaxStartups 10
125
+ #PermitTunnel no
126
+ #ChrootDirectory none
127
+
128
+ # no default banner path
129
+ #Banner none
130
+
131
+ # override default of no subsystems
132
+ Subsystem sftp /usr/libexec/openssh/sftp-server
133
+
134
+ # Example of overriding settings on a per-user basis
135
+ #Match User anoncvs
136
+ # X11Forwarding no
137
+ # AllowTcpForwarding no
138
+ # ForceCommand cvs server
@@ -0,0 +1,2 @@
1
+ # Fixture 2
2
+ PermitRootLogin no
File without changes
@@ -0,0 +1,19 @@
1
+ class sshd {
2
+ augeas { "root login":
3
+ context => '/files/etc/ssh/sshd_config',
4
+ changes => 'set PermitRootLogin yes',
5
+ }
6
+
7
+ augeas { "add root login":
8
+ context => '/files/etc/ssh/sshd_config',
9
+ changes => [
10
+ 'ins PermitRootLogin after *[last()]',
11
+ 'set PermitRootLogin[last()] yes'
12
+ ],
13
+ }
14
+
15
+ augeas { "fail to add root login":
16
+ context => '/files/etc/ssh/sshd_config',
17
+ changes => 'ins PermitRootLogin after *[last()]',
18
+ }
19
+ }
@@ -0,0 +1,7 @@
1
+ require 'rspec-puppet-augeas'
2
+
3
+ RSpec.configure do |c|
4
+ c.augeas_fixtures = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'augeas')
5
+ c.module_path = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'modules')
6
+ c.manifest_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'manifests')
7
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-puppet-augeas
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dominic Cleal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec-puppet
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: RSpec tests for Augeas resources in Puppet manifests
31
+ email: dcleal@redhat.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - LICENSE
39
+ - README.md
40
+ - Rakefile
41
+ - lib/rspec-puppet-augeas.rb
42
+ - lib/rspec-puppet-augeas/example.rb
43
+ - lib/rspec-puppet-augeas/example/run_augeas_example_group.rb
44
+ - lib/rspec-puppet-augeas/fixtures.rb
45
+ - lib/rspec-puppet-augeas/matchers.rb
46
+ - lib/rspec-puppet-augeas/matchers/execute.rb
47
+ - lib/rspec-puppet-augeas/resource.rb
48
+ - lib/rspec-puppet-augeas/test_utils.rb
49
+ - rspec-puppet-augeas.gemspec
50
+ - spec/classes/sshd_config_spec.rb
51
+ - spec/fixtures/augeas/etc/ssh/sshd_config
52
+ - spec/fixtures/augeas/etc/ssh/sshd_config_2
53
+ - spec/fixtures/manifests/site.pp
54
+ - spec/fixtures/modules/sshd/manifests/init.pp
55
+ - spec/spec_helper.rb
56
+ homepage: https://github.com/domcleal/rspec-puppet-augeas/
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: RSpec tests for Augeas resources in Puppet manifests
80
+ test_files: []