itchef/cookbooks/fb_launchd/resources/default.rb (66 lines of code) (raw):
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
#
# Copyright (c) 2018-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.
#
provides :fb_launchd, :os => 'darwin'
default_action :run
# Attributes that circumvent or defeat the purpose of using launchd as a node
# API. Blacklist them so that this blows up when they're used. If you really
# want to use these, just make a launchd resource instead.
BLACKLISTED_ATTRIBUTES = %w{
label
path
}.freeze
# Plist directories in which to search for managed jobs.
PLIST_DIRECTORIES = %w{
/Library/LaunchAgents
/Library/LaunchDaemons
}.freeze
action :run do
# The prefix should look like a normal label without a trailing '.' and
# *hopefully* no globs, but let's be on the safe side...
prefix = Chef::Util::PathHelper.escape_glob(node['fb_launchd']['prefix'])
if prefix.end_with?('.')
fail "fb_launchd: prefix '#{prefix}' must not end with a trailing '.'"
end
# Delete old jobs first.
managed_plists(prefix).each do |path|
label = ::File.basename(path, '.plist')
name = label.sub(prefix + '.', '')
next if node['fb_launchd']['jobs'].include?(name)
# Delete with 'path' specified to enforce that we delete the right one.
Chef::Log.debug("fb_launchd: deleting #{label}")
launchd_resource(label, :delete, { 'path' => path })
end
# Set up current jobs.
node['fb_launchd']['jobs'].each do |name, attrs|
if attrs.keys.any? { |k| BLACKLISTED_ATTRIBUTES.include?(k) }
fail "fb_launchd[#{name}]: uses a blacklisted attribute (one of " +
"#{BLACKLISTED_ATTRIBUTES}). If you want to use them, create a " +
"'launchd' resource instead"
end
# Determine our label. The directory (/Library/LaunchDaemons or
# /Library/LaunchAgents) is determined by the label + type (which defaults
# to daemon if unspecified).
label = "#{prefix}.#{name}"
if name.start_with?(prefix)
fail "fb_launchd: The provided job label: '#{name}' cannot include " +
"the prefix: '#{prefix}' Instead, pass in the job name only: " +
"`node.default['fb_launchd']['jobs']['#{name.gsub(prefix, '')}']`"
end
# Create resource
launchd_resource(label, attrs.fetch('action', :enable), attrs)
end
end
action_class do
# Constructs a new launchd resource with label 'label' and action 'action'.
# attrs is a Hash of key/value pairs of launchd attributes and their values.
# Returns the new launchd resource.
def launchd_resource(label, action, attrs = {})
Chef::Log.debug(
"fb_launchd: new launchd resource '#{label}' with action '#{action}' " +
"and attributes #{attrs}",
)
return unless label
res = launchd label do # ~FC022
action action.to_sym
if attrs['only_if']
only_if { attrs['only_if'].call }
end
if attrs['not_if']
not_if { attrs['not_if'].call }
end
end
attrs.each do |attribute, value|
next if ['action', 'only_if', 'not_if'].include?(attribute)
res.send(attribute.to_sym, value)
end
res
end
# Finds any managed plists in the standard launchd directories with the
# specified prefix. Returns a list of paths to the managed plists.
def managed_plists(prefix)
PLIST_DIRECTORIES.map do |dir|
plist_glob = ::File.join(::File.expand_path(dir), "*#{prefix}*.plist")
::Dir.glob(plist_glob)
end.flatten
end
end