Recently ran into an interesting issue with a tmpfs mount.

Originally, I had created a 1G tmpfs and the recipe code looked like:

size = '1G'

mount '/dev/shm' do
  device     'tmpfs'
  fstype     'tmpfs'
  options    ['rw', "size=#{size}"]
  action     [:enable, :mount]
end

Later it was bumped to 2G. When chef-client ran, only enable action was performed.

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable
    - enable tmpfs
  * mount[/dev/shm] action mount (up to date)

/etc/fstab was updated:

# grep /dev/shm /etc/fstab
tmpfs /dev/shm tmpfs rw,size=2G 0 2

But the mount was not:

# mount | grep /dev/shm
tmpfs on /dev/shm type tmpfs (rw,relatime,size=1048576k)

So :mount just ensures it is mounted, it does not verify options. There is a :remount action. Let’s try that:

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable
    - enable tmpfs
  * mount[/dev/shm] action remount
    - unmount tmpfs
    - mount tmpfs

Looks good:

# grep /dev/shm /etc/fstab
tmpfs /dev/shm tmpfs rw,size=2G 0 2

# mount | grep /dev/shm
tmpfs on /dev/shm type tmpfs (rw,relatime,size=2097152k)

But it did :unmount followed by :mount. Oh, I need to specify that :remount is supported.

mount '/dev/shm' do
  device     'tmpfs'
  fstype     'tmpfs'
  options    ['rw', "size=#{size}"]
  supports   :remount => true
  action     [:enable, :remount]
end

And ran it:

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable
    - enable tmpfs
  * mount[/dev/shm] action remount
    - remount tmpfs

So it just did a :remount, good! But on subsequent runs it did :remount again:

Recipe: wd_base::_tmpfs
  * mount[/dev/shm] action enable (up to date)
  * mount[/dev/shm] action remount
    - remount tmpfs

Looking at the mount:

# mount | grep /dev/shm
tmpfs on /dev/shm type tmpfs (rw,relatime,size=2097152k)

Ah, maybe it is relatime which is the default. Also, size is specified in k not G.

size = "#{2 * 1024 * 1024}k"

mount '/dev/shm' do
  device     'tmpfs'
  fstype     'tmpfs'
  options    ['rw', 'relatime', "size=#{size}"]
  supports   :remount => true
  action     [:enable, :remount]
end

The options match but it is still doing :remount!

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable (up to date)
  * mount[/dev/shm] action remount
    - remount tmpfs

This still not idempotent. Ohai to the rescue. I reworked the recipe a bit:

size = "#{2 * 1024 * 1024}k"

mount '/dev/shm' do
  device     'tmpfs'
  fstype     'tmpfs'
  options    ['rw', 'relatime', "size=#{size}"]
  supports   :remount => true
  action     :enable

  have = node.read('filesystem2', 'by_mountpoint', name, 'mount_options')
  if have.nil?
    # not mounted, mount
    action << :mount
  elsif have != options
    # mount options differ, remount
    action << :remount
  end
end

When the options are the same, only :enable is performed.

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable (up to date)

When the filesystem is not mounted, it will :mount because have is nil:

Recipe: base::_tmpfs
  * mount[/dev/shm] action enable (up to date)
  * mount[/dev/shm] action mount
    - mount tmpfs to /dev/shm

And if the options don’t match, it will :remount:

Recipe: wd_base::_tmpfs
  * mount[/dev/shm] action enable (up to date)
  * mount[/dev/shm] action remount
    - remount tmpfs

As you can see, sometime chef needs a little help in being idempotent.

Happy chef’ing!