Showing posts with label rspec-puppet. Show all posts
Showing posts with label rspec-puppet. Show all posts

Monday, February 12, 2024

Installing gems in PDK on airgapped/offline system without functional rubygems.org

 I ran into a problem recently installing gems in an offline system... I am not understanding something about how the `pdk bundle` setup creates it's cache directories.


If you try to do a `pdk bundle exec gem install ~/path/to/module.gem`, it will place it's cache files in ~/.gem/ruby, but the PDK expects it's files in ~/.pdk/cache/ruby/


I followed these basic steps to get it 'working' enough...

  1. Copy over .gem file and it's dependencies to the offline system to be able to do a local `gem install`
  2. Install it using `pdk bundle gem install ~/gems/*`
  3. Copy over the installed gems from ~/.gem to ~/.pdk/cache - `cp --no-clobber ~/.gem/ruby ~/.pdk/cache/`
  4. Update your Gemfile (in your PDK base directory) to include the gems you want to local install
  5. `pdk bundle install --local` to install the gems from the gemfile, but source them from the cache that you just manually populated.  
I'd love to know if there is a better way to do this!

Compare a whole file content with rspec-puppet

 I was attempting to use the entire body of a file for a quick and dirty 'golden master' style test while I was refactoring some things.

I ran into a problem with having a multi-line string representing the full body of my output - I would run into this problem after putting in something like the following( https://github.com/puppetlabs/rspec-puppet/issues/100 ):

Failure/Error: it { is expected.to contain_file('/mock/file.sh').with_content(multiline_content) }
  expected that the catalogue would contain File[/mock/file.sh] with content set to supplied string
  Diff: 
    <The diff is empty, are your objects producing identical '#inspect' output?>

And my test looked something like this ...

my_content=<<-CONTENT.chomp
file line one
file line two
file last line
CONTENT

on_supported_os.each do |os, os_facts|
  context "on #{os}" do
  it { is_expected.to contain_file('/mock/file.sh').with_content(my_content) }
  end
end

Since I was unable to figure out a 'proper' solution (with and without the chomp) for this problem, I did the next best thing - I simply looped over each line of the file input, and made sure all the lines were in the content - this could give a false 'passing' as it doesn't prevent extra lines from being in there, or the line order isn't tracked, but it was 'good enough' for my short purpose

my_content=<<-CONTENT.chomp
file line one
file line two
file last line
CONTENT

on_supported_os.each do |os, os_facts|
  context "on #{os}" do
  it {
    my_content.each_line do |line|
      is_expected.to contain_file('/mock/file.sh').with_content(/#{Regexp.quote(line)}/) 
  }
  end
end


Hope this helps someone, or perhaps someone see's this and can tell me what I'm doing wrong!

Tuesday, January 10, 2017

rspec-puppet test with array parameter

Today I stumbled onto something that was much harder than I expected it to be while trying to write an rspec-puppet test.

Say you have a resource with a parameter that is an array, and you only want to validate that the array contains an element.

In our case, say that we have the following code somewhere in our puppet manifests

        host { 'foo.local':
          ip => '1.2.3.4',
          host_aliases => [ 'foo1.local', 'foo2.local' ],
        }

It is pretty direct to validate the exact array inside of an rspec-puppet test

    it { should contain_host('foo.local').with_host_aliases( ['foo1.local', 'foo2.local'] ) }

But that requires a very specific 'expected' value to be passed into the matcher.  What if we just wanted to see if the parameter array includes a given value?  

In a conversation about his problem in the #voxpupuli channel on irc@freenode, dev_el_ops mentioned "one of the design problems of rspec-pupet is that it  doesn't expose property values to the regular rspec matchers"

Since we couldn't get the values directly from the matchers, I had to come up with a different way to approach the problem, which is not quite as readable as I would have wanted.

    it {
        host_aliases = catalogue.resource('host', 'foo.local').send(:parameters)[:host_aliases]
        expect(host_aliases).to include('foo1.local')
    }

Lets break down what is happening.  catalogue.resource('host', 'foo.local') will fetch us the catalog's resource object of type 'host' with the title 'foo.local'.  we can then use 'send' to call the private parameters method on the object to get back it's parameter hash, which we then reference the :host_aliases key to retrieve our array.  

Once we have the array (stored in host_aliases), we can then use a simple 'expect' call with an 'include' matcher to check that the given array includes the value we expect.


Here is a gist of the entire example.