summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/chef-resource-inspector26
-rw-r--r--lib/chef/resource_inspector.rb89
-rw-r--r--spec/unit/resource_inspector_spec.rb60
3 files changed, 175 insertions, 0 deletions
diff --git a/bin/chef-resource-inspector b/bin/chef-resource-inspector
new file mode 100755
index 0000000000..6a7eac0c32
--- /dev/null
+++ b/bin/chef-resource-inspector
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+#
+# ./chef-resource-inspector - Find information about a resource
+#
+# Copyright:: Copyright (c) 2018, 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.
+
+Encoding.default_external = Encoding::UTF_8
+
+$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
+
+require "chef/resource_inspector"
+
+ResourceInspector.start
diff --git a/lib/chef/resource_inspector.rb b/lib/chef/resource_inspector.rb
new file mode 100644
index 0000000000..0f4f718c72
--- /dev/null
+++ b/lib/chef/resource_inspector.rb
@@ -0,0 +1,89 @@
+# Copyright:: Copyright 2018, 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 "chef/cookbook_loader"
+require "chef/cookbook/file_vendor"
+require "chef/cookbook/file_system_file_vendor"
+require "chef/resource/lwrp_base"
+require "chef/run_context"
+require "chef/node"
+require "chef/resources"
+require "chef/json_compat"
+
+module ResourceInspector
+ def self.extract_resource(resource, complete = false)
+ data = {}
+ data[:description] = resource.description
+ # data[:deprecated] = resource.deprecated || false
+ data[:actions] = resource.allowed_actions
+ data[:examples] = resource.examples
+ data[:introduced] = resource.introduced
+
+ properties = unless complete
+ resource.properties.reject { |_, k| k.options[:declared_in] == Chef::Resource }
+ else
+ resource.properties
+ end
+
+ data[:properties] = properties.each_with_object([]) do |(n, k), acc|
+ opts = k.options
+ acc << { name: n, description: opts[:description], introduced: opts[:introduced], is: opts[:is], deprecated: opts[:deprecated] || false }
+ end
+ data
+ end
+
+ def self.extract_cookbook(path, complete)
+ path = File.expand_path(path)
+ dir, name = File.split(path)
+ Chef::Cookbook::FileVendor.fetch_from_disk(path)
+ loader = Chef::CookbookLoader.new(dir)
+ cookbooks = loader.load_cookbooks
+ resources = cookbooks[name].files_for(:resources)
+
+ resources.each_with_object({}) do |r, res|
+ pth = r["full_path"]
+ cur = Chef::Resource::LWRPBase.build_from_file(name, pth, Chef::RunContext.new(Chef::Node.new, nil, nil))
+ res[cur.resource_name] = extract_resource(cur, complete)
+ end
+ end
+
+ # If we're given no resources, dump all of Chef's built ins
+ # otherwise, if we have a path then extract all the resources from the cookbook
+ # or else do a list of built in resources
+ #
+ # @param complete [TrueClass, FalseClass] Whether to show properties defined in the base Resource class
+ def self.inspect(arguments = [], complete: false)
+ output = if arguments.empty?
+ ObjectSpace.each_object(Class).select { |k| k < Chef::Resource }.each_with_object({}) { |klass, acc| acc[klass.resource_name] = extract_resource(klass) }
+ else
+ arguments.each_with_object({}) do |arg, acc|
+ if File.directory?(arg)
+ extract_cookbook(arg, complete).each { |k, v| acc[k] = v }
+ else
+ r = Chef::ResourceResolver.resolve(arg.to_sym, canonical: nil)
+ acc[r.resource_name] = extract_resource(r, complete)
+ end
+ end
+ end
+
+ puts Chef::JSONCompat.to_json_pretty(output)
+ end
+
+ def self.start
+ inspect(ARGV, complete: true)
+ end
+
+end
diff --git a/spec/unit/resource_inspector_spec.rb b/spec/unit/resource_inspector_spec.rb
new file mode 100644
index 0000000000..2cb9c0bb65
--- /dev/null
+++ b/spec/unit/resource_inspector_spec.rb
@@ -0,0 +1,60 @@
+# Copyright:: Copyright 2018, 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 "spec_helper"
+require "chef/resource_inspector"
+
+class DummyResource < Chef::Resource
+ resource_name :dummy
+ description "A dummy resource"
+ examples <<~EOH
+ dummy "foo" do
+ first "yes"
+ end
+ EOH
+ introduced "14.0"
+ property :first, String, description: "My First Property", introduced: "14.0"
+
+ action :dummy do
+ return true
+ end
+end
+
+describe ResourceInspector do
+ describe "inspecting a resource" do
+ subject { ResourceInspector.extract_resource(DummyResource, false) }
+
+ it "returns a hash with required data" do
+ expect(subject[:description]).to eq "A dummy resource"
+ expect(subject[:actions]).to match_array [:nothing, :dummy]
+ end
+
+ context "excluding built in properties" do
+ it "returns a single property" do
+ expect(subject[:properties].count).to eq 1
+ expect(subject[:properties].first[:name]).to eq :first
+ end
+ end
+
+ context "including built in properties" do
+ subject { ResourceInspector.extract_resource(DummyResource, true) }
+ it "returns many properties" do
+ expect(subject[:properties].count).to be > 1
+ expect(subject[:properties].map { |n| n[:name] }).to include(:name, :first, :sensitive)
+ end
+ end
+ end
+end