summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Preston <stuart@chef.io>2018-06-20 12:46:34 +0100
committerGitHub <noreply@github.com>2018-06-20 12:46:34 +0100
commitf960c7f3b7c58c80a2e104ed631d97190b272b4e (patch)
treeda1890b75ac68d5047aec54083e0df42d140c96f
parent256e57adbda5fd53653c8c2ebba7bbd72e84a8bf (diff)
parent3d9f9731fcb6c5d02c57e0dffd9df21f168ef8fc (diff)
downloadchef-f960c7f3b7c58c80a2e104ed631d97190b272b4e.tar.gz
Merge pull request #7354 from Intility/powershell_package_source
Add powershell_package_source resource
-rw-r--r--lib/chef/resource/powershell_package_source.rb165
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/unit/resource/powershell_package_source_spec.rb219
3 files changed, 385 insertions, 0 deletions
diff --git a/lib/chef/resource/powershell_package_source.rb b/lib/chef/resource/powershell_package_source.rb
new file mode 100644
index 0000000000..9fa4bc8497
--- /dev/null
+++ b/lib/chef/resource/powershell_package_source.rb
@@ -0,0 +1,165 @@
+# Author:: Tor Magnus Rakvåg (tm@intility.no)
+# Copyright:: 2018, Intility AS
+# 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/resource"
+require "chef/json_compat"
+
+class Chef
+ class Resource
+ class PowershellPackageSource < Chef::Resource
+ preview_resource true
+ resource_name "powershell_package_source"
+
+ description "Use the powershell_package_source resource to register a powershell package repository"
+ introduced "14.3"
+
+ property :source_name, String,
+ description: "The name of the package source",
+ name_property: true
+
+ property :url, String,
+ description: "The url to the package source",
+ required: true
+
+ property :trusted, [TrueClass, FalseClass],
+ description: "Whether or not to trust packages from this source",
+ default: false
+
+ property :provider_name, String,
+ equal_to: %w{ Programs msi NuGet msu PowerShellGet psl chocolatey },
+ validation_message: "The following providers are supported: 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl' or 'chocolatey'",
+ description: "The package management provider for the source. It supports the following providers: 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl' and 'chocolatey'.",
+ default: "NuGet"
+
+ property :publish_location, String,
+ description: "The url where modules will be published to for this source. Only valid if the provider is 'PowerShellGet'."
+
+ property :script_source_location, String,
+ description: "The url where scripts are located for this source. Only valid if the provider is 'PowerShellGet'."
+
+ property :script_publish_location, String,
+ description: "The location where scripts will be published to for this source. Only valid if the provider is 'PowerShellGet'."
+
+ load_current_value do
+ cmd = load_resource_state_script(name)
+ repo = powershell_out!(cmd)
+ status = Chef::JSONCompat.from_json(repo.stdout)
+ url status["url"].nil? ? "not_set" : status["url"]
+ trusted status["trusted"]
+ provider_name status["provider_name"]
+ publish_location status["publish_location"]
+ script_source_location status["script_source_location"]
+ script_publish_location status["script_publish_location"]
+ end
+
+ action :register do
+ description "Registers and updates the powershell package source."
+ # TODO: Ensure package provider is installed?
+ if psrepository_cmdlet_appropriate?
+ if package_source_exists?
+ converge_if_changed :url, :trusted, :publish_location, :script_source_location, :script_publish_location do
+ update_cmd = build_ps_repository_command("Set", new_resource)
+ res = powershell_out(update_cmd)
+ raise "Failed to update #{new_resource.source_name}: #{res.stderr}" unless res.stderr.empty?
+ end
+ else
+ converge_by("register source: #{new_resource.source_name}") do
+ register_cmd = build_ps_repository_command("Register", new_resource)
+ res = powershell_out(register_cmd)
+ raise "Failed to register #{new_resource.source_name}: #{res.stderr}" unless res.stderr.empty?
+ end
+ end
+ else
+ if package_source_exists?
+ converge_if_changed :url, :trusted, :provider_name do
+ update_cmd = build_package_source_command("Set", new_resource)
+ res = powershell_out(update_cmd)
+ raise "Failed to update #{new_resource.source_name}: #{res.stderr}" unless res.stderr.empty?
+ end
+ else
+ converge_by("register source: #{new_resource.source_name}") do
+ register_cmd = build_package_source_command("Register", new_resource)
+ res = powershell_out(register_cmd)
+ raise "Failed to register #{new_resource.source_name}: #{res.stderr}" unless res.stderr.empty?
+ end
+ end
+ end
+ end
+
+ action :unregister do
+ description "Unregisters the powershell package source."
+ if package_source_exists?
+ unregister_cmd = "Get-PackageSource -Name '#{new_resource.source_name}' | Unregister-PackageSource"
+ converge_by("unregister source: #{new_resource.source_name}") do
+ res = powershell_out(unregister_cmd)
+ raise "Failed to unregister #{new_resource.source_name}: #{res.stderr}" unless res.stderr.empty?
+ end
+ end
+ end
+
+ action_class do
+ def package_source_exists?
+ cmd = powershell_out!("(Get-PackageSource -Name '#{new_resource.source_name}').Name")
+ cmd.stdout.downcase.strip == new_resource.source_name.downcase
+ end
+
+ def psrepository_cmdlet_appropriate?
+ new_resource.provider_name == "PowerShellGet"
+ end
+
+ def build_ps_repository_command(cmdlet_type, new_resource)
+ cmd = "#{cmdlet_type}-PSRepository -Name '#{new_resource.source_name}'"
+ cmd << " -SourceLocation '#{new_resource.url}'" if new_resource.url
+ cmd << " -InstallationPolicy '#{new_resource.trusted ? "Trusted" : "Untrusted"}'"
+ cmd << " -PublishLocation '#{new_resource.publish_location}'" if new_resource.publish_location
+ cmd << " -ScriptSourceLocation '#{new_resource.script_source_location}'" if new_resource.script_source_location
+ cmd << " -ScriptPublishLocation '#{new_resource.script_publish_location}'" if new_resource.script_publish_location
+ cmd
+ end
+
+ def build_package_source_command(cmdlet_type, new_resource)
+ cmd = "#{cmdlet_type}-PackageSource -Name '#{new_resource.source_name}'"
+ cmd << " -Location '#{new_resource.url}'" if new_resource.url
+ cmd << " -Trusted:#{new_resource.trusted ? "$true" : "$false"}"
+ cmd << " -ProviderName '#{new_resource.provider_name}'" if new_resource.provider_name
+ cmd
+ end
+ end
+ end
+
+ private
+
+ def load_resource_state_script(name)
+ <<-EOH
+ if(Get-PackageSource -Name '#{name}' -ErrorAction SilentlyContinue) {
+ if ((Get-PackageSource -Name '#{name}').ProviderName -eq 'PowerShellGet') {
+ (Get-PSRepository -Name '#{name}') | Select @{n='source_name';e={$_.Name}}, @{n='url';e={$_.SourceLocation}},
+ @{n='trusted';e={$_.Trusted}}, @{n='provider_name';e={$_.PackageManagementProvider}}, @{n='publish_location';e={$_.PublishLocation}},
+ @{n='script_source_location';e={$_.ScriptSourceLocation}}, @{n='script_publish_location';e={$_.ScriptPublishLocation}} | ConvertTo-Json
+ }
+ else {
+ (Get-PackageSource -Name '#{name}') | Select @{n='source_name';e={$_.Name}}, @{n='url';e={$_.Location}},
+ @{n='provider_name';e={$_.ProviderName}}, @{n='trusted';e={$_.IsTrusted}} | ConvertTo-Json
+ }
+ }
+ else {
+ "" | Select source_name, url, provider_name, trusted | ConvertTo-Json
+ }
+ EOH
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index d942518b21..b21f2fe6f7 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -69,6 +69,7 @@ require "chef/resource/pacman_package"
require "chef/resource/paludis_package"
require "chef/resource/perl"
require "chef/resource/portage_package"
+require "chef/resource/powershell_package_source"
require "chef/resource/powershell_script"
require "chef/resource/osx_profile"
require "chef/resource/python"
diff --git a/spec/unit/resource/powershell_package_source_spec.rb b/spec/unit/resource/powershell_package_source_spec.rb
new file mode 100644
index 0000000000..d8cb8a09a0
--- /dev/null
+++ b/spec/unit/resource/powershell_package_source_spec.rb
@@ -0,0 +1,219 @@
+# Author:: Tor Magnus Rakvåg (tm@intility.no)
+# Copyright:: 2018, Intility AS
+# 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::PowershellPackageSource do
+ let(:resource) { Chef::Resource::PowershellPackageSource.new("MyGallery") }
+ let(:provider) { resource.provider_for_action(:enable) }
+
+ it "has a resource name of :powershell_package_source" do
+ expect(resource.resource_name).to eql(:powershell_package_source)
+ end
+
+ it "the name_property is 'name'" do
+ expect(resource.source_name).to eql("MyGallery")
+ end
+
+ it "the default action is :register" do
+ expect(resource.action).to eql([:register])
+ end
+
+ it "supports :register and :unregister actions" do
+ expect { resource.action :register }.not_to raise_error
+ expect { resource.action :unregister }.not_to raise_error
+ end
+
+ it "the url property accepts strings" do
+ resource.url("https://mygallery.company.co/api/v2/")
+ expect(resource.url).to eql("https://mygallery.company.co/api/v2/")
+ end
+
+ it "the trusted property accepts true and false" do
+ resource.trusted(false)
+ expect(resource.trusted).to eql(false)
+ resource.trusted(true)
+ expect(resource.trusted).to eql(true)
+ end
+
+ it "trusted defaults to false" do
+ expect(resource.trusted).to eql(false)
+ end
+
+ it "provider_name accepts 'Programs', 'msi', 'NuGet', 'msu', 'PowerShellGet', 'psl', 'chocolatey'" do
+ expect { resource.provider_name("Programs") }.not_to raise_error
+ expect { resource.provider_name("msi") }.not_to raise_error
+ expect { resource.provider_name("NuGet") }.not_to raise_error
+ expect { resource.provider_name("msu") }.not_to raise_error
+ expect { resource.provider_name("PowerShellGet") }.not_to raise_error
+ expect { resource.provider_name("psl") }.not_to raise_error
+ expect { resource.provider_name("chocolatey") }.not_to raise_error
+ end
+
+ it "the publish_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/package")
+ end
+
+ it "the script_source_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/scripts")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/scripts")
+ end
+
+ it "the script_publish_location property accepts strings" do
+ resource.publish_location("https://mygallery.company.co/api/v2/scripts")
+ expect(resource.publish_location).to eql("https://mygallery.company.co/api/v2/scripts")
+ end
+
+ describe "#build_ps_repository_command" do
+ before do
+ resource.source_name("MyGallery")
+ resource.url("https://mygallery.company.co/api/v2/")
+ end
+
+ context "#register" do
+ it "builds a minimal command" do
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted'")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Trusted'")
+ end
+
+ it "builds a command with a publish location" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -PublishLocation 'https://mygallery.company.co/api/v2/package'")
+ end
+
+ it "builds a command with a script source location" do
+ resource.script_source_location("https://mygallery.company.co/api/v2/scripts")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptSourceLocation 'https://mygallery.company.co/api/v2/scripts'")
+ end
+
+ it "builds a command with a script publish location" do
+ resource.script_publish_location("https://mygallery.company.co/api/v2/scripts/package")
+ expect(provider.build_ps_repository_command("Register", resource)).to eql("Register-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptPublishLocation 'https://mygallery.company.co/api/v2/scripts/package'")
+ end
+ end
+
+ context "#set" do
+ it "builds a minimal command" do
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted'")
+ end
+
+ it "builds a command to change the url" do
+ resource.url("https://othergallery.company.co/api/v2/")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://othergallery.company.co/api/v2/' -InstallationPolicy 'Untrusted'")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Trusted'")
+ end
+
+ it "builds a command with a publish location" do
+ resource.publish_location("https://mygallery.company.co/api/v2/package")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -PublishLocation 'https://mygallery.company.co/api/v2/package'")
+ end
+
+ it "builds a command with a script source location" do
+ resource.script_source_location("https://mygallery.company.co/api/v2/scripts")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptSourceLocation 'https://mygallery.company.co/api/v2/scripts'")
+ end
+
+ it "builds a command with a script publish location" do
+ resource.script_publish_location("https://mygallery.company.co/api/v2/scripts/package")
+ expect(provider.build_ps_repository_command("Set", resource)).to eql("Set-PSRepository -Name 'MyGallery' -SourceLocation 'https://mygallery.company.co/api/v2/' -InstallationPolicy 'Untrusted' -ScriptPublishLocation 'https://mygallery.company.co/api/v2/scripts/package'")
+ end
+ end
+ end
+
+ describe "#build_package_source_command" do
+ before do
+ resource.source_name("NuGet")
+ resource.url("http://nuget.org/api/v2/")
+ end
+
+ context "#register" do
+ it "builds a minimal command" do
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$false -ProviderName 'NuGet'")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$true -ProviderName 'NuGet'")
+ end
+
+ it "builds a command with a different provider" do
+ resource.source_name("choco")
+ resource.url("https://chocolatey.org/api/v2/")
+ resource.provider_name("chocolatey")
+ expect(provider.build_package_source_command("Register", resource)).to eql("Register-PackageSource -Name 'choco' -Location 'https://chocolatey.org/api/v2/' -Trusted:$false -ProviderName 'chocolatey'")
+ end
+ end
+
+ context "#set" do
+ it "builds a minimal command" do
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$false -ProviderName 'NuGet'")
+ end
+
+ it "builds a command to change the url" do
+ resource.url("https://nuget.company.co/api/v2/")
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'https://nuget.company.co/api/v2/' -Trusted:$false -ProviderName 'NuGet'")
+ end
+
+ it "builds a command with trusted set to true" do
+ resource.trusted(true)
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'NuGet' -Location 'http://nuget.org/api/v2/' -Trusted:$true -ProviderName 'NuGet'")
+ end
+
+ it "builds a command with a different provider" do
+ resource.source_name("choco")
+ resource.url("https://chocolatey.org/api/v2/")
+ resource.provider_name("chocolatey")
+ expect(provider.build_package_source_command("Set", resource)).to eql("Set-PackageSource -Name 'choco' -Location 'https://chocolatey.org/api/v2/' -Trusted:$false -ProviderName 'chocolatey'")
+ end
+ end
+ end
+
+ describe "#psrepository_cmdlet_appropriate?" do
+ it "returns true if the provider_name is 'PowerShellGet'" do
+ resource.provider_name("PowerShellGet")
+ expect(provider.psrepository_cmdlet_appropriate?).to eql(true)
+ end
+
+ it "returns false if the provider_name is something else" do
+ resource.provider_name("NuGet")
+ expect(provider.psrepository_cmdlet_appropriate?).to eql(false)
+ end
+ end
+
+ describe "#package_source_exists?" do
+ it "returns true if it exists" do
+ allow(provider).to receive(:powershell_out!).with("(Get-PackageSource -Name 'MyGallery').Name").and_return(double("powershell_out!", :stdout => "MyGallery\r\n"))
+ resource.source_name("MyGallery")
+ expect(provider.package_source_exists?).to eql(true)
+ end
+
+ it "returns false if it doesn't exist" do
+ allow(provider).to receive(:powershell_out!).with("(Get-PackageSource -Name 'MyGallery').Name").and_return(double("powershell_out!", :stdout => ""))
+ resource.source_name("MyGallery")
+ expect(provider.package_source_exists?).to eql(false)
+ end
+ end
+end