cookbooks/fb_fstab/recipes/default.rb (79 lines of code) (raw):
#
# Cookbook Name:: fb_fstab
# Recipe:: default
#
# Copyright (c) 2016-present, Facebook, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
#
# This will conditionally generate our base file if we need to.
# We do this at compile time on purpose... nice and early.
FB::Fstab.generate_base_fstab
# ensure permissions
file FB::Fstab::BASE_FILENAME do
owner 'root'
group 'root'
mode '0444'
end
# We fill in defaults for most stuff if you don't specify, but there are a few
# basic things we need
whyrun_safe_ruby_block 'validate data' do
block do
uniq_devs = {}
node['fb_fstab']['mounts'].to_hash.each do |name, data|
# Handle only_if
if data['only_if']
unless data['only_if'].respond_to?(:call)
fail 'fb_fstab\'s only_if requires a Proc'
end
unless data['only_if'].call
Chef::Log.debug("fb_fstab: Not including #{name} due to only_if")
node.rm('fb_fstab', 'mounts', name)
next
end
end
# Add a default for non-required fields
unless data['opts']
node.default['fb_fstab']['mounts'][name]['opts'] = 'rw'
end
unless data['type']
node.default['fb_fstab']['mounts'][name]['type'] = 'auto'
end
# Enforce required fields
%w{mount_point device}.each do |req_field|
unless data[req_field]
fail "No #{req_field} provided for #{name}"
end
end
# Sanity checks
if data['device'] == 'tmpfs' && data['mount_point'] != '/dev/shm'
fail 'Using "tmpfs" as a device for non-/dev/shm is no longer ' +
'supported. Please use a meaningful name - the fstype will be ' +
'enough to tell the kernel it is tmpfs. Offending mount: ' +
"#{data['mount_point']}."
end
is_bind_mount = false
is_systemd_automount = false
if data['opts']
opt_list = data['opts'].split(',')
is_bind_mount = opt_list.include?('bind')
is_systemd_automount = opt_list.include?('x-systemd.automount')
end
unless ['nfs', 'nfs4', 'glusterfs', 'nfusr'].include?(
data['type'],
) || is_bind_mount
if uniq_devs[data['device']] &&
!FB::Fstab.btrfs_subvol?(data['type'], data['opts'])
fail 'Device names must be unique and you have repeated ' +
"#{data['device']} for #{uniq_devs[data['device']]} and " +
"#{data['mount_point']}. If this is a tmpfs or other virtual " +
'filesystem, please use descriptive unique names.'
end
uniq_devs[data['device']] = data['mount_point']
end
# Handle dumb
auto = FB::Fstab.autofs_parent(data['mount_point'], node)
if auto && !is_systemd_automount
fail "fb_fstab: Refusing to mount '#{name}' because the mount point " +
"(#{data['mount_point']}) is within an autofs controlled directory" +
" #{auto}"
end
end
end
end
execute 'fb_fstab-daemon-reload' do
command '/bin/systemctl daemon-reload'
action :nothing
end
template '/etc/fstab' do
source 'fstab.erb'
owner 'root'
group 'root'
mode '0644'
# On systemd hosts we use the generated mount units to mount filesystems
# so it's important we ask it to regenerate them when we edit fstab
if node.systemd?
notifies :run, 'execute[fb_fstab-daemon-reload]', :immediately
end
end
fb_fstab 'handle_mounts'