vagrant-windows-update 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63e248d30d6a3fc4f3ba91cf9170c96d7ac45ae6
4
- data.tar.gz: 5d7a309419c7a4b8065de1eda991c10303b26192
3
+ metadata.gz: 9f351ccfce6734c91491ac81b4048ab62396aefa
4
+ data.tar.gz: 7a1789990f433bf461e670d4083d5507369276da
5
5
  SHA512:
6
- metadata.gz: 51292d0b6e50429280cfb57397fd93ba719e7440a069ee5c936ac617354d958785f13ea18cd8aa0b830037acf7bcfc7e91926e52ee13ecb0bc019109242a1632
7
- data.tar.gz: acf3aef2ef012f164abb9c8bddf2d374b4a1270d844a46722457079ce5719571c2642a09e08ecc0645c63ae7b529015263a126a73a25d16b24f8650203ddba83
6
+ metadata.gz: 0125aade3e921cb9815c92ebf7b5742ce4c3345c79e51ce4f3ee370954b61499a13e0a47a7845ad9ed7c56be02d95cbee8a545b40ac3378579b7a2be0fdde44b
7
+ data.tar.gz: f6795a0861c8083c45593fe1cd582e967d9478eb1751af2e3bbf5e791afc012fb3cfbded9f5dec73f7d536e3466aa1f38ca3d5664dff58efda93a2e3b4a9766b
@@ -4,25 +4,50 @@ rescue LoadError
4
4
  raise "The Vagrant Windows Update plugin must be run within Vagrant."
5
5
  end
6
6
 
7
- if Vagrant::VERSION < "1.9.0"
8
- raise "The Vagrant Windows Update plugin is only compatible with Vagrant 1.9+"
7
+ if Vagrant::VERSION < "2.0.3"
8
+ raise "The Vagrant Windows Update plugin is only compatible with Vagrant 2.0.3+"
9
9
  end
10
10
 
11
+ require "base64"
12
+
11
13
  module VagrantPlugins
12
14
  module WindowsUpdate
13
15
  class Plugin < Vagrant.plugin("2")
14
16
  name "Windows Update"
15
17
  description "Vagrant plugin to update a Windows VM as a provisioning step."
16
18
 
17
- provisioner "windows-update" do
18
- class Provisioner < Vagrant.plugin("2", :provisioner)
19
- def initialize(machine, config)
20
- super
19
+ config("windows-update", :provisioner) do
20
+ class Config < Vagrant.plugin("2", :config)
21
+ attr_accessor :filters
22
+
23
+ def initialize
24
+ @filters = UNSET_VALUE
25
+ end
26
+
27
+ def finalize!
28
+ @filters = ['include:$_.AutoSelectOnWebSites'] if @filters == UNSET_VALUE
21
29
  end
22
30
 
23
- def configure(root_config)
31
+ def validate(machine)
32
+ errors = _detected_errors
33
+ errors << 'filters must be a string array' unless filters_valid?
34
+ return { "windows-update provisioner" => errors }
24
35
  end
25
36
 
37
+ def filters_valid?
38
+ return false if !filters.is_a?(Array)
39
+ filters.each do |a|
40
+ return false if !a.kind_of?(String)
41
+ end
42
+ return true
43
+ end
44
+ end
45
+
46
+ Config
47
+ end
48
+
49
+ provisioner("windows-update") do
50
+ class Provisioner < Vagrant.plugin("2", :provisioner)
26
51
  # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/ui.rb
27
52
  # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/provisioner.rb
28
53
  # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/communicator.rb
@@ -32,7 +57,7 @@ module VagrantPlugins
32
57
  @machine.communicate.upload(
33
58
  File.join(File.dirname(__FILE__), "vagrant-windows-update", "windows-update.ps1"),
34
59
  remote_path)
35
- command = "PowerShell -ExecutionPolicy Bypass -OutputFormat Text -File #{remote_path}"
60
+ command = "PowerShell -ExecutionPolicy Bypass -OutputFormat Text -EncodedCommand #{windows_update_encoded_command(remote_path, config.filters)}"
36
61
  loop do
37
62
  begin
38
63
  until @machine.communicate.ready?
@@ -85,6 +110,22 @@ module VagrantPlugins
85
110
  @machine.ui.info(data.chomp, options)
86
111
  end
87
112
  end
113
+
114
+ def windows_update_encoded_command(remote_path, filters)
115
+ # NB you can get the string back with:
116
+ # Base64.decode64(encoded).force_encoding("utf-16le")
117
+ return Base64.strict_encode64("#{remote_path}#{windows_update_filters_argument(filters)}".encode("utf-16le"))
118
+ end
119
+
120
+ def windows_update_filters_argument(filters)
121
+ return "" if !filters
122
+ arg = " -Filters "
123
+ filters.each_with_index do |filter, i|
124
+ arg += "," if i > 0
125
+ arg += "'#{filter.gsub("'", "''")}'" # escape single quotes with another single quote.
126
+ end
127
+ return arg
128
+ end
88
129
  end
89
130
 
90
131
  Provisioner
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module WindowsUpdate
3
- VERSION = "0.0.5"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
@@ -13,6 +13,18 @@
13
13
  # see IUpdate interface
14
14
  # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
15
15
 
16
+ param(
17
+ [string[]]$Filters = @('include:$_.AutoSelectOnWebSites'),
18
+ [int]$UpdateLimit = 100
19
+ )
20
+
21
+ $mock = $false
22
+
23
+ function ExitWithCode($exitCode) {
24
+ $host.SetShouldExit($exitCode)
25
+ Exit
26
+ }
27
+
16
28
  Set-StrictMode -Version Latest
17
29
 
18
30
  $ErrorActionPreference = 'Stop'
@@ -21,7 +33,23 @@ trap {
21
33
  Write-Output "ERROR: $_"
22
34
  Write-Output (($_.ScriptStackTrace -split '\r?\n') -replace '^(.*)$','ERROR: $1')
23
35
  Write-Output (($_.Exception.ToString() -split '\r?\n') -replace '^(.*)$','ERROR EXCEPTION: $1')
24
- Exit 1
36
+ ExitWithCode 1
37
+ }
38
+
39
+ if ($mock) {
40
+ $mockWindowsUpdatePath = 'C:\Windows\Temp\windows-update-count-mock.txt'
41
+ if (!(Test-Path $mockWindowsUpdatePath)) {
42
+ Set-Content $mockWindowsUpdatePath 10
43
+ }
44
+ $count = [int]::Parse((Get-Content $mockWindowsUpdatePath).Trim())
45
+ if ($count) {
46
+ Write-Output "Synthetic reboot countdown counter is at $count"
47
+ Set-Content $mockWindowsUpdatePath (--$count)
48
+ Write-Output 'Rebooting...'
49
+ ExitWithCode 101
50
+ }
51
+ Write-Output 'No Windows updates found'
52
+ ExitWithCode 0
25
53
  }
26
54
 
27
55
  Add-Type @'
@@ -62,34 +90,47 @@ function Wait-Condition {
62
90
  }
63
91
  }
64
92
 
65
- function ExitWhenRebootRequired([bool]$forceReboot=$false) {
66
- $rebootRequired = $forceReboot
67
-
93
+ function ExitWhenRebootRequired($rebootRequired = $false) {
68
94
  # check for pending Windows Updates.
69
95
  if (!$rebootRequired) {
70
96
  $systemInformation = New-Object -ComObject 'Microsoft.Update.SystemInfo'
71
- $rebootRequired = $rebootRequired -or $systemInformation.RebootRequired
97
+ $rebootRequired = $systemInformation.RebootRequired
72
98
  }
73
99
 
74
100
  # check for pending Windows Features.
75
101
  if (!$rebootRequired) {
76
102
  $pendingPackagesKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending'
77
103
  $pendingPackagesCount = (Get-ChildItem -ErrorAction SilentlyContinue $pendingPackagesKey | Measure-Object).Count
78
- $rebootRequired = $rebootRequired -or $pendingPackagesCount -gt 0
104
+ $rebootRequired = $pendingPackagesCount -gt 0
79
105
  }
80
106
 
81
107
  if ($rebootRequired) {
82
- if (!$forceReboot) {
83
- Write-Output 'Pending Reboot detected. Waiting for the Windows Modules Installer to exit...'
84
- Wait-Condition {(Get-Process -ErrorAction SilentlyContinue TiWorker,TrustedInstaller | Measure-Object).Count -eq 0}
85
- }
108
+ Write-Output 'Pending Reboot detected. Waiting for the Windows Modules Installer to exit...'
109
+ Wait-Condition {(Get-Process -ErrorAction SilentlyContinue TiWorker | Measure-Object).Count -eq 0}
86
110
  Write-Output 'Rebooting...'
87
- Exit 0
111
+ ExitWithCode 101
88
112
  }
89
113
  }
90
114
 
91
115
  ExitWhenRebootRequired
92
116
 
117
+ $updateFilters = $Filters | ForEach-Object {
118
+ $action, $expression = $_ -split ':',2
119
+ New-Object PSObject -Property @{
120
+ Action = $action
121
+ Expression = [ScriptBlock]::Create($expression)
122
+ }
123
+ }
124
+
125
+ function Test-IncludeUpdate($filters, $update) {
126
+ foreach ($filter in $filters) {
127
+ if (Where-Object -InputObject $update $filter.Expression) {
128
+ return $filter.Action -eq 'include'
129
+ }
130
+ }
131
+ return $false
132
+ }
133
+
93
134
  $updateSession = New-Object -ComObject 'Microsoft.Update.Session'
94
135
  $updateSession.ClientApplicationID = 'vagrant-windows-update'
95
136
 
@@ -98,20 +139,24 @@ $updatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
98
139
  $updatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
99
140
  $updateSearcher = $updateSession.CreateUpdateSearcher()
100
141
  $searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
142
+ $rebootRequired = $false
101
143
  for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
102
144
  $update = $searchResult.Updates.Item($i)
145
+ $updateDate = $update.LastDeploymentChangeTime.ToString('yyyy-MM-dd')
146
+ $updateSize = ($update.MaxDownloadSize/1024/1024).ToString('0.##')
147
+ $updateSummary = "Windows update ($updateDate; $updateSize MB): $($update.Title)"
103
148
 
104
149
  if ($update.InstallationBehavior.CanRequestUserInput) {
150
+ Write-Output "Skipped (CanRequestUserInput) $updateSummary"
105
151
  continue
106
152
  }
107
153
 
108
- if (!$update.AutoSelectOnWebSites) {
154
+ if (!(Test-IncludeUpdate $updateFilters $update)) {
155
+ Write-Output "Skipped (filter) $updateSummary"
109
156
  continue
110
157
  }
111
158
 
112
- $updateDate = $update.LastDeploymentChangeTime.ToString('yyyy-MM-dd')
113
- $updateSize = ($update.MaxDownloadSize/1024/1024).ToString('0.##')
114
- Write-Output "Found Windows update ($updateDate; $updateSize MB): $($update.Title)"
159
+ Write-Output "Found $updateSummary"
115
160
 
116
161
  $update.AcceptEula() | Out-Null
117
162
 
@@ -120,6 +165,10 @@ for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
120
165
  }
121
166
 
122
167
  $updatesToInstall.Add($update) | Out-Null
168
+ if ($updatesToInstall.Count -ge $UpdateLimit) {
169
+ $rebootRequired = $true
170
+ break
171
+ }
123
172
  }
124
173
 
125
174
  if ($updatesToDownload.Count) {
@@ -134,8 +183,7 @@ if ($updatesToInstall.Count) {
134
183
  $updateInstaller = $updateSession.CreateUpdateInstaller()
135
184
  $updateInstaller.Updates = $updatesToInstall
136
185
  $installResult = $updateInstaller.Install()
137
- ExitWhenRebootRequired $true
138
- Exit 0
186
+ ExitWhenRebootRequired ($installResult.RebootRequired -or $rebootRequired)
187
+ } else {
188
+ Write-Output 'No Windows updates found'
139
189
  }
140
-
141
- Write-Output 'No Windows updates found'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-windows-update
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rui Lopes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-31 00:00:00.000000000 Z
11
+ date: 2018-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -53,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
53
  version: '0'
54
54
  requirements: []
55
55
  rubyforge_project:
56
- rubygems_version: 2.5.2
56
+ rubygems_version: 2.5.2.1
57
57
  signing_key:
58
58
  specification_version: 4
59
59
  summary: Vagrant plugin for installing Windows updates.