Chef_Processors/ChefTemplate.py (199 lines of code) (raw):
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.#
"""See docstring for ChefTemplate class."""
from __future__ import absolute_import
from autopkglib import Processor
__all__ = ["ChefTemplate"]
class ChefTemplate(Processor):
description = (
"Produces a template Chef block. See "
"https://docs.chef.io/resource_template.html."
)
input_variables = {
"resource_name": {
"required": True,
"description": (
"Name for the resource. This can be a single "
"string or an array of strings. If an array is "
"provided, the first item in the array will be the "
"resource name and the rest will be turned "
"into an array."
),
},
"action": {
"required": False,
"description": "Resource action. See documentation.",
},
"atomic_update": {
"required": False,
"description": "Perform atomic file updates on a per-resource basis.",
},
"backup": {
"required": False,
"description": "The number of backups to be kept in /var/chef/backup.",
},
"cookbook": {
"required": False,
"description": "The cookbook in which a file is located.",
},
"force_unlink": {
"required": False,
"description": (
"How the chef-client handles certain situations when "
"the target file turns out not to be a file."
),
},
"group": {
"required": False,
"description": "Use to configure permissions for directories.",
},
"helper": {"required": False, "description": "Define a helper method inline."},
"ignore_failure": {
"required": False,
"description": (
"Continue running a recipe if a resource fails for any reason."
),
},
"inherits": {
"required": False,
"description": (
"Windows only. Whether a file inherits rights from "
"its parent template."
),
},
"local": {
"required": False,
"description": "Load a template from a local path.",
},
"manage_symlink_source": {
"required": False,
"description": (
"Cause the chef-client to detect and manage the "
"source file for a symlink."
),
},
"mode": {
"required": False,
"description": (
"A quoted 3-5 character string that defines the octal mode."
),
},
"notifies": {
"required": False,
"description": (
"Which resource takes action when this resource's state changes."
),
},
"owner": {
"required": False,
"description": "Use to configure permissions for directories.",
},
"path": {"required": False, "description": "The path to the template."},
"provider": {
"required": False,
"description": "Optional. Explicitly specify a provider.",
},
"retries": {
"required": False,
"description": (
"The number of times to catch exceptions and retry the resource."
),
},
"retry_delay": {
"required": False,
"description": "The retry delay (in seconds).",
},
"rights": {
"required": False,
"description": (
"Microsoft Windows only. The permissions for users and "
"groups in a Microsoft Windows environment."
),
},
"sensitive": {
"required": False,
"description": (
"Ensure that sensitive resource data is not logged "
"by the chef-client."
),
},
"source": {
"required": True,
"description": (
"The name of the file in COOKBOOK_NAME/files/default "
"or the path to a file located in COOKBOOK_NAME/files."
),
},
"subscribes": {
"required": False,
"description": (
"Specify that this resource is to listen to another "
"resource, and then take action when that resource's "
"state changes.."
),
},
"variables": {
"required": True,
"description": (
"A Hash of variables that are passed into a Ruby template file."
),
},
"verify": {
"required": False,
"description": (
"A block or a string that returns true or false. A "
"string, when true is executed as a system command."
),
},
"only_if": {"required": False, "description": "only_if guard phrase."},
"not_if": {"required": False, "description": "not_if guard phrase."},
}
output_variables = {"chef_block": {"description": "Chef block."}}
__doc__ = description
def main(self):
extra_formatting = ""
block_name = "template"
end_text = "end\n"
self.env["chef_block"] = ""
if not isinstance(self.env["resource_name"], basestring):
# Not a string, assume it's an array of strings
each_do_beginning = "[\n"
each_do_end = "].each do |item|\n\t"
self.env["chef_block"] = each_do_beginning
for resource_name in self.env["resource_name"]:
self.env["chef_block"] += "\t%s,\n" % resource_name
self.env["chef_block"] += each_do_end
name = "item"
# insert an extra tab before everything
extra_formatting = "\t"
end_text = "\tend\nend\n"
else:
name = self.env["resource_name"]
notif_text = "\tnot_if"
onlyif_text = "\tonly_if"
input_list = sorted(self.input_variables.keys())
# Start the block
self.env["chef_block"] += "%s %s do\n" % (block_name, name)
# Place not_if guards first
if self.env.get("not_if"):
self.env["chef_block"] += "%s\t%s %s\n" % (
extra_formatting,
notif_text,
self.env["not_if"],
)
input_list.remove("not_if")
# Place only_if guards next
if self.env.get("only_if"):
self.env["chef_block"] += "%s\t%s %s\n" % (
extra_formatting,
onlyif_text,
self.env["only_if"],
)
input_list.remove("only_if")
input_list.remove("resource_name")
# Loop through all keys
for key in input_list:
if self.env.get(key, ""):
key_text = "\t%s" % key
self.env["chef_block"] += "%s\t%s %s\n" % (
extra_formatting,
key_text,
self.env[key],
)
# clear out the key so it doesn't poison future runs
self.env[key] = ""
# end it
self.env["chef_block"] += end_text
self.output("Chef block:\n%s" % self.env["chef_block"])
if __name__ == "__main__":
PROCESSOR = ChefTemplate()
PROCESSOR.execute_shell()