WebHook近些年來變得越來越流行,github,gitlab等代碼托管平台都提供webhook功能.關於webhook這里不做詳細介紹,大家可以參閱讀相關互聯網書籍或者材料來更深了解.可以把它簡單理解為某一事件完成以后的一個回調.
在持續集成環境里,我們可以使用Sonarqube的webhook功能來實現持續發布和發布包歸檔功能.大致思路是當項目構建成功后我們可以通過webhook通知服務器構建任務已完成,接下來web 服務器可以根據webhook傳遞的參數決定要處理的包是哪個項目的包(通過項目的key來判斷),如何對包進行歸檔以及如何把包發布到遠程服務器(通過http,ftp等方式).
Web服務器搭建
要想實現webhook,必須有一個預先設計好的web服務器供回調.我們預先建好了一個web項目(新建一個mvc項目即可)
由於是測試,我們這里就用Visual的模板生成一個mvc項目,然后在Home控制器下新建一個Action,代碼如下:
public IActionResult HookTest([FromBody]SonarQubeVm sonar)
{
return new EmptyResult();
}
由於Sonarqube webhook是通過post方式提交,因此action必須支持Post方式請求.
參數sonar是SonarQubeVm
類型的參數,是根據Sonarqube請求規格文檔構建的,代碼如下:
public class SonarQubeVm
{
public DateTime? AnalysedAt { get; set; }
public SonarProjectInfo project { get; set; }
public string ServerUrl { get; set; }
public string Status { get; set; }
public string TaskId { get; set; }
}
public class SonarProjectInfo
{
public string Key { get; set; }
public string Name { get; set; }
}
注意以上參數並不完全包含Sonarqube返回的所有參數,我們只取了部分.關於Sonarqube webhook完整請求參數請查看
http://localhost:9000/documentation/webhooks
,localhost:9000是默認的服務器的端口號,如果你更改了端口號或者從外網請求,則要更改為實際的ip地址(或者域名)加上指定的端口號.
WebHook調用
在Sonarqube里可以通過兩種方式調用webhook,全局模式和項目模式.全局模式每當一個構建成功后就會觸發.項目模式則只有指定的項目構建以后才會觸發.
全局模式
如上圖示,我們點擊全局Administration
然后點擊configuration在出現的下拉列表里選擇WebHooks,此時右上角有一個create
按鈕,點擊后出現一個彈出框,要求輸入名稱和url,然后點擊確定.
我們以調試模式啟動web項目,然后執行一個Sonarqube項目構建,執行完成后看看是否有請求到達web服務器.
MSBuild.SonarQube.Runner.exe begin /k:"mytest" /n:"mytest" /v:"v3.0" /d:sonar.cs.opencover.reportsPaths="%CD%\testcover.xml"
msbuild.exe
"E:\personalproject\newTest2018\ConsoleApp1\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe" -output:"%CD%\testcover.xml" -register:user -target:"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" -targetargs:"%CD%\bin\Debug\NunitTest.dll"
MSBuild.SonarQube.Runner.exe end
以上是我們上一節講單元測試的時候執行的代碼只修改了版本號.我們執行它.
等等以上代碼都執行完成,稍等片刻我們就可以看到http請求擊中斷點
通過serverurl是來自localhost:9000,我們可以確定是Sonarqube發來的請求.項目的key和name都是我們設定的mytest
以上僅是個示例程序,沒有有用代碼,實際項目中我們可以根據webhook請求的key來獲取到構建的是哪個項目,然后根據預先設定的邏輯決定把它歸檔到哪里,以及把它發布到哪些web服務器下的哪些目錄里(前面我們講過通過ftt方式發布web項目,可以在這里使用)
項目模式
項目模式與全局模式設置完全一樣,只是入口不同,項目模式需要進入項目的Administration
標簽里進行設置.仍然以mytest項目為例子,我們打開mytest項目,進入到Administration標簽里選擇webhooks即可.
設置和全局設置一樣,這里不再贅述.
請求認證
通過以上配置,我們成功搞好了webhook功能,然而以上代碼根本無法使用到生產環境中,因為沒有對請求進行認證,如果任何人都可以調用構建服務器地址則后果不堪設想.我們必須對請求進行認證,然后再決定是否執行相應邏輯.
由於sonaqube不支持設置header,因此我們無法使用復雜的請求認證.只能使用基本的http認證
我們在服務端增加以下類
public class BasicAuthenticationAttribute: ActionFilterAttribute
{
protected string Username { get; set; } = "sto";
protected string Password { get; set; } = "sto";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"].ToString();
if (!String.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
var user = new { Name = cred[0], Pass = cred[1] };
if (user.Name == Username && user.Pass == Password) return;
}
filterContext.Result = new UnauthorizedResult();
}
}
以上代碼中,我們通過硬編碼方式指定了用戶名和密碼,實際生產環境中我們可以通過查詢數據庫來獲取用戶名和密碼. 在OnActionExecuting
重寫方法中我們通過頭信息Authorization
獲取加密的Base64字符串,然后通過:
分割獲取到用戶名和密碼.然后和真實用戶名密碼做對比然后決定下一步動作.
我們把這個filter加到請求方法上.
改造后的代碼如下:
[BasicAuthentication]
public IActionResult HookTest([FromBody]SonarQubeVm sonar)
{
return new EmptyResult();
}
我們把webhook的url更新為如下:
http://sto:sto1@localhost:49442/home/HookTest
實際上服務端邏輯要求賬戶和密碼都必須是sto能請求,我們故意把密碼改為sto1看看請求是否能成功.
我們仍然執行前面的構建代碼,只是把版本號增加一下.
我們再進入webhook管理界面,可以看到請求失敗了
我們點擊失日期后面的四框
圖標,可以看到失敗的狀態是401
我們把請求地址更改為如下
http://sto:sto@localhost:49442/home/HookTest
這里sto1改為服務器期待的sto,請求就能成功了.