summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Ewan Park <gep13@gep13.co.uk>2019-05-23 12:22:59 -0700
committerGary Ewan Park <gep13@gep13.co.uk>2019-05-27 21:13:32 +0100
commit71fe79cae89a57b37f6750466e96f49eedc9097b (patch)
tree124abcd346170cd49b38b5303045288fe466b8ad
parent819307636de80266c6eb7e2072839e47f2dd4dbe (diff)
downloadchef-71fe79cae89a57b37f6750466e96f49eedc9097b.tar.gz
(GH-8580) Add ability to toggle Chocolatey feature
Add the ability to enable and disable a named Chocolatey feature by creating a new Chef resource named chocolatey_feature. This accepts a single property either from feature_name or directly from name attribute then, based on the defined action, will either enable or disable that feature. Resource inspects the current state of the chocolatey.config in order to decide whether a change is required or not. Signed-off-by: Gary Ewan Park <gep13@gep13.co.uk>
-rw-r--r--lib/chef/resource/chocolatey_feature.rb80
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/unit/resource/chocolatey_feature_spec.rb89
3 files changed, 170 insertions, 0 deletions
diff --git a/lib/chef/resource/chocolatey_feature.rb b/lib/chef/resource/chocolatey_feature.rb
new file mode 100644
index 0000000000..1c190905f3
--- /dev/null
+++ b/lib/chef/resource/chocolatey_feature.rb
@@ -0,0 +1,80 @@
+#
+# Copyright:: 2019, Chef Software, Inc.
+#
+# 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.
+#
+
+class Chef
+ class Resource
+ class ChocolateyFeature < Chef::Resource
+ resource_name :chocolatey_feature
+
+ description "Use the chocolatey_feature resource to enable and disable Chocolatey features."
+ introduced "15.1"
+
+ property :feature_name, String, name_property: true,
+ description: "The name of the Chocolatey feature to enable or disable."
+
+ property :feature_state, [TrueClass, FalseClass], default: false
+
+ load_current_value do
+ current_state = fetch_feature_element(feature_name)
+ current_value_does_not_exist! if current_state.nil?
+
+ feature_name feature_name
+ feature_state current_state == "true"
+ end
+
+ # @param [String] id the feature name
+ # @return [String] the element's value field
+ def fetch_feature_element(name)
+ require "rexml/document" unless defined?(REXML::Document)
+ config_file = "#{ENV['ALLUSERSPROFILE']}\\chocolatey\\config\\chocolatey.config"
+ raise "Could not find the Chocolatey config at #{config_file}!" unless ::File.exist?(config_file)
+
+ contents = REXML::Document.new(::File.read(config_file))
+ data = REXML::XPath.first(contents, "//features/feature[@name=\"#{name}\"]")
+ data ? data.attribute("enabled").to_s : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :enable do
+ description "Enables a named Chocolatey feature."
+
+ if current_resource.feature_state != true
+ converge_by("enable Chocolatey feature '#{new_resource.feature_name}'") do
+ shell_out!(choco_cmd("enable"))
+ end
+ end
+ end
+
+ action :disable do
+ description "Disables a named Chocolatey feature."
+
+ if current_resource.feature_state == true
+ converge_by("disable Chocolatey feature '#{new_resource.feature_name}'") do
+ shell_out!(choco_cmd("disable"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco feature command string
+ def choco_cmd(action)
+ cmd = "#{ENV['ALLUSERSPROFILE']}\\chocolatey\\bin\\choco feature #{action} --name #{new_resource.feature_name}"
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 11003e304c..d76f1bbf06 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -29,6 +29,7 @@ require_relative "resource/cookbook_file"
require_relative "resource/chef_gem"
require_relative "resource/chef_handler"
require_relative "resource/chocolatey_config"
+require_relative "resource/chocolatey_feature"
require_relative "resource/chocolatey_package"
require_relative "resource/chocolatey_source"
require_relative "resource/cron"
diff --git a/spec/unit/resource/chocolatey_feature_spec.rb b/spec/unit/resource/chocolatey_feature_spec.rb
new file mode 100644
index 0000000000..1eb04cf240
--- /dev/null
+++ b/spec/unit/resource/chocolatey_feature_spec.rb
@@ -0,0 +1,89 @@
+#
+# Copyright:: Copyright 2019, 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"
+
+describe Chef::Resource::ChocolateyFeature do
+
+ let(:resource) { Chef::Resource::ChocolateyFeature.new("fakey_fakerton") }
+ let(:config) do
+ <<-CONFIG
+ <?xml version="1.0" encoding="utf-8"?>
+ <chocolatey xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <config>
+ <add key="containsLegacyPackageInstalls" value="true" description="Install has packages installed prior to 0.9.9 series." />
+ </config>
+ <sources>
+ <source id="chocolatey" value="https://chocolatey.org/api/v2/" disabled="false" bypassProxy="false" selfService="false" adminOnly="false" priority="0" />
+ </sources>
+ <features>
+ <feature name="checksumFiles" enabled="true" setExplicitly="false" description="Checksum files when pulled in from internet (based on package)." />
+ </features>
+ <apiKeys />
+ </chocolatey>
+ CONFIG
+ end
+
+ # we save off the ENV and set ALLUSERSPROFILE so these specs will work on *nix and non-C drive Windows installs
+ before(:each) do
+ @original_env = ENV.to_hash
+ ENV["ALLUSERSPROFILE"] = 'C:\ProgramData'
+ end
+
+ after(:each) do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "has a resource name of :chocolatey_feature" do
+ expect(resource.resource_name).to eql(:chocolatey_feature)
+ end
+
+ it "has a name property of feature_name" do
+ expect(resource.feature_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :enable" do
+ expect(resource.action).to eql([:enable])
+ end
+
+ it "supports :enable and :disable actions" do
+ expect { resource.action :enable }.not_to raise_error
+ expect { resource.action :disable }.not_to raise_error
+ end
+
+ describe "#fetch_feature_element" do
+ it "raises and error if the config file cannot be found" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(false)
+ expect { resource.fetch_feature_element("foo") }.to raise_error(RuntimeError)
+ end
+
+ it "returns the value if present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_feature_element("checksumFiles")).to eq("true")
+ expect { resource.fetch_feature_element("foo") }.not_to raise_error
+ end
+
+ it "returns nil if the element is not present in the config file" do
+ allow(::File).to receive(:exist?).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(true)
+ allow(::File).to receive(:read).with('C:\ProgramData\chocolatey\config\chocolatey.config').and_return(config)
+ expect(resource.fetch_feature_element("foo")).to be_nil
+ expect { resource.fetch_feature_element("foo") }.not_to raise_error
+ end
+ end
+end