PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Kudu.Services/GitServer/FetchHandler.cs

https://github.com/moacap/kudu
C# | 191 lines | 153 code | 26 blank | 12 comment | 9 complexity | 5cb9fb7f09d6caca1b644ecc2849fa11 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Linq;
  3. using System.Web;
  4. using Kudu.Contracts.Infrastructure;
  5. using Kudu.Contracts.Settings;
  6. using Kudu.Contracts.SourceControl;
  7. using Kudu.Contracts.Tracing;
  8. using Kudu.Core.Deployment;
  9. using Kudu.Core.SourceControl.Git;
  10. using Newtonsoft.Json.Linq;
  11. namespace Kudu.Services.GitServer
  12. {
  13. public class FetchHandler : IHttpHandler
  14. {
  15. private readonly IGitServer _gitServer;
  16. private readonly IDeploymentManager _deploymentManager;
  17. private readonly IDeploymentSettingsManager _settings;
  18. private readonly ITracer _tracer;
  19. private readonly IOperationLock _deploymentLock;
  20. private readonly RepositoryConfiguration _configuration;
  21. public FetchHandler(ITracer tracer,
  22. IGitServer gitServer,
  23. IDeploymentManager deploymentManager,
  24. IDeploymentSettingsManager settings,
  25. IOperationLock deploymentLock,
  26. RepositoryConfiguration configuration)
  27. {
  28. _gitServer = gitServer;
  29. _deploymentManager = deploymentManager;
  30. _settings = settings;
  31. _tracer = tracer;
  32. _deploymentLock = deploymentLock;
  33. _configuration = configuration;
  34. }
  35. public bool IsReusable
  36. {
  37. get
  38. {
  39. return false;
  40. }
  41. }
  42. public void ProcessRequest(HttpContext context)
  43. {
  44. using (_tracer.Step("FetchHandler"))
  45. {
  46. string json = context.Request.Form["payload"];
  47. context.Response.TrySkipIisCustomErrors = true;
  48. if (String.IsNullOrEmpty(json))
  49. {
  50. _tracer.TraceWarning("Received empty json payload");
  51. context.Response.StatusCode = 400;
  52. context.ApplicationInstance.CompleteRequest();
  53. return;
  54. }
  55. RepositoryInfo repositoryInfo = null;
  56. try
  57. {
  58. repositoryInfo = GetRepositoryInfo(context.Request, json);
  59. }
  60. catch (FormatException ex)
  61. {
  62. _tracer.TraceError(ex);
  63. context.Response.StatusCode = 400;
  64. context.Response.Write(ex.Message);
  65. context.ApplicationInstance.CompleteRequest();
  66. return;
  67. }
  68. string targetBranch = _settings.GetValue("branch") ?? "master";
  69. _tracer.Trace("Attempting to fetch target branch {0}", targetBranch);
  70. if (!targetBranch.Equals(repositoryInfo.Branch, StringComparison.OrdinalIgnoreCase))
  71. {
  72. _tracer.Trace("Expected to fetch {0} but got {1}.", targetBranch, repositoryInfo.Branch);
  73. context.Response.StatusCode = 200;
  74. context.Response.Write(Resources.NothingToUpdate);
  75. context.ApplicationInstance.CompleteRequest();
  76. return;
  77. }
  78. _deploymentLock.LockOperation(() =>
  79. {
  80. _gitServer.Initialize(_configuration);
  81. _gitServer.SetReceiveInfo(repositoryInfo.OldRef, repositoryInfo.NewRef, repositoryInfo.Branch);
  82. _gitServer.FetchWithoutConflict(repositoryInfo.RepositoryUrl, "external", repositoryInfo.Branch);
  83. _deploymentManager.Deploy(repositoryInfo.Deployer);
  84. },
  85. () =>
  86. {
  87. context.Response.StatusCode = 409;
  88. context.ApplicationInstance.CompleteRequest();
  89. });
  90. }
  91. }
  92. private RepositoryInfo GetRepositoryInfo(HttpRequest request, string json)
  93. {
  94. JObject payload = null;
  95. try
  96. {
  97. payload = JObject.Parse(json);
  98. }
  99. catch (Exception ex)
  100. {
  101. throw new FormatException(Resources.Error_UnsupportedFormat, ex);
  102. }
  103. var info = new RepositoryInfo();
  104. // If it has a repository, then try to get information from that
  105. var repository = payload.Value<JObject>("repository");
  106. if (repository != null)
  107. {
  108. // Try to assume the github format
  109. // { repository: { url: "" }, ref: "", before: "", after: "" }
  110. info.RepositoryUrl = repository.Value<string>("url");
  111. // The format of ref is refs/something/something else
  112. // For master it's normally refs/head/master
  113. string @ref = payload.Value<string>("ref");
  114. if (String.IsNullOrEmpty(@ref))
  115. {
  116. throw new FormatException(Resources.Error_UnsupportedFormat);
  117. }
  118. // Just get the last token
  119. info.Branch = @ref.Split('/').Last();
  120. info.Deployer = GetDeployer(request);
  121. info.OldRef = payload.Value<string>("before");
  122. info.NewRef = payload.Value<string>("after");
  123. }
  124. else
  125. {
  126. // Look for the generic format
  127. // { url: "", branch: "", deployer: "", oldRef: "", newRef: "" }
  128. info.RepositoryUrl = payload.Value<string>("url");
  129. info.Branch = payload.Value<string>("branch");
  130. info.Deployer = payload.Value<string>("deployer");
  131. info.OldRef = payload.Value<string>("oldRef");
  132. info.NewRef = payload.Value<string>("newRef");
  133. }
  134. // If there's no specified branch assume master
  135. if (String.IsNullOrEmpty(info.Branch))
  136. {
  137. // REVIEW: Is this correct
  138. info.Branch = "master";
  139. }
  140. if (String.IsNullOrEmpty(info.RepositoryUrl))
  141. {
  142. throw new FormatException(Resources.Error_MissingRepositoryUrl);
  143. }
  144. return info;
  145. }
  146. private string GetDeployer(HttpRequest httpRequest)
  147. {
  148. // This is kind of hacky, we should have a consistent way of figuring out who's pushing to us
  149. if (httpRequest.Headers["X-Github-Event"] != null)
  150. {
  151. return "github";
  152. }
  153. // Look for a specific header here
  154. return null;
  155. }
  156. private class RepositoryInfo
  157. {
  158. public string RepositoryUrl { get; set; }
  159. public string OldRef { get; set; }
  160. public string NewRef { get; set; }
  161. public string Branch { get; set; }
  162. public string Deployer { get; set; }
  163. }
  164. }
  165. }