summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2018-06-12 18:44:23 -0700
committerTim Smith <tsmith@chef.io>2018-06-21 22:28:45 -0600
commit757b5b7b60a78e52a95cecdda87dd738268acfc6 (patch)
tree4b5cab9da2adc9ffd43d7dd4d88cb14c3e7207e0
parentf960c7f3b7c58c80a2e104ed631d97190b272b4e (diff)
downloadchef-choco.tar.gz
Add chocolatey_config and chocolatey_source resourceschoco
Allow a user to fully manage chocolatey and get it setup for airgapped environments using chef out of the box. Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r--lib/chef/resource/chocolatey_config.rb83
-rw-r--r--lib/chef/resource/chocolatey_source.rb88
-rw-r--r--lib/chef/resources.rb2
-rw-r--r--spec/unit/resource/chocolatey_config_spec.rb87
-rw-r--r--spec/unit/resource/chocolatey_source_spec.rb91
5 files changed, 351 insertions, 0 deletions
diff --git a/lib/chef/resource/chocolatey_config.rb b/lib/chef/resource/chocolatey_config.rb
new file mode 100644
index 0000000000..87ba3af66d
--- /dev/null
+++ b/lib/chef/resource/chocolatey_config.rb
@@ -0,0 +1,83 @@
+#
+# Copyright:: 2018, 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 ChocolateyConfig < Chef::Resource
+ preview_resource true
+ resource_name :chocolatey_config
+
+ description "Use the chocolatey_config resource to add or remove Chocolatey configuration keys."
+ introduced "14.3"
+
+ property :config_key, String, name_property: true,
+ description: "The name of the config. We'll use the resource's name if this isn't provided."
+
+ property :value, String,
+ description: "The value to set."
+
+ load_current_value do
+ current_val = fetch_config_element(config_key)
+ current_value_does_not_exist! if current_val.nil?
+
+ config_key config_key
+ value current_val
+ end
+
+ # @param [String] id the config name
+ # @return [String] the element's value field
+ def fetch_config_element(id)
+ require "rexml/document"
+ config_file = 'C:\ProgramData\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, "//config/add[@key=\"#{id}\"]")
+ data ? data.attribute("value").to_s : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :set do
+ description "Sets a Chocolatey config value."
+
+ raise "#{new_resource}: When adding a Chocolatey config you must pass the 'value' property!" unless new_resource.value
+
+ converge_if_changed do
+ shell_out!(choco_cmd("set"))
+ end
+ end
+
+ action :unset do
+ description "Unsets a Chocolatey config value."
+
+ if current_resource
+ converge_by("unset Chocolatey config '#{new_resource.config_key}'") do
+ shell_out!(choco_cmd("unset"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco config command string
+ def choco_cmd(action)
+ cmd = "C:\\ProgramData\\chocolatey\\bin\\choco config #{action} --name #{new_resource.config_key}"
+ cmd << " --value #{new_resource.value}" if action == "set"
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/chocolatey_source.rb b/lib/chef/resource/chocolatey_source.rb
new file mode 100644
index 0000000000..9308a07587
--- /dev/null
+++ b/lib/chef/resource/chocolatey_source.rb
@@ -0,0 +1,88 @@
+#
+# Copyright:: 2018, 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 ChocolateySource < Chef::Resource
+ preview_resource true
+ resource_name :chocolatey_source
+
+ description "Use the chocolatey_source resource to add or remove Chocolatey sources."
+ introduced "14.3"
+
+ property :source_name, String, name_property: true
+ property :source, String
+ property :bypass_proxy, [TrueClass, FalseClass], default: false
+ property :priority, Integer, default: 0
+
+ load_current_value do
+ element = fetch_source_element(source_name)
+ current_value_does_not_exist! if element.nil?
+
+ source_name element["id"]
+ source element["value"]
+ bypass_proxy element["bypassProxy"] == "true"
+ priority element["priority"].to_i
+ end
+
+ # @param [String] id the source name
+ # @return [REXML::Attributes] finds the source element with the
+ def fetch_source_element(id)
+ require "rexml/document"
+
+ config_file = 'C:\ProgramData\chocolatey\config\chocolatey.config'
+ raise "Could not find the Chocolatey config at #{config_file}!" unless ::File.exist?(config_file)
+
+ config_contents = REXML::Document.new(::File.read(config_file))
+ data = REXML::XPath.first(config_contents, "//sources/source[@id=\"#{id}\"]")
+ data ? data.attributes : nil # REXML just returns nil if it can't find anything so avoid an undefined method error
+ end
+
+ action :add do
+ description "Adds a Chocolatey source."
+
+ raise "#{new_resource}: When adding a Chocolatey source you must pass the 'source' property!" unless new_resource.source
+
+ converge_if_changed do
+ shell_out!(choco_cmd("add"))
+ end
+ end
+
+ action :remove do
+ description "Removes a Chocolatey source."
+
+ if current_resource
+ converge_by("remove Chocolatey source '#{new_resource.source_name}'") do
+ shell_out!(choco_cmd("remove"))
+ end
+ end
+ end
+
+ action_class do
+ # @param [String] action the name of the action to perform
+ # @return [String] the choco source command string
+ def choco_cmd(action)
+ cmd = "C:\\ProgramData\\chocolatey\\bin\\choco source #{action} -n \"#{new_resource.source_name}\""
+ if action == "add"
+ cmd << " -s #{new_resource.source} --priority=#{new_resource.priority}"
+ cmd << " --bypassproxy" if new_resource.bypass_proxy
+ end
+ cmd
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index b21f2fe6f7..c13a01c352 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -27,7 +27,9 @@ require "chef/resource/build_essential"
require "chef/resource/cookbook_file"
require "chef/resource/chef_gem"
require "chef/resource/chef_handler"
+require "chef/resource/chocolatey_config"
require "chef/resource/chocolatey_package"
+require "chef/resource/chocolatey_source"
require "chef/resource/cron"
require "chef/resource/csh"
require "chef/resource/directory"
diff --git a/spec/unit/resource/chocolatey_config_spec.rb b/spec/unit/resource/chocolatey_config_spec.rb
new file mode 100644
index 0000000000..5f4d9c78de
--- /dev/null
+++ b/spec/unit/resource/chocolatey_config_spec.rb
@@ -0,0 +1,87 @@
+#
+# 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"
+
+describe Chef::Resource::ChocolateyConfig do
+
+ let(:resource) { Chef::Resource::ChocolateyConfig.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
+
+ it "has a resource name of :chocolatey_config" do
+ expect(resource.resource_name).to eql(:chocolatey_config)
+ end
+
+ it "is not a preview resource in Chef 15" do
+ pending("Chef 15") unless Chef::VERSION.start_with?("15")
+ expect(resource.class.preview_resource).to be_falsey
+ end
+
+ it "has a name property of config_key" do
+ expect(resource.config_key).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set and :unset actions" do
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :unset }.not_to raise_error
+ end
+
+ it "bypass_proxy property defaults to false" do
+ expect { resource.bypass_proxy.to be_false }
+ end
+
+ describe "#fetch_config_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_config_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_config_element("containsLegacyPackageInstalls")).to eq("true")
+ expect { resource.fetch_config_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_config_element("foo")).to be_nil
+ expect { resource.fetch_config_element("foo") }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/unit/resource/chocolatey_source_spec.rb b/spec/unit/resource/chocolatey_source_spec.rb
new file mode 100644
index 0000000000..ee8d098b0b
--- /dev/null
+++ b/spec/unit/resource/chocolatey_source_spec.rb
@@ -0,0 +1,91 @@
+#
+# 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"
+
+describe Chef::Resource::ChocolateySource do
+
+ let(:resource) { Chef::Resource::ChocolateySource.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
+
+ it "has a resource name of :chocolatey_source" do
+ expect(resource.resource_name).to eql(:chocolatey_source)
+ end
+
+ it "is not a preview resource in Chef 15" do
+ pending("Chef 15") unless Chef::VERSION.start_with?("15")
+ expect(resource.class.preview_resource).to be_falsey
+ end
+
+ it "has a name property of source_name" do
+ expect(resource.source_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :add" do
+ expect(resource.action).to eql([:add])
+ end
+
+ it "supports :add and :remove actions" do
+ expect { resource.action :add }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+
+ it "bypass_proxy property defaults to false" do
+ expect { resource.bypass_proxy.to be_false }
+ end
+
+ it "priority property defaults to 0" do
+ expect { resource.priority.to eq(0) }
+ end
+
+ describe "#fetch_source_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_source_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_source_element("chocolatey")["value"]).to eq("https://chocolatey.org/api/v2/")
+ expect { resource.fetch_source_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_source_element("foo")).to be_nil
+ expect { resource.fetch_source_element("foo") }.not_to raise_error
+ end
+ end
+end