summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2020-03-25 12:51:35 -0700
committerGitHub <noreply@github.com>2020-03-25 12:51:35 -0700
commit4fff7072a364a91a98638a45a85a1bd89166a178 (patch)
tree555ec008e8db5d9d4db588b5e524e4bf4a15c4c4
parent15f29977e0fcd620058c662b9e788db42ef34432 (diff)
parent45c48f8ffb857d13b7a3f1ae6c522d3beffbbe5d (diff)
downloadchef-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.rb8
-rw-r--r--lib/chef/knife/yaml_convert.rb91
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