Assert things about a list of files

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 }

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?

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: add directories resource · Issue #709 · inspec/inspec · GitHub

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' }

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: learningchef-video/chefdk_version.rb at master · learningchef/learningchef-video · GitHub

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]

  def major_version

  def minor_version

  def patch_version

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.

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.

1 Like

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

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:

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.

1 Like