diff options
author | Tim Smith <tsmith@chef.io> | 2020-03-25 12:51:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-25 12:51:35 -0700 |
commit | 4fff7072a364a91a98638a45a85a1bd89166a178 (patch) | |
tree | 555ec008e8db5d9d4db588b5e524e4bf4a15c4c4 | |
parent | 15f29977e0fcd620058c662b9e788db42ef34432 (diff) | |
parent | 45c48f8ffb857d13b7a3f1ae6c522d3beffbbe5d (diff) | |
download | chef-4fff7072a364a91a98638a45a85a1bd89166a178.tar.gz |
Merge pull request #9508 from chef/btm/knife-yaml
Add a `knife yaml convert` tool
-rw-r--r-- | lib/chef/knife/core/ui.rb | 8 | ||||
-rw-r--r-- | lib/chef/knife/yaml_convert.rb | 91 |
2 files changed, 99 insertions, 0 deletions
diff --git a/lib/chef/knife/core/ui.rb b/lib/chef/knife/core/ui.rb index 03b11a1b4d..893daa04e9 100644 --- a/lib/chef/knife/core/ui.rb +++ b/lib/chef/knife/core/ui.rb @@ -140,6 +140,14 @@ class Chef log("#{color("FATAL:", :red, :bold)} #{message}") end + # Print a message describing a fatal error and exit 1 + # + # @param message [String] the text string + def fatal!(message) + fatal(message) + exit 1 + end + def color(string, *colors) if color? pastel.decorate(string, *colors) diff --git a/lib/chef/knife/yaml_convert.rb b/lib/chef/knife/yaml_convert.rb new file mode 100644 index 0000000000..a5b75e5691 --- /dev/null +++ b/lib/chef/knife/yaml_convert.rb @@ -0,0 +1,91 @@ +# +# Author:: Bryan McLellan <btm@loftninjas.org> +# Copyright:: Copyright 2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# 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. +# + +require "yaml" +require_relative "../knife" +class Chef::Knife::YamlConvert < Chef::Knife + + banner "knife yaml convert YAML_FILENAME [RUBY_FILENAME]" + + def run + if name_args.empty? + ui.fatal!("Please specify the file name of a YAML recipe to convert to Ruby") + elsif name_args.size >= 3 + ui.fatal!("knife yaml convert YAML_FILENAME [RUBY_FILENAME]") + end + + yaml_file = @name_args[0] + unless ::File.exist?(yaml_file) && ::File.readable?(yaml_file) + ui.fatal("Input YAML file '#{yaml_file}' does not exist or is unreadable") + end + + ruby_file = if @name_args[1] + @name_args[1] # use the specified output filename if provided + else + if ::File.extname(yaml_file) == ".yml" || ::File.extname(yaml_file) == ".yaml" + yaml_file.gsub(/\.(yml|yaml)$/, ".rb") + else + yaml_file + ".rb" # fall back to putting .rb on the end of whatever the yaml file was named + end + end + + if ::File.exist?(ruby_file) + ui.fatal!("Output Ruby file '#{ruby_file}' already exists") + end + + yaml_contents = IO.read(yaml_file) + + # YAML can contain multiple documents (--- is the separator), let's not support that. + if ::YAML.load_stream(yaml_contents).length > 1 + ui.fatal!("YAML recipe '#{yaml_file}' contains multiple documents, only one is supported") + end + + # Unfortunately, per the YAML spec, comments are stripped when we load, so we lose them on conversion + yaml_hash = ::YAML.safe_load(yaml_contents) + unless yaml_hash.is_a?(Hash) && yaml_hash.key?("resources") + ui.fatal!("YAML recipe '#{source_file}' must contain a top-level 'resources' hash (YAML sequence), i.e. 'resources:'") + end + + ui.warn("No resources found in '#{yaml_file}'") if yaml_hash["resources"].size == 0 + + ::File.open(ruby_file, "w") do |file| + file.write(resource_hash_to_string(yaml_hash["resources"], yaml_file)) + end + ui.info("Converted '#{yaml_file}' to '#{ruby_file}'") + end + + # Converts a Hash of resources to a Ruby recipe + # returns a string ready to be written to a file or stdout + def resource_hash_to_string(resource_hash, filename) + ruby_contents = [] + ruby_contents << "# Autoconverted recipe from #{filename}\n" + + resource_hash.each do |r| + type = r.delete("type") + name = r.delete("name") + + ruby_contents << "#{type} \"#{name}\" do" + r.each do |k, v| + ruby_contents << " #{k} \"#{v}\"" + end + ruby_contents << "end\n" + end + + ruby_contents.join("\n") + end +end |