common.psm1 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. # Copyright 2019 The Kubernetes Authors.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. <#
  15. .SYNOPSIS
  16. Library containing common variables and code used by other PowerShell modules
  17. and scripts for configuring Windows nodes.
  18. #>
  19. # IMPORTANT PLEASE NOTE:
  20. # Any time the file structure in the `windows` directory changes,
  21. # `windows/BUILD` and `k8s.io/release/lib/releaselib.sh` must be manually
  22. # updated with the changes.
  23. # We HIGHLY recommend not changing the file structure, because consumers of
  24. # Kubernetes releases depend on the release structure remaining stable.
  25. # Disable progress bar to increase download speed.
  26. $ProgressPreference = 'SilentlyContinue'
  27. # REDO_STEPS affects the behavior of a node that is rebooted after initial
  28. # bringup. When true, on a reboot the scripts will redo steps that were
  29. # determined to have already been completed once (e.g. to overwrite
  30. # already-existing config files). When false the scripts will perform the
  31. # minimum required steps to re-join this node to the cluster.
  32. $REDO_STEPS = $false
  33. Export-ModuleMember -Variable REDO_STEPS
  34. # Writes $Message to the console. Terminates the script if $Fatal is set.
  35. function Log-Output {
  36. param (
  37. [parameter(Mandatory=$true)] [string]$Message,
  38. [switch]$Fatal
  39. )
  40. Write-Host "${Message}"
  41. if (${Fatal}) {
  42. Exit 1
  43. }
  44. }
  45. # Checks if a file should be written or overwritten by testing if it already
  46. # exists and checking the value of the global $REDO_STEPS variable. Emits an
  47. # informative message if the file already exists.
  48. #
  49. # Returns $true if the file does not exist, or if it does but the global
  50. # $REDO_STEPS variable is set to $true. Returns $false if the file exists and
  51. # the caller should not overwrite it.
  52. function ShouldWrite-File {
  53. param (
  54. [parameter(Mandatory=$true)] [string]$Filename
  55. )
  56. if (Test-Path $Filename) {
  57. if ($REDO_STEPS) {
  58. Log-Output "Warning: $Filename already exists, will overwrite it"
  59. return $true
  60. }
  61. Log-Output "Skip: $Filename already exists, not overwriting it"
  62. return $false
  63. }
  64. return $true
  65. }
  66. # Returns the GCE instance metadata value for $Key. If the key is not present
  67. # in the instance metadata returns $Default if set, otherwise returns $null.
  68. function Get-InstanceMetadata {
  69. param (
  70. [parameter(Mandatory=$true)] [string]$Key,
  71. [parameter(Mandatory=$false)] [string]$Default
  72. )
  73. $url = "http://metadata.google.internal/computeMetadata/v1/instance/$Key"
  74. try {
  75. $client = New-Object Net.WebClient
  76. $client.Headers.Add('Metadata-Flavor', 'Google')
  77. return ($client.DownloadString($url)).Trim()
  78. }
  79. catch [System.Net.WebException] {
  80. if ($Default) {
  81. return $Default
  82. }
  83. else {
  84. Log-Output "Failed to retrieve value for $Key."
  85. return $null
  86. }
  87. }
  88. }
  89. # Returns the GCE instance metadata value for $Key where key is an "attribute"
  90. # of the instance. If the key is not present in the instance metadata returns
  91. # $Default if set, otherwise returns $null.
  92. function Get-InstanceMetadataAttribute {
  93. param (
  94. [parameter(Mandatory=$true)] [string]$Key,
  95. [parameter(Mandatory=$false)] [string]$Default
  96. )
  97. return Get-InstanceMetadata "attributes/$Key" $Default
  98. }
  99. function Validate-SHA {
  100. param(
  101. [parameter(Mandatory=$true)] [string]$Hash,
  102. [parameter(Mandatory=$true)] [string]$Path,
  103. [parameter(Mandatory=$true)] [string]$Algorithm
  104. )
  105. $actual = Get-FileHash -Path $Path -Algorithm $Algorithm
  106. # Note: Powershell string comparisons are case-insensitive by default, and this
  107. # is important here because Linux shell scripts produce lowercase hashes but
  108. # Powershell Get-FileHash produces uppercase hashes. This must be case-insensitive
  109. # to work.
  110. if ($actual.Hash -ne $Hash) {
  111. Log-Output "$Path corrupted, $Algorithm $actual doesn't match expected $Hash"
  112. Throw ("$Path corrupted, $Algorithm $actual doesn't match expected $Hash")
  113. }
  114. }
  115. # Attempts to download the file from URLs, trying each URL until it succeeds.
  116. # It will loop through the URLs list forever until it has a success. If
  117. # successful, it will write the file to OutFile. You can optionally provide a
  118. # Hash argument with an optional Algorithm, in which case it will attempt to
  119. # validate the downloaded file against the hash. SHA1 will be used if Algorithm
  120. # is not provided.
  121. function MustDownload-File {
  122. param (
  123. [parameter(Mandatory=$false)] [string]$Hash,
  124. [parameter(Mandatory=$false)] [string]$Algorithm = 'SHA1',
  125. [parameter(Mandatory=$true)] [string]$OutFile,
  126. [parameter(Mandatory=$true)] [System.Collections.Generic.List[String]]$URLs,
  127. [parameter(Mandatory=$false)] [System.Collections.IDictionary]$Headers = @{}
  128. )
  129. While($true) {
  130. ForEach($url in $URLs) {
  131. # If the URL is for GCS and the node has dev storage scope, add the
  132. # service account token to the request headers.
  133. if (($url -match "^https://storage`.googleapis`.com.*") -and $(Check-StorageScope)) {
  134. $Headers["Authorization"] = "Bearer $(Get-Credentials)"
  135. }
  136. # Attempt to download the file
  137. Try {
  138. # TODO(mtaufen): When we finally get a Windows version that has Powershell 6
  139. # installed we can set `-MaximumRetryCount 6 -RetryIntervalSec 10` to make this even more robust.
  140. $result = Invoke-WebRequest $url -Headers $Headers -OutFile $OutFile -TimeoutSec 300
  141. } Catch {
  142. $message = $_.Exception.ToString()
  143. Log-Output "Failed to download file from $url. Will retry. Error: $message"
  144. continue
  145. }
  146. # Attempt to validate the hash
  147. if ($Hash) {
  148. Try {
  149. Validate-SHA -Hash $Hash -Path $OutFile -Algorithm $Algorithm
  150. } Catch {
  151. $message = $_.Exception.ToString()
  152. Log-Output "Hash validation of $url failed. Will retry. Error: $message"
  153. continue
  154. }
  155. Log-Output "Downloaded $url ($Algorithm = $Hash)"
  156. return
  157. }
  158. Log-Output "Downloaded $url"
  159. return
  160. }
  161. }
  162. }
  163. # Returns the default service account token for the VM, retrieved from
  164. # the instance metadata.
  165. function Get-Credentials {
  166. While($true) {
  167. $data = Get-InstanceMetadata -Key "service-accounts/default/token"
  168. if ($data) {
  169. return ($data | ConvertFrom-Json).access_token
  170. }
  171. Start-Sleep -Seconds 1
  172. }
  173. }
  174. # Returns True if the VM has the dev storage scope, False otherwise.
  175. function Check-StorageScope {
  176. While($true) {
  177. $data = Get-InstanceMetadata -Key "service-accounts/default/scopes"
  178. if ($data) {
  179. return ($data -match "auth/devstorage")
  180. }
  181. Start-Sleep -Seconds 1
  182. }
  183. }
  184. # This compiles some C# code that can make syscalls, and pulls the
  185. # result into our powershell environment so we can make syscalls from this script.
  186. # We make syscalls directly, because whatever the powershell cmdlets do under the hood,
  187. # they can't seem to open the log files concurrently with writers.
  188. # See https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke
  189. # for details on which unmanaged types map to managed types.
  190. $SyscallDefinitions = @'
  191. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  192. public static extern IntPtr CreateFileW(
  193. String lpFileName,
  194. UInt32 dwDesiredAccess,
  195. UInt32 dwShareMode,
  196. IntPtr lpSecurityAttributes,
  197. UInt32 dwCreationDisposition,
  198. UInt32 dwFlagsAndAttributes,
  199. IntPtr hTemplateFile
  200. );
  201. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  202. public static extern bool SetFilePointer(
  203. IntPtr hFile,
  204. Int32 lDistanceToMove,
  205. IntPtr lpDistanceToMoveHigh,
  206. UInt32 dwMoveMethod
  207. );
  208. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  209. public static extern bool SetEndOfFile(
  210. IntPtr hFile
  211. );
  212. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  213. public static extern bool CloseHandle(
  214. IntPtr hObject
  215. );
  216. '@
  217. $Kernel32 = Add-Type -MemberDefinition $SyscallDefinitions -Name 'Kernel32' -Namespace 'Win32' -PassThru
  218. # Close-Handle closes the specified open file handle.
  219. # On failure, throws an exception.
  220. function Close-Handle {
  221. param (
  222. [parameter(Mandatory=$true)] [System.IntPtr]$Handle
  223. )
  224. $ret = $Kernel32::CloseHandle($Handle)
  225. $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
  226. if (-not $ret) {
  227. throw "Failed to close open file handle ${Handle}, system error code: ${err}"
  228. }
  229. }
  230. # Open-File tries to open the file at the specified path with ReadWrite access mode and ReadWrite file share mode.
  231. # On success, returns an open file handle.
  232. # On failure, throws an exception.
  233. function Open-File {
  234. param (
  235. [parameter(Mandatory=$true)] [string]$Path
  236. )
  237. $lpFileName = $Path
  238. $dwDesiredAccess = [System.IO.FileAccess]::ReadWrite
  239. $dwShareMode = [System.IO.FileShare]::ReadWrite # Fortunately golang also passes these same flags when it creates the log files, so we can open it concurrently.
  240. $lpSecurityAttributes = [System.IntPtr]::Zero
  241. $dwCreationDisposition = [System.IO.FileMode]::Open
  242. $dwFlagsAndAttributes = [System.IO.FileAttributes]::Normal
  243. $hTemplateFile = [System.IntPtr]::Zero
  244. $handle = $Kernel32::CreateFileW($lpFileName, $dwDesiredAccess, $dwShareMode, $lpSecurityAttributes, $dwCreationDisposition, $dwFlagsAndAttributes, $hTemplateFile)
  245. $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
  246. if ($handle -eq -1) {
  247. throw "Failed to open file ${Path}, system error code: ${err}"
  248. }
  249. return $handle
  250. }
  251. # Truncate-File truncates the file in-place by opening it, moving the file pointer to the beginning,
  252. # and setting the end of file to the file pointer's location.
  253. # On failure, throws an exception.
  254. # The file must have been originally created with FILE_SHARE_WRITE for this to be possible.
  255. # Fortunately Go creates files with FILE_SHARE_READ|FILE_SHARE_WRITE by for all os.Open calls,
  256. # so our log writers should be doing the right thing.
  257. function Truncate-File {
  258. param (
  259. [parameter(Mandatory=$true)] [string]$Path
  260. )
  261. $INVALID_SET_FILE_POINTER = 0xffffffff
  262. $NO_ERROR = 0
  263. $FILE_BEGIN = 0
  264. $handle = Open-File -Path $Path
  265. # https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointer
  266. # Docs: Because INVALID_SET_FILE_POINTER is a valid value for the low-order DWORD of the new file pointer,
  267. # you must check both the return value of the function and the error code returned by GetLastError to
  268. # determine whether or not an error has occurred. If an error has occurred, the return value of SetFilePointer
  269. # is INVALID_SET_FILE_POINTER and GetLastError returns a value other than NO_ERROR.
  270. $ret = $Kernel32::SetFilePointer($handle, 0, [System.IntPtr]::Zero, $FILE_BEGIN)
  271. $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
  272. if ($ret -eq $INVALID_SET_FILE_POINTER -and $err -ne $NO_ERROR) {
  273. Close-Handle -Handle $handle
  274. throw "Failed to set file pointer for handle ${handle}, system error code: ${err}"
  275. }
  276. $ret = $Kernel32::SetEndOfFile($handle)
  277. $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
  278. if ($ret -eq 0) {
  279. Close-Handle -Handle $handle
  280. throw "Failed to set end of file for handle ${handle}, system error code: ${err}"
  281. }
  282. Close-Handle -Handle $handle
  283. }
  284. # FileRotationConfig defines the common options for file rotation.
  285. class FileRotationConfig {
  286. # Force rotation, ignoring $MaxBackupInterval and $MaxSize criteria.
  287. [bool]$Force
  288. # Maximum time since last backup, after which file will be rotated.
  289. # When no backups exist, Rotate-File acts as if -MaxBackupInterval has not elapsed,
  290. # instead relying on the other criteria.
  291. [TimeSpan]$MaxBackupInterval
  292. # Maximum file size, after which file will be rotated.
  293. [int]$MaxSize
  294. # Maximum number of backup archives to maintain.
  295. [int]$MaxBackups
  296. }
  297. # New-FileRotationConfig constructs a FileRotationConfig with default options.
  298. function New-FileRotationConfig {
  299. param (
  300. # Force rotation, ignoring $MaxBackupInterval and $MaxSize criteria.
  301. [parameter(Mandatory=$false)] [switch]$Force,
  302. # Maximum time since last backup, after which file will be rotated.
  303. # When no backups exist, Rotate-File acts as if -MaxBackupInterval has not elapsed,
  304. # instead relying on the other criteria.
  305. # Defaults to daily rotations.
  306. [parameter(Mandatory=$false)] [TimeSpan]$MaxBackupInterval = $(New-TimeSpan -Day 1),
  307. # Maximum file size, after which file will be rotated.
  308. [parameter(Mandatory=$false)] [int]$MaxSize = 100mb,
  309. # Maximum number of backup archives to maintain.
  310. [parameter(Mandatory=$false)] [int]$MaxBackups = 5
  311. )
  312. $config = [FileRotationConfig]::new()
  313. $config.Force = $Force
  314. $config.MaxBackupInterval = $MaxBackupInterval
  315. $config.MaxSize = $MaxSize
  316. $config.MaxBackups = $MaxBackups
  317. return $config
  318. }
  319. # Get-Backups returns a list of paths to backup files for the original file path -Path,
  320. # assuming that backup files are in the same directory, with a prefix matching
  321. # the original file name and a .zip suffix.
  322. function Get-Backups {
  323. param (
  324. # Original path of the file for which backups were created (no suffix).
  325. [parameter(Mandatory=$true)] [string]$Path
  326. )
  327. $parent = Split-Path -Parent -Path $Path
  328. $leaf = Split-Path -Leaf -Path $Path
  329. $files = Get-ChildItem -File -Path $parent |
  330. Where-Object Name -like "${leaf}*.zip"
  331. return $files
  332. }
  333. # Trim-Backups deletes old backups for the log file identified by -Path until only -Count remain.
  334. # Deletes backups with the oldest CreationTime first.
  335. function Trim-Backups {
  336. param (
  337. [parameter(Mandatory=$true)] [int]$Count,
  338. [parameter(Mandatory=$true)] [string]$Path
  339. )
  340. if ($Count -lt 0) {
  341. $Count = 0
  342. }
  343. # If creating a new backup will exceed $Count, delete the oldest files
  344. # until we have one less than $Count, leaving room for the new one.
  345. # If the pipe results in zero items, $backups is $null, and if it results
  346. # in only one item, PowerShell doesn't wrap in an array, so we check both cases.
  347. # In the latter case, this actually caused it to often trim all backups, because
  348. # .Length is also a property of FileInfo (size of the file)!
  349. $backups = Get-Backups -Path $Path | Sort-Object -Property CreationTime
  350. if ($backups -and $backups.GetType() -eq @().GetType() -and $backups.Length -gt $Count) {
  351. $num = $backups.Length - $Count
  352. $rmFiles = $backups | Select-Object -First $num
  353. ForEach ($file in $rmFiles) {
  354. Remove-Item $file.FullName
  355. }
  356. }
  357. }
  358. # Backup-File creates a copy of the file at -Path.
  359. # The name of the backup is the same as the file,
  360. # with the suffix "-%Y%m%d-%s" to identify the time of the backup.
  361. # Returns the path to the backup file.
  362. function Backup-File {
  363. param (
  364. [parameter(Mandatory=$true)] [string]$Path
  365. )
  366. $date = Get-Date -UFormat "%Y%m%d-%s"
  367. $dest = "${Path}-${date}"
  368. Copy-Item -Path $Path -Destination $dest
  369. return $dest
  370. }
  371. # Compress-BackupFile creates a compressed archive containing the file
  372. # at -Path and subsequently deletes the file at -Path. We split backup
  373. # and compression steps to minimize time between backup and truncation,
  374. # which helps minimize log loss.
  375. function Compress-BackupFile {
  376. param (
  377. [parameter(Mandatory=$true)] [string]$Path
  378. )
  379. Compress-Archive -Path $Path -DestinationPath "${Path}.zip"
  380. Remove-Item -Path $Path
  381. }
  382. # Rotate-File rotates the log file at -Path by first making a compressed copy of the original
  383. # log file with the suffix "-%Y%m%d-%s" to identify the time of the backup, then truncating
  384. # the original file in-place. Rotation is performed according to the options in -Config.
  385. function Rotate-File {
  386. param (
  387. # Path to the log file to rotate.
  388. [parameter(Mandatory=$true)] [string]$Path,
  389. # Config for file rotation.
  390. [parameter(Mandatory=$true)] [FileRotationConfig]$Config
  391. )
  392. function rotate {
  393. # If creating a new backup will exceed $MaxBackups, delete the oldest files
  394. # until we have one less than $MaxBackups, leaving room for the new one.
  395. Trim-Backups -Count ($Config.MaxBackups - 1) -Path $Path
  396. $backupPath = Backup-File -Path $Path
  397. Truncate-File -Path $Path
  398. Compress-BackupFile -Path $backupPath
  399. }
  400. # Check Force
  401. if ($Config.Force) {
  402. rotate
  403. return
  404. }
  405. # Check MaxSize.
  406. $file = Get-Item $Path
  407. if ($file.Length -gt $Config.MaxSize) {
  408. rotate
  409. return
  410. }
  411. # Check MaxBackupInterval.
  412. $backups = Get-Backups -Path $Path | Sort-Object -Property CreationTime
  413. if ($backups.Length -ge 1) {
  414. $lastBackupTime = $backups[0].CreationTime
  415. $now = Get-Date
  416. if ($now - $lastBackupTime -gt $Config.MaxBackupInterval) {
  417. rotate
  418. return
  419. }
  420. }
  421. }
  422. # Rotate-Files rotates the log files in directory -Path that match -Pattern.
  423. # Rotation is performed by Rotate-File, according to -Config.
  424. function Rotate-Files {
  425. param (
  426. # Pattern that file names must match to be rotated. Does not include parent path.
  427. [parameter(Mandatory=$true)] [string]$Pattern,
  428. # Path to the log directory containing files to rotate.
  429. [parameter(Mandatory=$true)] [string]$Path,
  430. # Config for file rotation.
  431. [parameter(Mandatory=$true)] [FileRotationConfig]$Config
  432. )
  433. $files = Get-ChildItem -File -Path $Path | Where-Object Name -match $Pattern
  434. ForEach ($file in $files) {
  435. try {
  436. Rotate-File -Path $file.FullName -Config $Config
  437. } catch {
  438. Log-Output "Caught exception rotating $($file.FullName): $($_.Exception)"
  439. }
  440. }
  441. }
  442. # Schedule-LogRotation schedules periodic log rotation with the Windows Task Scheduler.
  443. # Rotation is performed by Rotate-Files, according to -Pattern and -Config.
  444. # The system will check whether log files need to be rotated at -RepetitionInterval.
  445. function Schedule-LogRotation {
  446. param (
  447. # Pattern that file names must match to be rotated. Does not include parent path.
  448. [parameter(Mandatory=$true)] [string]$Pattern,
  449. # Path to the log directory containing files to rotate.
  450. [parameter(Mandatory=$true)] [string]$Path,
  451. # Interval at which to check logs against rotation criteria.
  452. # Minimum 1 minute, maximum 31 days (see https://docs.microsoft.com/en-us/windows/desktop/taskschd/taskschedulerschema-interval-repetitiontype-element).
  453. [parameter(Mandatory=$true)] [TimeSpan]$RepetitionInterval,
  454. # Config for file rotation.
  455. [parameter(Mandatory=$true)] [FileRotationConfig]$Config
  456. )
  457. # Write a powershell script to a file that imports this module ($PSCommandPath)
  458. # and calls Rotate-Files with the configured arguments.
  459. $scriptPath = "C:\rotate-kube-logs.ps1"
  460. New-Item -Force -ItemType file -Path $scriptPath | Out-Null
  461. Set-Content -Path $scriptPath @"
  462. `$ErrorActionPreference = 'Stop'
  463. Import-Module -Force ${PSCommandPath}
  464. `$maxBackupInterval = New-Timespan -Days $($Config.MaxBackupInterval.Days) -Hours $($Config.MaxBackupInterval.Hours) -Minutes $($Config.MaxBackupInterval.Minutes) -Seconds $($Config.MaxBackupInterval.Seconds)
  465. `$config = New-FileRotationConfig -Force:`$$($Config.Force) -MaxBackupInterval `$maxBackupInterval -MaxSize $($Config.MaxSize) -MaxBackups $($Config.MaxBackups)
  466. Rotate-Files -Pattern '${Pattern}' -Path '${Path}' -Config `$config
  467. "@
  468. # The task will execute the rotate-kube-logs.ps1 script created above.
  469. # We explicitly set -WorkingDirectory to $Path for safety's sake, otherwise
  470. # it runs in %windir%\system32 by default, which sounds dangerous.
  471. $action = New-ScheduledTaskAction -Execute "powershell" -Argument "-NoLogo -NonInteractive -File ${scriptPath}" -WorkingDirectory $Path
  472. # Start the task immediately, and trigger the task once every $RepetitionInterval.
  473. $trigger = New-ScheduledTaskTrigger -Once -At $(Get-Date) -RepetitionInterval $RepetitionInterval
  474. # Run the task as the same user who is currently running this script.
  475. $principal = New-ScheduledTaskPrincipal $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
  476. # Just use the default task settings.
  477. $settings = New-ScheduledTaskSettingsSet
  478. # Create the ScheduledTask object from the above parameters.
  479. $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settings -Description "Rotate Kubernetes logs"
  480. # Register the new ScheduledTask with the Task Scheduler.
  481. # Always try to unregister and re-register, in case it already exists (e.g. across reboots).
  482. $name = "RotateKubeLogs"
  483. try {
  484. Unregister-ScheduledTask -Confirm:$false -TaskName $name
  485. } catch {} finally {
  486. Register-ScheduledTask -TaskName $name -InputObject $task
  487. }
  488. }
  489. # Returns true if this node is part of a test cluster (see
  490. # cluster/gce/config-test.sh). $KubeEnv is a hash table containing the kube-env
  491. # metadata keys+values.
  492. function Test-IsTestCluster {
  493. param (
  494. [parameter(Mandatory=$true)] [hashtable]$KubeEnv
  495. )
  496. if ($KubeEnv.Contains('TEST_CLUSTER') -and `
  497. ($KubeEnv['TEST_CLUSTER'] -eq 'true')) {
  498. return $true
  499. }
  500. return $false
  501. }
  502. # Returns true if this node uses a plugin to support authentication to the
  503. # master, e.g. for TPM-based authentication. $KubeEnv is a hash table
  504. # containing the kube-env metadata keys+values.
  505. function Test-NodeUsesAuthPlugin {
  506. param (
  507. [parameter(Mandatory=$true)] [hashtable]$KubeEnv
  508. )
  509. return $KubeEnv.Contains('EXEC_AUTH_PLUGIN_URL')
  510. }
  511. # Export all public functions:
  512. Export-ModuleMember -Function *-*