public async Task <DeployStatus> PollDeploymentStatusAsync(string deploymentUrl, string userName, string password) { DeployStatus deployStatus = DeployStatus.Pending; var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaxMinutesToWait)); if (_logMessages) { _log.LogMessage(Resources.ZIPDEPLOY_DeploymentStatusPolling); } while (!tokenSource.IsCancellationRequested && deployStatus != DeployStatus.Success && deployStatus != DeployStatus.Failed && deployStatus != DeployStatus.Unknown) { try { deployStatus = await GetDeploymentStatusAsync(deploymentUrl, userName, password, RetryCount, TimeSpan.FromSeconds(RetryDelaySeconds), tokenSource); if (_logMessages) { _log.LogMessage(String.Format(Resources.ZIPDEPLOY_DeploymentStatus, Enum.GetName(typeof(DeployStatus), deployStatus))); } } catch (HttpRequestException) { return(DeployStatus.Unknown); } await Task.Delay(TimeSpan.FromSeconds(StatusRefreshDelaySeconds)); } return(deployStatus); }
public static async Task <DeployStatus> WaitForDedicatedBuildToComplete(HttpClient client, Site functionApp) { // There is a tracked Locking issue in kudulite causing Race conditions, so we have to use this API // to gather deployment progress. ColoredConsole.Write("Remote build in progress, please wait"); while (true) { var json = await InvokeRequest <IDictionary <string, string> >(client, HttpMethod.Get, "/api/isdeploying"); if (bool.TryParse(json["value"], out bool isDeploying)) { if (!isDeploying) { string deploymentId = await GetLatestDeploymentId(client, functionApp, restrictedToken : null); DeployStatus status = await GetDeploymentStatusById(client, functionApp, restrictedToken : null, id : deploymentId); ColoredConsole.Write($"done{Environment.NewLine}"); return(status); } } else { throw new CliException($"Expected \"value\" from /api/isdeploying endpoing to be a boolean. Actual: {json["value"]}"); } ColoredConsole.Write("."); await Task.Delay(5000); } }
public static async Task <DeployStatus> WaitForServerSideBuild(HttpClient client, Site functionApp, string accessToken, string managementUrl) { ColoredConsole.WriteLine("Server side build in progress, please wait"); DeployStatus statusCode = DeployStatus.Pending; DateTime logLastUpdate = DateTime.MinValue; string id = null; while (string.IsNullOrEmpty(id)) { string restrictedToken = await GetRestrictedToken(functionApp, accessToken, managementUrl); id = await GetLatestDeploymentId(client, functionApp, restrictedToken); await Task.Delay(TimeSpan.FromSeconds(Constants.KuduLiteDeploymentConstants.StatusRefreshSeconds)); } while (statusCode != DeployStatus.Success && statusCode != DeployStatus.Failed) { string restrictedToken = await GetRestrictedToken(functionApp, accessToken, managementUrl); statusCode = await GetDeploymentStatusById(client, functionApp, restrictedToken, id); logLastUpdate = await DisplayDeploymentLog(client, functionApp, restrictedToken, id, logLastUpdate); await Task.Delay(TimeSpan.FromSeconds(Constants.KuduLiteDeploymentConstants.StatusRefreshSeconds)); } return(statusCode); }
public async Task <DeployStatus> PerformServerSideBuild(Site functionApp, Func <Task <Stream> > zipFileFactory, Func <HttpClient, Task <DeployStatus> > deploymentStatusPollTask) { using (var handler = new ProgressMessageHandler(new HttpClientHandler())) using (var client = GetRemoteZipClient(new Uri($"https://{functionApp.ScmUri}"), handler)) using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri( $"api/zipdeploy?isAsync=true&author={Environment.MachineName}", UriKind.Relative))) { ColoredConsole.WriteLine("Creating archive for current directory..."); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); (var content, var length) = CreateStreamContentZip(await zipFileFactory()); request.Content = content; HttpResponseMessage response = await PublishHelper.InvokeLongRunningRequest(client, handler, request, length, "Uploading"); await PublishHelper.CheckResponseStatusAsync(response, "Uploading archive..."); // Streaming deployment status for Linux Server Side Build DeployStatus status = await deploymentStatusPollTask(client); if (status == DeployStatus.Success) { ColoredConsole.WriteLine(Green("Remote build succeeded!")); } else if (status == DeployStatus.Failed) { throw new CliException("Remote build failed!"); } else if (status == DeployStatus.Unknown) { ColoredConsole.WriteLine(Yellow($"Failed to retrieve remote build status, please visit https://{functionApp.ScmUri}/api/deployments")); } return(status); } }
public static async Task <DeployStatus> WaitForRemoteBuild(HttpClient client, Site functionApp) { ColoredConsole.WriteLine("Remote build in progress, please wait..."); DeployStatus statusCode = DeployStatus.Pending; DateTime logLastUpdate = DateTime.MinValue; string id = null; while (string.IsNullOrEmpty(id)) { id = await GetLatestDeploymentId(client, functionApp); await Task.Delay(TimeSpan.FromSeconds(Constants.KuduLiteDeploymentConstants.StatusRefreshSeconds)); } while (statusCode != DeployStatus.Success && statusCode != DeployStatus.Failed && statusCode != DeployStatus.Unknown) { try { statusCode = await GetDeploymentStatusById(client, functionApp, id); logLastUpdate = await DisplayDeploymentLog(client, functionApp, id, logLastUpdate); } catch (HttpRequestException) { return(DeployStatus.Unknown); } await Task.Delay(TimeSpan.FromSeconds(Constants.KuduLiteDeploymentConstants.StatusRefreshSeconds)); } return(statusCode); }
//Common code internal static void PushAndDeployApps(string repoCloneUrl, string defaultBranchName, string verificationText, HttpStatusCode expectedResponseCode, string verificationLogText, DeployStatus expectedStatus = DeployStatus.Success, string resourcePath = "", string httpMethod = "GET", string jsonPayload = "", bool deleteSCM = false) { using (new LatencyLogger("PushAndDeployApps - " + repoCloneUrl)) { Uri uri; if (!Uri.TryCreate(repoCloneUrl, UriKind.Absolute, out uri)) { uri = null; } string randomTestName = uri != null?Path.GetFileNameWithoutExtension(repoCloneUrl) : repoCloneUrl; ApplicationManager.Run(randomTestName, appManager => { // Act using (TestRepository testRepository = Git.Clone(randomTestName, uri != null ? repoCloneUrl : null)) { using (new LatencyLogger("GitDeploy")) { appManager.GitDeploy(testRepository.PhysicalPath, defaultBranchName); } } var results = appManager.DeploymentManager.GetResultsAsync().Result.ToList(); // Assert Assert.Equal(1, results.Count); Assert.Equal(expectedStatus, results[0].Status); var url = new Uri(new Uri(appManager.SiteUrl), resourcePath); if (!String.IsNullOrEmpty(verificationText)) { KuduAssert.VerifyUrl(url.ToString(), verificationText, expectedResponseCode, httpMethod, jsonPayload); } if (!String.IsNullOrEmpty(verificationLogText)) { KuduAssert.VerifyLogOutput(appManager, results[0].Id, verificationLogText.Trim()); } if (deleteSCM) { using (new LatencyLogger("SCMAndWebDelete")) { appManager.RepositoryManager.Delete(deleteWebRoot: false, ignoreErrors: false).Wait(); } } }); } }
/// <summary> /// 指定された配置状態のグリッド行を選択可能とするかのフラグを取得します。 /// </summary> /// <param name="status">配置状態</param> /// <returns>グリッド行を選択可能とするかのフラグ</returns> public static bool CanSelect(this DeployStatus status) { switch (status) { case DeployStatus.NoBuild: return(false); case DeployStatus.NoDeploy: case DeployStatus.Updated: case DeployStatus.Degrade: case DeployStatus.Deployed: return(true); default: throw new ArgumentException("InvalidArgument:GetStatusName"); } }
private static async Task <string> GetLatestDeploymentId(HttpClient client, Site functionApp) { var json = await InvokeRequest <List <Dictionary <string, string> > >(client, HttpMethod.Get, "/deployments"); // Automatically ordered by received time var latestDeployment = json.First(); if (latestDeployment.TryGetValue("status", out string statusString)) { DeployStatus status = ConvertToDeployementStatus(statusString); if (status != DeployStatus.Pending) { return(latestDeployment["id"]); } } return(null); }
public async Task <DeployStatus> PerformServerSideBuild(Site functionApp, Func <Task <Stream> > zipFileFactory, Func <HttpClient, Task <DeployStatus> > deploymentStatusPollTask, bool attachRestrictedToken = true) { if (string.IsNullOrEmpty(functionApp.ScmUri)) { throw new CliException("Remote build is a new feature added to function apps. " + $"Your function app {functionApp.SiteName} does not support remote build as it was created before August 1st, 2019."); } using (var handler = new ProgressMessageHandler(new HttpClientHandler())) using (var client = GetRemoteZipClient(new Uri($"https://{functionApp.ScmUri}"), handler)) using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri( $"api/zipdeploy?isAsync=true&author={Environment.MachineName}", UriKind.Relative))) { ColoredConsole.WriteLine("Creating archive for current directory..."); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); (var content, var length) = CreateStreamContentZip(await zipFileFactory()); request.Content = content; if (attachRestrictedToken) { // Attach x-ms-site-restricted-token for Linux Consumption remote build string restrictedToken = await KuduLiteDeploymentHelpers.GetRestrictedToken(functionApp, AccessToken, ManagementURL); request.Headers.Add("x-ms-site-restricted-token", restrictedToken); } HttpResponseMessage response = await PublishHelper.InvokeLongRunningRequest(client, handler, request, length, "Uploading"); await PublishHelper.CheckResponseStatusAsync(response, "Uploading archive..."); // Streaming deployment status for Linux Server Side Build DeployStatus status = await deploymentStatusPollTask(client); if (status == DeployStatus.Success) { ColoredConsole.WriteLine(Green("Remote build succeeded!")); } else if (status == DeployStatus.Failed) { throw new CliException("Remote build failed!"); } return(status); } }
private static DeployResult CreateResult(DeployStatus status, bool?lastSuccess = null, bool isActive = false, bool isTemp = false, bool toDelete = false) { var id = ++_id; DateTime?lastSuccessTime = null; if (lastSuccess.HasValue) { if (lastSuccess.Value) { lastSuccessTime = DateTime.Now; } } else { if (status == DeployStatus.Success) { lastSuccessTime = DateTime.Now; } } var resultId = id.ToString(); if (isActive) { resultId = "activeId"; } else if (toDelete) { resultId = "delete-" + id; } return(new DeployResult { Id = resultId, Status = status, LastSuccessEndTime = lastSuccessTime, IsTemporary = isTemp }); }
public async Task <DeployStatus> PerformServerSideBuild(Site functionApp, Func <Task <Stream> > zipFileFactory, Func <HttpClient, Task <DeployStatus> > deploymentStatusPollTask) { if (string.IsNullOrEmpty(functionApp.ScmUri)) { throw new CliException($"Remote build is a new feature added to function apps.{Environment.NewLine}" + $"Your function app {functionApp.SiteName} does not support remote build as it was created before August 1st, 2019.{Environment.NewLine}" + $"Please use '--build local', '--build-native-deps' or manually enable remote build.{Environment.NewLine}" + $"For more information, please visit https://aka.ms/remotebuild"); } using (var handler = new ProgressMessageHandler(new HttpClientHandler())) using (var client = GetRemoteZipClient(new Uri($"https://{functionApp.ScmUri}"), handler)) using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri( $"api/zipdeploy?isAsync=true&author={Environment.MachineName}", UriKind.Relative))) { ColoredConsole.WriteLine("Creating archive for current directory..."); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); (var content, var length) = CreateStreamContentZip(await zipFileFactory()); request.Content = content; HttpResponseMessage response = await PublishHelper.InvokeLongRunningRequest(client, handler, request, length, "Uploading"); await PublishHelper.CheckResponseStatusAsync(response, "Uploading archive..."); // Streaming deployment status for Linux Server Side Build DeployStatus status = await deploymentStatusPollTask(client); if (status == DeployStatus.Success) { ColoredConsole.WriteLine(Green("Remote build succeeded!")); } else if (status == DeployStatus.Failed) { throw new CliException("Remote build failed!"); } return(status); } }
/// <summary> /// 配置状態を配置状態名に変換します。 /// </summary> /// <param name="status">配置状態</param> /// <returns>配置状態名</returns> public static string ToName(this DeployStatus status) { switch (status) { case DeployStatus.NoBuild: return("未ビルド"); case DeployStatus.NoDeploy: return("未配置"); case DeployStatus.Updated: return("更新"); case DeployStatus.Degrade: return("デグレード"); case DeployStatus.Deployed: return("配置済み"); default: throw new ArgumentException("InvalidArgument:GetStatusName"); } }
public async Task <DeployStatus> PerformServerSideBuild(Site functionApp, Func <Task <Stream> > zipFileFactory) { using (var handler = new ProgressMessageHandler(new HttpClientHandler())) using (var client = GetRemoteZipClient(new Uri($"https://{functionApp.ScmUri}"), handler)) using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri( $"api/zipdeploy?isAsync=true&author={Environment.MachineName}", UriKind.Relative))) { ColoredConsole.WriteLine("Creating archive for current directory..."); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); // Attach x-ms-site-restricted-token for Linux Consumption remote build string restrictedToken = await KuduLiteDeploymentHelpers.GetRestrictedToken(functionApp, AccessToken, ManagementURL); (var content, var length) = CreateStreamContentZip(await zipFileFactory()); request.Content = content; request.Headers.Add("x-ms-site-restricted-token", restrictedToken); HttpResponseMessage response = await PublishHelper.InvokeLongRunningRequest(client, handler, request, length, "Uploading"); await PublishHelper.CheckResponseStatusAsync(response, "uploading archive"); // Streaming deployment status for Linux Dynamic Server Side Build DeployStatus status = await KuduLiteDeploymentHelpers.WaitForServerSideBuild(client, functionApp, AccessToken, ManagementURL); if (status == DeployStatus.Success) { ColoredConsole.WriteLine(Green("Server side build succeeded!")); } else if (status == DeployStatus.Failed) { ColoredConsole.WriteLine(Red("Server side build failed!")); } return(status); } }
public string GetUnitStatus() { return(DeployStatus != DeployStatus.NA ? DeployStatus.ToString() : Status.ToString()); }
internal async Task PostDeploymentAndVerifyUrl(ApplicationManager appManager, string url, bool isMercurial, DeployStatus status, string content = null, string path = null) { TestTracer.Trace("PostDeploymentAndVerifyUrl: {0}", url); var payload = new JObject(); payload["url"] = url; payload["format"] = "basic"; if (isMercurial) { payload["scm"] = "hg"; } await DeployPayloadHelperAsync(appManager, client => client.PostAsJsonAsync("deploy", payload)); var results = (await appManager.DeploymentManager.GetResultsAsync()).ToList(); Assert.True(results.Count > 0); Assert.Equal(status, results[0].Status); if (!String.IsNullOrEmpty(content)) { KuduAssert.VerifyUrl(appManager.SiteUrl + path, content); } }
public async Task PollDeploymentStatusTest_ForValidResponses(HttpStatusCode responseStatusCode, DeployStatus expectedDeployStatus) { // Arrange string deployUrl = "https://sitename.scm.azurewebsites.net/DeploymentStatus?Id=knownId"; Action <Mock <IHttpClient>, bool> verifyStep = (client, result) => { client.Verify(c => c.GetAsync( It.Is <Uri>(uri => string.Equals(uri.AbsoluteUri, deployUrl, StringComparison.Ordinal)), It.IsAny <CancellationToken>())); Assert.Equal($"{UserAgentName}/{UserAgentVersion}", client.Object.DefaultRequestHeaders.GetValues("User-Agent").FirstOrDefault()); Assert.True(result); }; Mock <IHttpClient> client = new Mock <IHttpClient>(); HttpRequestMessage requestMessage = new HttpRequestMessage(); client.Setup(x => x.DefaultRequestHeaders).Returns(requestMessage.Headers); client.Setup(c => c.GetAsync(new Uri(deployUrl, UriKind.RelativeOrAbsolute), It.IsAny <CancellationToken>())).Returns(() => { string statusJson = JsonConvert.SerializeObject(new { status = Enum.GetName(typeof(DeployStatus), expectedDeployStatus) }, Formatting.Indented); HttpContent httpContent = new StringContent(statusJson, Encoding.UTF8, "application/json"); HttpResponseMessage responseMessage = new HttpResponseMessage(responseStatusCode) { Content = httpContent }; return(Task.FromResult(responseMessage)); }); ZipDeploymentStatus deploymentStatus = new ZipDeploymentStatus(client.Object, $"{UserAgentName}/{UserAgentVersion}", null, false); // Act var actualdeployStatus = await deploymentStatus.PollDeploymentStatusAsync(deployUrl, userName, password); // Assert verifyStep(client, expectedDeployStatus == actualdeployStatus); }
/// <summary> /// 配置状態を更新します。 /// </summary> private void RefreshDeployStatus() { if (BuildedFile == null || !BuildedFile.Exists) { Status = DeployStatus.NoBuild; } else if (DeployedFile == null || !DeployedFile.Exists) { Status = DeployStatus.NoDeploy; } else { if (BuildedTimestamp.Value > DeployedTimestamp.Value) { Status = DeployStatus.Updated; } else if (BuildedTimestamp.Value < DeployedTimestamp.Value) { Status = DeployStatus.Degrade; } else { Status = DeployStatus.Deployed; } } }
internal async System.Threading.Tasks.Task <bool> ZipDeployAsync(string zipToPublishPath, string userName, string password, string?publishUrl, string siteName, string userAgentVersion, IHttpClient client, bool logMessages) { if (!File.Exists(zipToPublishPath) || client == null) { return(false); } string zipDeployPublishUrl; if (!string.IsNullOrEmpty(publishUrl)) { if (!publishUrl !.EndsWith("/")) { publishUrl += "/"; } zipDeployPublishUrl = publishUrl + "api/zipdeploy"; } else if (!string.IsNullOrEmpty(siteName)) { zipDeployPublishUrl = $"https://{siteName}.scm.azurewebsites.net/api/zipdeploy"; } else { if (logMessages) { Log.LogError(StringMessages.NeitherSiteNameNorPublishUrlGivenError); } return(false); } if (logMessages) { Log.LogMessage(MessageImportance.High, String.Format(StringMessages.PublishingZipViaZipDeploy, zipToPublishPath, zipDeployPublishUrl)); } // use the async version of the api Uri uri = new Uri($"{zipDeployPublishUrl}?isAsync=true", UriKind.Absolute); string userAgent = $"{UserAgentName}/{userAgentVersion}"; FileStream stream = File.OpenRead(zipToPublishPath); IHttpResponse response = await client.PostWithBasicAuthAsync(uri, userName, password, "application/zip", userAgent, Encoding.UTF8, stream); if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Accepted) { if (logMessages) { Log.LogError(String.Format(StringMessages.ZipDeployFailureErrorMessage, zipDeployPublishUrl, response.StatusCode)); } return(false); } else { if (logMessages) { Log.LogMessage(StringMessages.ZipFileUploaded); } string deploymentUrl = response.GetHeader("Location").FirstOrDefault(); if (!string.IsNullOrEmpty(deploymentUrl)) { ZipDeploymentStatus deploymentStatus = new ZipDeploymentStatus(client, userAgent, Log, logMessages); DeployStatus status = await deploymentStatus.PollDeploymentStatusAsync(deploymentUrl, userName, password); if (status == DeployStatus.Success) { Log.LogMessage(MessageImportance.High, StringMessages.ZipDeploymentSucceeded); return(true); } else if (status == DeployStatus.Failed || status == DeployStatus.Unknown) { Log.LogError(String.Format(StringMessages.ZipDeployFailureErrorMessage, zipDeployPublishUrl, status)); return(false); } } } return(true); }
//Common code internal static void PushAndDeployApps(string repoCloneUrl, string defaultBranchName, string verificationText, HttpStatusCode expectedResponseCode, string verificationLogText, DeployStatus expectedStatus = DeployStatus.Success, string resourcePath = "", string httpMethod = "GET", string jsonPayload = "", bool deleteSCM = false) { using (new LatencyLogger("PushAndDeployApps - " + repoCloneUrl)) { Uri uri; if (!Uri.TryCreate(repoCloneUrl, UriKind.Absolute, out uri)) { uri = null; } string randomTestName = uri != null ? Path.GetFileNameWithoutExtension(repoCloneUrl) : repoCloneUrl; ApplicationManager.Run(randomTestName, appManager => { // Act using (TestRepository testRepository = Git.Clone(randomTestName, uri != null ? repoCloneUrl : null)) { using (new LatencyLogger("GitDeploy")) { appManager.GitDeploy(testRepository.PhysicalPath, defaultBranchName); } } var results = appManager.DeploymentManager.GetResultsAsync().Result.ToList(); // Assert Assert.Equal(1, results.Count); Assert.Equal(expectedStatus, results[0].Status); var url = new Uri(new Uri(appManager.SiteUrl), resourcePath); if (!String.IsNullOrEmpty(verificationText)) { KuduAssert.VerifyUrl(url.ToString(), verificationText, expectedResponseCode, httpMethod, jsonPayload); } if (!String.IsNullOrEmpty(verificationLogText)) { KuduAssert.VerifyLogOutput(appManager, results[0].Id, verificationLogText.Trim()); } if (deleteSCM) { using (new LatencyLogger("SCMAndWebDelete")) { appManager.RepositoryManager.Delete(deleteWebRoot: false, ignoreErrors: false).Wait(); } } }); } }
private void VerifyDeploymentConfiguration(string siteName, string targetProject, string expectedText, DeployStatus expectedStatus = DeployStatus.Success, string expectedLog = null) { string name = KuduUtils.GetRandomWebsiteName(siteName); string cloneUrl = "https://github.com/KuduApps/SpecificDeploymentConfiguration.git"; using (var repo = Git.Clone(name, cloneUrl)) { ApplicationManager.Run(name, appManager => { string deploymentFile = Path.Combine(repo.PhysicalPath, @".deployment"); File.WriteAllText(deploymentFile, String.Format(@"[config] project = {0}", targetProject)); Git.Commit(name, "Updated configuration"); // Act appManager.GitDeploy(repo.PhysicalPath); var results = appManager.DeploymentManager.GetResultsAsync().Result.ToList(); // Assert Assert.Equal(1, results.Count); Assert.Equal(expectedStatus, results[0].Status); KuduAssert.VerifyUrl(appManager.SiteUrl, expectedText); if (!String.IsNullOrEmpty(expectedLog)) { KuduAssert.VerifyLogOutput(appManager, results[0].Id, expectedLog); } }); } }
private void VerifyDeploymentConfiguration(string siteName, string targetProject, string expectedText, DeployStatus expectedStatus = DeployStatus.Success, string expectedLog = null) { string name = siteName; string cloneUrl = "https://github.com/KuduApps/SpecificDeploymentConfiguration.git"; using (var repo = Git.Clone(name, cloneUrl)) { ApplicationManager.Run(name, appManager => { string deploymentFile = Path.Combine(repo.PhysicalPath, @".deployment"); File.WriteAllText(deploymentFile, String.Format(@"[config] project = {0} ", targetProject)); Git.Commit(repo.PhysicalPath, "Updated configuration " + Guid.NewGuid()); // Act appManager.GitDeploy(repo.PhysicalPath); var results = appManager.DeploymentManager.GetResultsAsync().Result.ToList(); // Assert Assert.Equal(1, results.Count); Assert.Equal(expectedStatus, results[0].Status); KuduAssert.VerifyUrl(appManager.SiteUrl, expectedText); if (!String.IsNullOrEmpty(expectedLog)) { KuduAssert.VerifyLogOutput(appManager, results[0].Id, expectedLog); } }); } }
private static bool TryParseDeploymentStatus(IDictionary <string, object> json, out DeployStatus status) { status = DeployStatus.Unknown; if (json.TryGetValue("status", out object statusObj) && int.TryParse(statusObj.ToString(), out int statusInt) && Enum.TryParse(statusInt.ToString(), out status)) { return(true); } return(false); }