From 6805006207c78f48961d24336be2054095a8bd68 Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Tue, 11 Jun 2019 18:02:10 -0700 Subject: parse yaml recipes Signed-off-by: Lamont Granquist --- lib/chef/cookbook_version.rb | 39 +++++++++++++++++++++++++++++++++++++-- lib/chef/mixin/from_file.rb | 21 +++++++++++++++++++++ lib/chef/recipe.rb | 16 ++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) (limited to 'lib/chef') diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb index 4989fb8d91..ed0c199728 100644 --- a/lib/chef/cookbook_version.rb +++ b/lib/chef/cookbook_version.rb @@ -4,7 +4,7 @@ # Author:: Tim Hinderliter () # Author:: Seth Falcon () # Author:: Daniel DeLeo () -# Copyright:: Copyright 2008-2018, Chef Software Inc. +# Copyright:: Copyright 2008-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -137,6 +137,18 @@ class Chef end end + def recipe_yml_filenames_by_name + @recipe_ym_filenames_by_name ||= begin + name_map = yml_filenames_by_name(files_for("recipes")) + root_alias = cookbook_manifest.root_files.find { |record| record[:name] == "root_files/recipe.yml" } + if root_alias + Chef::Log.error("Cookbook #{name} contains both recipe.yml and and recipes/default.yml, ignoring recipes/default.yml") if name_map["default"] + name_map["default"] = root_alias[:full_path] + end + name_map + end + end + def recipe_filenames_by_name @recipe_filenames_by_name ||= begin name_map = filenames_by_name(files_for("recipes")) @@ -184,10 +196,29 @@ class Chef # called from DSL def load_recipe(recipe_name, run_context) - unless recipe_filenames_by_name.key?(recipe_name) + if recipe_filenames_by_name.key?(recipe_name) + load_ruby_recipe(recipe_name, run_context) + elsif recipe_yml_filenames_by_name.key?(recipe_name) + load_yml_recipe(recipe_name, run_context) + else raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}" end + end + def load_yml_recipe(recipe_name, run_context) + Chef::Log.trace("Found recipe #{recipe_name} in cookbook #{name}") + recipe = Chef::Recipe.new(name, recipe_name, run_context) + recipe_filename = recipe_yml_filenames_by_name[recipe_name] + + unless recipe_filename + raise Chef::Exceptions::RecipeNotFound, "could not find #{recipe_name} files for cookbook #{name}" + end + + recipe.from_yaml_file(recipe_filename) + recipe + end + + def load_ruby_recipe(recipe_name, run_context) Chef::Log.trace("Found recipe #{recipe_name} in cookbook #{name}") recipe = Chef::Recipe.new(name, recipe_name, run_context) recipe_filename = recipe_filenames_by_name[recipe_name] @@ -551,6 +582,10 @@ class Chef records.select { |record| record[:name] =~ /\.rb$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".rb")] = record[:full_path]; memo } end + def yml_filenames_by_name(records) + records.select { |record| record[:name] =~ /\.yml$/ }.inject({}) { |memo, record| memo[File.basename(record[:name], ".yml")] = record[:full_path]; memo } + end + def file_vendor unless @file_vendor @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(cookbook_manifest) diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb index b8300340c7..ba034d331e 100644 --- a/lib/chef/mixin/from_file.rb +++ b/lib/chef/mixin/from_file.rb @@ -17,6 +17,8 @@ # limitations under the License. # +require "erb" + class Chef module Mixin module FromFile @@ -37,6 +39,25 @@ class Chef end end + # This will return an array of hashes or something that then needs to get inflated. + def from_yaml_file(filename) + self.source_file = filename + if File.file?(filename) && File.readable?(filename) + tpl = ERB.new(IO.read(filename)) + tpl.filename = filename + res = ::YAML.safe_load(tpl.result) + if res.is_a?(Hash) + from_hash(res) + elsif res.is_a?(Array) + from_array(res) + else + raise "boom" + end + else + raise IOError, "Cannot open or read #{filename}!" + end + end + # Loads a given ruby file, and runs class_eval against it in the context of the current # object. # diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb index 154072827e..95b69532f8 100644 --- a/lib/chef/recipe.rb +++ b/lib/chef/recipe.rb @@ -85,6 +85,22 @@ class Chef end end + def from_array(array) + array.each { |e| from_hash(e) } + end + + def from_hash(hash) + hash["resources"].each do |rhash| + type = rhash.delete("type").to_sym + name = rhash.delete("name") + res = declare_resource(type, name) + rhash.each do |key, value| + # FIXME?: we probably need a way to instance_exec a string that contains block code against the property? + res.send(key, value) + end + end + end + def to_s "cookbook: #{cookbook_name ? cookbook_name : "(none)"}, recipe: #{recipe_name ? recipe_name : "(none)"} " end -- cgit v1.2.1 From 361edd250687dbe93587a659c1fc93baf35be000 Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Wed, 12 Jun 2019 17:30:54 -0700 Subject: remove erb parsing if we do this we open the door to arbitrary ruby which is going to be context dependent and it will eliminate the possibility of ever autoconverting from YAML to ruby. if we build up a YAML structure we can better ensure that there's a static mapping of the YAML code to ruby for autoconversion. Signed-off-by: Lamont Granquist --- lib/chef/mixin/from_file.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lib/chef') diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb index ba034d331e..94e3144f9f 100644 --- a/lib/chef/mixin/from_file.rb +++ b/lib/chef/mixin/from_file.rb @@ -17,8 +17,6 @@ # limitations under the License. # -require "erb" - class Chef module Mixin module FromFile @@ -43,9 +41,7 @@ class Chef def from_yaml_file(filename) self.source_file = filename if File.file?(filename) && File.readable?(filename) - tpl = ERB.new(IO.read(filename)) - tpl.filename = filename - res = ::YAML.safe_load(tpl.result) + res = ::YAML.safe_load(IO.read(filename)) if res.is_a?(Hash) from_hash(res) elsif res.is_a?(Array) -- cgit v1.2.1 From fc25d304203409093206ab2a76698932b990641c Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Wed, 12 Jun 2019 17:40:20 -0700 Subject: code rearrangement and warn about using arrays from_yaml_file was not actually generic i don't think Signed-off-by: Lamont Granquist --- lib/chef/mixin/from_file.rb | 17 ----------------- lib/chef/recipe.rb | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'lib/chef') diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb index 94e3144f9f..b8300340c7 100644 --- a/lib/chef/mixin/from_file.rb +++ b/lib/chef/mixin/from_file.rb @@ -37,23 +37,6 @@ class Chef end end - # This will return an array of hashes or something that then needs to get inflated. - def from_yaml_file(filename) - self.source_file = filename - if File.file?(filename) && File.readable?(filename) - res = ::YAML.safe_load(IO.read(filename)) - if res.is_a?(Hash) - from_hash(res) - elsif res.is_a?(Array) - from_array(res) - else - raise "boom" - end - else - raise IOError, "Cannot open or read #{filename}!" - end - end - # Loads a given ruby file, and runs class_eval against it in the context of the current # object. # diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb index 95b69532f8..ddb45de8e3 100644 --- a/lib/chef/recipe.rb +++ b/lib/chef/recipe.rb @@ -85,7 +85,28 @@ class Chef end end + def from_yaml_file(filename) + self.source_file = filename + if File.file?(filename) && File.readable?(filename) + from_yaml(IO.read(filename)) + else + raise IOError, "Cannot open or read #{filename}!" + end + end + + def from_yaml(string) + res = ::YAML.safe_load(string) + if res.is_a?(Hash) + from_hash(res) + elsif res.is_a?(Array) + from_array(res) + else + raise "boom" + end + end + def from_array(array) + Chef::Log.warn "array yaml files are super duper experimental behavior" array.each { |e| from_hash(e) } end -- cgit v1.2.1