Assert things about a list of files


#1

howdy all.

I want to make assertions on files that I can’t create a list for a-priori. I need to do something like

get_list_of_files_in_dir.each do |f|
  describe file(f) it { should exist }
end

I wanted to use Dir.glob for this, but that is executed on the local machine, not the target.

Is there a way for What is considered good practice for getting the list of files in a directory on the target machine?
Thanks!


#2

AFAIK currently you have to use the command resource. I’m not aware of the inspec resource classes exposing something to list the files in a directory. Thankfully the command resource is your Swiss army knife when inspec doesn’t support something.

This is noted in the following GitHub issue: https://github.com/inspec/inspec/issues/709

An example of the workaround is provided:

command('find /lib -type f').stdout.split.each do |fname|
  describe file(fname) do
    its('owner') { should cmp 'root' }
  end
end

I also provide an example of how to scrape command output and how to package this into a custom resource, in my Learning Chef video on Safari if you have a subscription:

The example code is here: https://github.com/learningchef/learningchef-video/blob/master/working-files/chapter11/chef_workstation/libraries/chefdk_version.rb

class ChefdkVersion < Inspec.resource(1)
  name 'chefdk_version'

  def initialize
    chef_version_output = inspec.command('/opt/chefdk/bin/chef --version').stdout
    version = chef_version_output.match(/Chef Development Kit Version: (\d+\.\d+\.\d+)/)[1]
    @major_version = version.split('.')[0]
    @minor_version = version.split('.')[1]
    @patch_version = version.split('.')[2]
  end

  def major_version
    @major_version.to_i
  end

  def minor_version
    @minor_version.to_i
  end

  def patch_version
    @patch_version.to_i
  end
end

There’s mention of providing more proper support for dynamic file listings in the GitHub issue as well, but I don’t think it has made it into the product yet.


#3

P.S. By creating a custom resource, you could create your own version of a Dir resource that lists files in a directory on the remote machine (until Chef provides equivalent functionality in a prepackaged resource).

I would say that encapsulating the “remote directory listing” in a custom resource would be a recommended best practice for adding this new capability.


#4

Thanks @misheska that’s exactly what I ended up doing (going the command().stdout.split route. Great to know that this is considered the “right” way of doing things - it felt a bit dirty.

Is the “solution” plugin enabled on this discourse ? I’d like to have one of the moderators set this as a solutin


#5

You’re welcome. If it helps, a great deal of inspec itself is basically a bunch of command parsing, so it should definitely feel “right”.

Take a look at the source for the inspec resources sometime. You’ll see a lot of it: https://github.com/inspec/inspec/tree/master/lib/resources

If you search for "inspec.command", you’ll get a lot of hits.

How does inspec get a list of running services? Like so:

inspec.command('find /etc/rc*.d /etc/init.d/rc*.d -name "S*"').stdout

List of yum repos:

inspec.command('yum -v repolist all')

List of docker images:

inspec.command('docker images -a --no-trunc --format \'{ "id": {{json .ID}}, "repository": {{json .Repository}}, "tag": {{json .Tag}}, "size": {{json .Size}}, "digest": {{json .Digest}}, "createdat": {{json .CreatedAt}}, "createdsize": {{json .CreatedSince}} }\'').stdout

And so on. A lot of inspec itself is just encapsulating robust command scraping.