我们在脚本执行模式下使用TeamCity powershell,作为具有快照和工件依赖关系的管道的一部分。我们有一个相当强大的系统,并且已经使用这个特定的过程几年了,所以这不是我第一次调试的全新代码。悲哀地。它通常会工作,直到随机不工作。当错误确实发生时,TeamCity Agent是不同的。
我们流程的这一部分执行一些代码部署和一些日志备份。为了完全进行备份,我们必须确保QA或开发人员在他们的办公桌上查看日志时不会打开文件,并且可能会以读写模式等打开它们。因为他们将从笔记本电脑/台式机打开它们,所以它们自然是SMB共享。因此,我们在下面有一个函数,它应该关闭给定服务器上打开的文件。我说应该,因为每隔一段时间它就会抛出这个错误,我似乎既无法捕捉到它(甚至在本地),也无法抑制它,所以它破坏了TeamCity的运行。(我匿名了
...SNIP
代码是专有名称或专有输出的任何地方)
您实际上可以在您的机器上测试它,只需导航到
\\yourhostname\c$\somefilepath\somefile
并查看它是否会显示文件已打开。一旦你通读了代码并看到它在做什么,它就不会在你的机器上失败,但如果你采取了所有的“预防措施”,你可能会在本地重现错误。
function Close-SMBApplicationLocks {
<#
.SYNOPSIS
Closes Active SMB Sessions for Default or User Supplied Paths
.DESCRIPTION
This function is used to prevent interruption to deployments by closing any SMB locks
in application paths. Defaults to closing sessions in folders matching regex
...SNIP
.PARAMETER Paths
[string[]] A string array of paths or path segments to match sessions against.
.EXAMPLE
Close-SMBApplicationLocks
...SNIP
.EXAMPLE
Close-SMBApplicationLocks -Paths @("TEMP")
...SNIP
#>
[CmdletBinding()]
param(
[Alias("SharePaths")]
[Parameter(Mandatory=$false)]
[string[]]$Paths
)
$pathsToUse = Test-IsNull ($Paths -join "|") "...SNIP"
Write-Verbose ("Looking for SMB Sessions Matching Path: {0}" -f $pathsToUse)
$smbSessions = @(Get-SmbOpenFile | Where-Object {$_.Path -match $pathsToUse})
if ((Test-IsCollectionNullOrEmpty $smbSessions)) {
Write-Host ("No Matching SMB Sessions Found")
return
}
Write-Verbose "Found $($smbSessions.Count) Matching SMB Sessions"
$uniqueFileIds = ($smbSessions).FileId | Sort-Object -Unique
foreach ($fileId in $uniqueFileIds) {
$session = @($smbSessions | Where-Object { $_.FileId -eq $fileId })[0]
$sessionId = $session.SessionId
$username = $session.ClientUserName
$path = $session.Path
Write-Verbose "Closing FileId $fileId on SMB Session $sessionId for user $username in path $path"
try {
if ($null -ne (Get-SmbOpenFile -FileId $fileId)) {
## Yes this is FOUR ways to suppress output.
## Microsoft has proven remarkably resilient at showing an error here.
## the ErrorAction Continue still throws an error in TeamCity but not locally
## The try catch doesn't catch
## The Out-Null is because on the off chance the redirect works on the output, it shouldn't show the faux-error
## The output redirection is because this error isn't written to "standard error"
## TeamCity seems to be not honoring this output redirection in the shell it's running under to execute this block
(Close-SmbOpenFile -FileId $fileId -Force -ErrorAction Continue *>&1) | Out-Null
## Run this line instead of the above to actually see the error pretty frequently, by my testing
## Close-SmbOpenFile -FileId $fileId -Force
}
} catch {
$errorMessage = $_.Exception.Message
Write-Warning "An Error Occurred While Trying to Close Session $sessionId : $errorMessage"
}
}
}
我们最初是通过会话的,但我更改为这个$fileId版本的代码,看看我是否可以用unique等清理它。这些似乎没有改善。
我们完全可以这样做
Get-SMBOpenFile | Where-Object <pathmatch> | Close-SMBOpenFile
(例如,请参见此处
https://serverfault.com/questions/718875/close-locked-file-in-windows-share-using-powershell-and-openfiles
在这里
https://community.spiceworks.com/topic/2218597-issue-with-close-smbopenfile
)但正如你所看到的,我们想记录下我们正在关闭它,以防我们发现出了什么问题,这有助于我们理解是什么。
以下是我必须克服的错误:
[Clearing File Locks] No MSFT_SMBOpenFile objects found with property 'FileId' equal to '825975900669'. Verify the value of the property
[Clearing File Locks] and retry.
[Clearing File Locks] At C:\Program Files\WindowsPowerShell\Modules\...SNIP.psm1:2566 char:34
[Clearing File Locks] + $jobs | ForEach-Object { Receive-Job -Job $_ }
[Clearing File Locks] + ~~~~~~~~~~~~~~~~~~~
[Clearing File Locks] + CategoryInfo : ObjectNotFound: (825975900669:UInt64) [Get-SmbOpenFile], CimJobException
[Clearing File Locks] + FullyQualifiedErrorId : CmdletizationQuery_NotFound_FileId,Get-SmbOpenFile
[Clearing File Locks] + PSComputerName : localhost
[Clearing File Locks]
[Clearing File Locks] Process exited with code 1
但问题是,在我执行删除操作之前,我会再次检查文件是否已打开,对吗?所以我说“这存在吗?是吗?关闭它”,但我得到了一个对我来说毫无意义的错误。
我试图对返回的对象提出其他方法,以确保我需要删除文件,或者如果有什么东西说“应该跳过”,但我什么都想不出来。
既然我在这里似乎没有选择,有没有我没有考虑过的替代方法?某种CIMInstance命令?如果有的话,我显然是雪盲了。这确实在机器上本地运行,而不是在整个会话中运行。
我组织中的某个人终于注意到,错误确实表明使用FileId参数获取SmbOpenFile是失败的,因此这一定是相同的重定向错误。目前看来,我可能已经有了答案。
雪盲症很糟糕
相关机器注意事项:
PS Z:\git\...SNIP> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.17763.1007
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.17763.1007
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
PS Z:\git\...SNIP> Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture, CSName, WindowsDirectory
Caption : Microsoft Windows 10 Enterprise LTSC
Version : 10.0.17763
ServicePackMajorVersion : 0
OSArchitecture : 64-bit
CSName : ...SNIP
WindowsDirectory : C:\Windows
但这也在Windows Server环境中运行。PowerShell的版本相同。所有服务器上的最新Windows补丁等。我知道,我们还没有将机队转移到2019年的数据中心,但据我所知,机队中有大约800台服务器正在生产/测试中,这些事情当然需要时间。如果2016年是问题所在,那就是问题所在。
PS Z:\git\...SNIP> Get-CimInstance Win32_OperatingSystem -ComputerName ...SNIP | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture, CSName, WindowsDirectory
Caption : Microsoft Windows Server 2016 Datacenter
Version : 10.0.14393
ServicePackMajorVersion : 0
OSArchitecture : 64-bit
CSName : ...SNIP
WindowsDirectory : C:\Windows
也许我的解决方案是让TeamCity尊重输出重定向?是Server 2016不支持输出重定向吗?这只是试图可靠地关闭这些连接的一线希望吗?是否有我不想检查的文件系统版本?
当我尝试在以下位置创建文件时
\\mymachine\c$\temp\temp.txt
打开它,这就是我得到的(请注意,我只使用记事本打开文件,所以没有正在进行的锁定)
PS Z:\git\devops_powershell> Get-SMBOpenFile
FileId SessionId Path ShareRelativePath ClientComputerName ClientUserName
------ --------- ---- ----------------- ------------------ --------------
1065151889485 1065151889409 C:\ ...SNIP ...SNIP
1065151889489 1065151889409 C:\ ...SNIP ...SNIP
1065151889613 1065151889409 C:\temp temp ...SNIP ...SNIP
1065151889617 1065151889409 C:\temp temp ...SNIP ...SNIP
1065151889833 1065151889409 C:\temp temp ...SNIP ...SNIP
PS Z:\git\...SNIP> Get-SmbOpenFile -FileId 1065151889833 | Select-Object -Property *
SmbInstance : Default
ClientComputerName : ...SNIP
ClientUserName : ...SNIP
ClusterNodeName :
ContinuouslyAvailable : False
Encrypted : False
FileId : 1065151889833
Locks : 0
Path : C:\temp
Permissions : 1048736
ScopeName : *
SessionId : 1065151889409
ShareRelativePath : temp
Signed : True
PSComputerName :
CimClass : ROOT/Microsoft/Windows/SMB:MSFT_SmbOpenFile
CimInstanceProperties : {ClientComputerName, ClientUserName, ClusterNodeName, ContinuouslyAvailable...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
PS Z:\git\...SNIP> Get-SmbOpenFile -FileId 1065151889617 | Select-Object -Property *
SmbInstance : Default
ClientComputerName : ...SNIP
ClientUserName : ...SNIP
ClusterNodeName :
ContinuouslyAvailable : False
Encrypted : False
FileId : 1065151889617
Locks : 0
Path : C:\temp
Permissions : 1048705
ScopeName : *
SessionId : 1065151889409
ShareRelativePath : temp
Signed : True
PSComputerName :
CimClass : ROOT/Microsoft/Windows/SMB:MSFT_SmbOpenFile
CimInstanceProperties : {ClientComputerName, ClientUserName, ClusterNodeName, ContinuouslyAvailable...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
我应该只关注以下情况吗
Locks -gt 0?