summaryrefslogtreecommitdiff
path: root/lib/chef/provider/package/powershell.rb
blob: 60244c62e5e9d0a8a6db1de4b74ba5f4525a05f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Author:: Dheeraj Dubey(dheeraj.dubey@msystechnologies.com)
# Copyright:: Copyright (c) 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_relative "../package"
require_relative "../../resource/powershell_package"
require_relative "../../mixin/powershell_out"

class Chef
  class Provider
    class Package
      class Powershell < Chef::Provider::Package
        include Chef::Mixin::PowershellOut

        provides :powershell_package

        def load_current_resource
          @current_resource = Chef::Resource::PowershellPackage.new(new_resource.name)
          current_resource.package_name(new_resource.package_name)
          current_resource.version(build_current_versions)
          current_resource
        end

        def define_resource_requirements
          super
          if powershell_out("$PSVersionTable.PSVersion.Major").stdout.strip.to_i < 5
            raise "Minimum installed PowerShell Version required is 5"
          end

          requirements.assert(:install) do |a|
            a.assertion { candidates_exist_for_all_uninstalled? }
            a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}")
            a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured")
          end
        end

        def candidate_version
          @candidate_version ||= build_candidate_versions
        end

        # Installs the package specified with the version passed else latest version will be installed
        def install_package(names, versions)
          # To enable tls 1.2, which is disabled by default in some OS
          powershell_out("[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12")

          names.each_with_index do |name, index|
            cmd = powershell_out(build_powershell_package_command("Install-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
            next if cmd.nil?
            raise Chef::Exceptions::PowershellCmdletException, "Failed to install package due to catalog signing error, use skip_publisher_check to force install" if cmd.stderr =~ /SkipPublisherCheck/
          end
        end

        # Removes the package for the version passed and if no version is passed, then all installed versions of the package are removed
        def remove_package(names, versions)
          names.each_with_index do |name, index|
            if versions && !versions[index].nil?
              powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'", versions[index]), timeout: new_resource.timeout)
            else
              version = "0"
              until version.empty?
                version = powershell_out(build_powershell_package_command("Uninstall-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
                unless version.empty?
                  logger.info("Removed package '#{name}' with version #{version}")
                end
              end
            end
          end
        end

        # Returns array of available available online
        def build_candidate_versions
          versions = []
          new_resource.package_name.each_with_index do |name, index|
            version = if new_resource.version && !new_resource.version[index].nil?
                        powershell_out(build_powershell_package_command("Find-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
                      else
                        powershell_out(build_powershell_package_command("Find-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
                      end
            if version.empty?
              version = nil
            end
            versions.push(version)
          end
          versions
        end

        # Returns version array of installed version on the system
        def build_current_versions
          version_list = []
          new_resource.package_name.each_with_index do |name, index|
            version = if new_resource.version && !new_resource.version[index].nil?
                        powershell_out(build_powershell_package_command("Get-Package '#{name}'", new_resource.version[index]), timeout: new_resource.timeout).stdout.strip
                      else
                        powershell_out(build_powershell_package_command("Get-Package '#{name}'"), timeout: new_resource.timeout).stdout.strip
                      end
            if version.empty?
              version = nil
            end
            version_list.push(version)
          end
          version_list
        end

        def build_powershell_package_command(command, version = nil)
          command = [command] unless command.is_a?(Array)
          cmdlet_name = command.first
          command.unshift("(")
          # -WarningAction SilentlyContinue is used to suppress the warnings from stdout
          %w{-Force -ForceBootstrap -WarningAction SilentlyContinue}.each do |arg|
            command.push(arg)
          end
          command.push("-RequiredVersion #{version}") if version
          command.push("-Source #{new_resource.source}") if new_resource.source && cmdlet_name =~ Regexp.union(/Install-Package/, /Find-Package/)
          command.push("-SkipPublisherCheck") if new_resource.skip_publisher_check && cmdlet_name !~ /Find-Package/
          command.push(").Version")
          command.join(" ")
        end

        def check_resource_semantics!
          # This validation method from Chef::Provider::Package does not apply here, so no-op it.
        end
      end
    end
  end
end