问题描述
我一整天都在盯着这个问题,我对我所看到的完全感到困惑.发生了两个问题,不幸的是,其中一个只发生在生产环境中,所以我无法按照我想要的方式对其进行测试.
我将在最后提供所有背景和相关信息以及代码.在您查看代码之前,我在接下来的几节中说的一些内容没有多大意义.
背景信息:
(我已经三重验证了所有这些信息)
- 这个类是从 TFS 2010 WWF 构建模板调用的.它依赖于我在另一个工具中创建的库,以使用 UniDK 将文件部署到我们的 Universe 环境
- 部署本身运行良好,问题在于日志记录和返回代码.
- 如果下面的类返回代码 0,则构建被标记为成功",返回代码为 1 的部分成功",如果有任何其他返回代码,则标记为失败".
- 文件只部署一次(objDeploy.DeployFiles() 只调用一次)
- serverInfo.ServerCount = 2
- 第二个环境(计数器 = 1)的 serverInfo.ServerActive 为 False
- 为了帮助追踪问题,我在 ProcessResults() 中添加了额外的日志记录以将不同集合的值输出到单独的文件,但我没有机会使用额外的代码运行它
症状:
- 在生产环境中,它以返回码 1 (exitCode = 1) 退出
- 这是 results 字符串返回的内容:
服务器名称的结果
部署成功!
************************
服务器名称的结果
部署成功!
************************
服务器名称的结果
部署错误,请查看日志
************************
服务器名称的结果
部署成功!
************************
服务器名称的结果
部署成功!
************************
3.在QA中,我们有6次服务器名称的结果"消息,但每次都说部署成功
4. 部署日志文件中的所有内容显示所有部署的文件返回的代码为0(这意味着Result40Collection、BackupErrorCollection和BadErrorCollection应该是空的.我稍后会解释为什么这特别重要)
我期望发生的事情:
- 退出代码 = 0
- 构建 = 成功
- 结果:
服务器名称的结果
部署成功!
************************
根据 TFS 构建日志中的结果,我预计会发生什么:在本节中,我忽略了返回多个条目的事实,只关注表示存在错误的条目
- 退出代码 = 2
- 构建 = 失败
- 结果:
服务器名称的结果
部署错误,请查看日志
************************
代码:
导入系统导入 Microsoft.TeamFoundation.Build.Client导入系统.活动进口 RMUtilities<BuildActivity(HostEnvironmentOption.All)>Public NotInheritable 类 DeployU2Files继承 CodeActivity#Region参数"' 在参数中Property inServerDataSet As InArgument(Of DataSet) ' 包含服务器信息的数据集Property inSourcesDirectory As InArgument(Of String) ' 正在部署的源目录的完整路径Property inBuildName As InArgument(Of String) ' 构建的名称,用于备份Property inLogDirectory As InArgument(Of String) ' 日志文件夹的路径' 输出参数Property outExitCode As OutArgument(Of Integer) ' 产生的错误代码,0 是好的Property outResult As OutArgument(Of String) ' 结果字符串#End Region ' "参数"#Region变量"' 从构建传入的变量Dim dsServerDataSet 作为数据集Dim strSourcesDirectory 作为字符串Dim strBuildName As StringDim strLogDirectory 作为字符串' 构建使用的变量Dim serverInfo As XMLReader将文件列表调暗为 U2FileListParser' 结果变量Dim exitCode As Integer = 0Dim 结果 As String = ""#End Region '"变量"Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext)' 设置工作变量dsServerDataSet = context.GetValue(Me.inServerDataSet)strSourcesDirectory = context.GetValue(Me.inSourcesDirectory)strBuildName = context.GetValue(Me.inBuildName)strLogDirectory = context.GetValue(Me.inLogDirectory)' 创建部署所需的基础对象尝试serverInfo = 新 XMLReader(dsServerDataSet)fileList = New U2FileListParser(strSourcesDirectory)将 ex 捕获为 NullReferenceException抛出新的 NullReferenceException("无效的 XML 数据集", ex)退出子Catch ex 作为例外Throw New Exception("错误处理文件列表:" & ex.Message, ex)结束尝试' 首先判断是否有文件要部署将文件计数调暗为整数尝试使用文件列表fileCount = .DeployList.Count + .PreDeployList.Count + .PostDeployList.Count结束于Catch ex 作为例外抛出新的 ArgumentException("没有要部署的文件")结束尝试If fileCount = 0 Then Throw New ArgumentException("没有要部署的文件")' 然后,检查以确保有服务器要部署到如果 serverInfo.ServerCount = 0 那么抛出新的 ArgumentException("XML 文件中没有列出要部署到的服务器")万一' 遍历 XML 文件中的每个服务器对于计数器 = 0 到 serverInfo.ServerCount - 1' 设置当前环境serverInfo.ChosenEnvironment = 计数器' 检查以确保服务器处于活动状态.如果不是,则跳过如果 serverInfo.ServerActive 那么' 创建新的日志对象以将所有输出记录到一个文件中,该文件的名称是要部署到的服务器的名称Dim logger = New RMLogging(strLogDirectory & "\" & serverInfo.ServerHostName & ".log")logger.Header = "自动部署" &vbCrLf &_内部版本号:" &strBuildName &vbCrLf &_"日期:" &DateTime.Now.ToString("MMM ddd d yyyy hh:mm:ss tt")' 创建部署对象Dim objDeploy As New U2Deploy(serverInfo, fileList, logger, strBuildName)' 将文件部署到环境中,然后检查结果以确保它们objDeploy.DeployFiles()' 这将确定部署的成功级别,并且还会解析消息以获取日志ProcessResults(objDeploy, serverInfo.ServerHostName)' 如果写入日志有问题,则将日志的全文添加到结果中如果 objDeploy.FullLog.Length >0 那么结果 &= objDeploy.FullLog &vbCrLf结果 &= "****************************************" &vbCrLfEnd If 'objDeploy.FullLog.Length >0'处理对象记录器 = 没有objDeploy.Clear()objDeploy = 无End If ' serverInfo.ServerActiveNext ' counter = 0 到 serverInfo.ServerCount - 1SetResults(退出代码,结果,上下文)结束子''' <总结>''' 将根据部署结果更改退出代码''' </summary>''' <param name="objDeploy">包含集合的 U2Deploy 对象</param>''' Private Sub ProcessResults(objDeploy As U2Deploy, serverName As String)Dim currentErrorCode As Integer = 0结果 &= "结果 " &服务器名称 &vbCrLf如果 objDeploy.Result40Collection.Count() >0 那么当前错误代码 = 1结果 &="类型 40 错误,请查看日志" &vbCrLfEnd If ' objDeploy.Result40Collection.Count() >0如果 objDeploy.BackupErrorCollection.Count >0 那么当前错误代码 = 1结果 &="文件备份错误,请查看日志" &vbCrLfEnd If 'objDeploy.BackupErrorCollection.Count >0如果 objDeploy.BadErrorCollection.Count >0 那么当前错误代码 = 2结果 &= "部署错误,请查看日志" &vbCrLf万一如果 currentErrorCode = 0 那么结果 &="部署成功!"&vbCrLf结果 &= "************************" &vbCrLf如果 currentErrorCode >exitCode 然后exitCode = currentErrorCode结束子' 设置传出消息和退出代码.工作流使用它来向 buld 本身添加消息Private Sub SetResults(ByVal exitCode As Int32, message As String, ByRef context As CodeActivityContext)context.SetValue(Me.outExitCode, exitCode)context.SetValue(Me.outResult, message)结束子结束类
更新:我已经能够在打开详细日志记录的情况下在 QA 中运行两次,这是结果(同样,完全不一致).我仅使用 VS2013 来查看和运行构建,对构建使用的类的任何代码更改都在 VS2010 中完成.
运行 1:
运行 2:
实际上我昨晚刚刚解决了这个问题.问题实际上出在我阅读的有关 WWF 流程的文档中.这段代码实际上是正确执行的,并返回了正确的值,但由于不正确的指令,它在工作流中走错了路,这使得这段代码看起来是错误的.
打开诊断日志后,我花了一段时间才再次出现此错误,一旦我看到它,我立即知道问题出在哪里.
根本原因是我的印象是 WriteBuildError 会将构建标记为失败,而不是将其标记为部分成功,这使我走上了错误的故障排除路径.
I have been staring at this problem all day and I'm completely baffled by what I'm seeing.There are two issues occurring, and unfortunately, one of them only happens in Production so I can't test it the way I'd like.
I will give all the background and relevant information up front with the code at the end. Some of what I say in these next couple sections won't make much sense until you review the code.
Background info:
(I have triple verified all this information)
- This class is being called from a TFS 2010 WWF build template. It relies on a library I've created in another tool to deploy files to our Universe environment using UniDK
- The deployment itself is working fine, the problem is with the logging and return codes.
- The build is marked as "Success" if the class below returns a code of 0, "Partial Success" with a return code of 1, and "Failed" if there is any other return code.
- The files are only being deployed one time (objDeploy.DeployFiles() is only called once)
- serverInfo.ServerCount = 2
- serverInfo.ServerActive for the second environment (counter = 1) is False
- To help track down the issues, I have added additional logging in ProcessResults() to output the values of the different collections to a separate file, but I haven't had an opportunity to run it with the additional code
Symptoms:
- In Production, it is exiting with a return code of 1 (exitCode = 1)
- This is what is returned by the results string:
What I expect to happen:
- exitCode = 0
- Build = succeeded
- results:
What I expect to happen based on the results in the TFS build log:In this section, I'm ignoring the fact that there are multiple entries being returned and only focusing on the one that says there were errors
- exitCode = 2
- Build = Failed
- results:
Code:
Imports System
Imports Microsoft.TeamFoundation.Build.Client
Imports System.Activities
Imports RMUtilities
<BuildActivity(HostEnvironmentOption.All)>
Public NotInheritable Class DeployU2Files
Inherits CodeActivity
#Region "Arguments"
' In Arguments
Property inServerDataSet As InArgument(Of DataSet) ' Dataset containing the server information
Property inSourcesDirectory As InArgument(Of String) ' Full path to the Source directory being deployed
Property inBuildName As InArgument(Of String) ' Name of the build, to be used for backups
Property inLogDirectory As InArgument(Of String) ' Path to the log folder
' Out Arguments
Property outExitCode As OutArgument(Of Integer) ' Resulting error code, 0 is good
Property outResult As OutArgument(Of String) ' Result string
#End Region ' "Arguments"
#Region "Variables"
' Variables passed in from the build
Dim dsServerDataSet As DataSet
Dim strSourcesDirectory As String
Dim strBuildName As String
Dim strLogDirectory As String
' Variables used by the build
Dim serverInfo As XMLReader
Dim fileList As U2FileListParser
' Result variables
Dim exitCode As Integer = 0
Dim results As String = ""
#End Region '"Variables"
Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext)
' Sets the working variables
dsServerDataSet = context.GetValue(Me.inServerDataSet)
strSourcesDirectory = context.GetValue(Me.inSourcesDirectory)
strBuildName = context.GetValue(Me.inBuildName)
strLogDirectory = context.GetValue(Me.inLogDirectory)
' Creates the base objects needed for the deployment
Try
serverInfo = New XMLReader(dsServerDataSet)
fileList = New U2FileListParser(strSourcesDirectory)
Catch ex As NullReferenceException
Throw New NullReferenceException("Invalid XML Dataset", ex)
Exit Sub
Catch ex As Exception
Throw New Exception("Error processing file list: " & ex.Message, ex)
End Try
' First, determine if there are files to deploy
Dim fileCount As Integer
Try
With fileList
fileCount = .DeployList.Count + .PreDeployList.Count + .PostDeployList.Count
End With
Catch ex As Exception
Throw New ArgumentException("No files to deploy")
End Try
If fileCount = 0 Then Throw New ArgumentException("No files to deploy")
' Then, check to make sure there are servers to deploy to
If serverInfo.ServerCount = 0 Then
Throw New ArgumentException("No servers listed in XML file to deploy to")
End If
' Iterates through each server in the XML file
For counter = 0 To serverInfo.ServerCount - 1
' Sets the current environment
serverInfo.ChosenEnvironment = counter
' Checks to make sure the server is active. If it isn't, it's skipped
If serverInfo.ServerActive Then
' Creates new logging object to log all output to a file with the name of the server being deployed to
Dim logger = New RMLogging(strLogDirectory & "\" & serverInfo.ServerHostName & ".log")
logger.Header = "Automated deploy" & vbCrLf & _
"Build Number: " & strBuildName & vbCrLf & _
"Date: " & DateTime.Now.ToString("MMM ddd d yyyy hh:mm:ss tt")
' Creates the deployment object
Dim objDeploy As New U2Deploy(serverInfo, fileList, logger, strBuildName)
' Deploys the files to the environment, then checks the results to make sure they
objDeploy.DeployFiles()
' This will determine the success level of the deployment, and also parses the message for the log
ProcessResults(objDeploy, serverInfo.ServerHostName)
' If there was a problem writing the log, then add the full text of the log to the results
If objDeploy.FullLog.Length > 0 Then
results &= objDeploy.FullLog & vbCrLf
results &= "**********************************" & vbCrLf
End If ' objDeploy.FullLog.Length > 0
' Disposes the objects
logger = Nothing
objDeploy.Clear()
objDeploy = Nothing
End If ' serverInfo.ServerActive
Next ' counter = 0 To serverInfo.ServerCount - 1
SetResults(exitCode, results, context)
End Sub
''' <summary>
''' Will change the exite code based on the results of the deployment
''' </summary>
''' <param name="objDeploy">U2Deploy object that contains the collections</param>
''' <remarks></remarks>
Private Sub ProcessResults(objDeploy As U2Deploy, serverName As String)
Dim currentErrorCode As Integer = 0
results &= "Results for " & serverName & vbCrLf
If objDeploy.Result40Collection.Count() > 0 Then
currentErrorCode = 1
results &= "Type 40 errors, please review the log" & vbCrLf
End If ' objDeploy.Result40Collection.Count() > 0
If objDeploy.BackupErrorCollection.Count > 0 Then
currentErrorCode = 1
results &= "File backup errors, please review the log" & vbCrLf
End If ' objDeploy.BackupErrorCollection.Count > 0
If objDeploy.BadErrorCollection.Count > 0 Then
currentErrorCode = 2
results &= "Deployment errors, please review the log" & vbCrLf
End If
If currentErrorCode = 0 Then results &= "Deployment successful!" & vbCrLf
results &= "***********************" & vbCrLf
If currentErrorCode > exitCode Then exitCode = currentErrorCode
End Sub
' Sets the outgoing message and exit code. This is used by the workflow to add messages to the buld itself
Private Sub SetResults(ByVal exitCode As Int32, message As String, ByRef context As CodeActivityContext)
context.SetValue(Me.outExitCode, exitCode)
context.SetValue(Me.outResult, message)
End Sub
End Class
UPDATE:I've been able to run this in QA twice with verbose logging turned on, and here are the results (Again, totally inconsistent). I am using VS2013 only to view and run the builds, any code changes to the classes used by the build are done within VS2010.
Run 1:
Run 2:
I actually just had a resolution to this last night. The problem was actually with the documentation I had read on the WWF process. This code was actually executing properly, and returned the proper values, but because of the incorrect instructions, it was taking the wrong path in the workflow, which made it appear that this code was wrong.
It took me a while for this error to occur again with the diagnostic logging turned on, and once I saw it, I knew immediately what the problem was.
The root cause was that I was under the impression that WriteBuildError would mark the build as Failed, instead it marked it as Partially Succeeded, which put me on the wrong troubleshooting path.
这篇关于VB.Net 方法似乎被多次调用,但实际上并非如此,并返回不一致的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!