You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

AzureService.cs 7.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // AzureService.cs
  3. using System.Text;
  4. using Azure;
  5. using Azure.Core;
  6. using Azure.ResourceManager;
  7. using Azure.ResourceManager.ContainerInstance;
  8. using Azure.ResourceManager.ContainerInstance.Models;
  9. using Azure.ResourceManager.Resources;
  10. using Azure.Storage.Files.Shares;
  11. using DevTeam.Options;
  12. using Microsoft.Extensions.Options;
  13. namespace DevTeam.Backend.Services;
  14. public class AzureService : IManageAzure
  15. {
  16. private readonly AzureOptions _azSettings;
  17. private readonly ILogger<AzureService> _logger;
  18. private readonly ArmClient _client;
  19. public AzureService(IOptions<AzureOptions> azOptions, ILogger<AzureService> logger, ArmClient client)
  20. {
  21. ArgumentNullException.ThrowIfNull(azOptions);
  22. ArgumentNullException.ThrowIfNull(logger);
  23. ArgumentNullException.ThrowIfNull(client);
  24. _azSettings = azOptions.Value;
  25. _logger = logger;
  26. _client = client;
  27. }
  28. public async Task DeleteSandbox(string sandboxId)
  29. {
  30. try
  31. {
  32. var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
  33. var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
  34. var collection = resourceGroupResource.GetContainerGroups();
  35. var containerGroup = await collection.GetAsync(sandboxId);
  36. await containerGroup.Value.DeleteAsync(WaitUntil.Started);
  37. }
  38. catch (Exception ex)
  39. {
  40. _logger.LogError(ex, "Error deleting sandbox");
  41. throw;
  42. }
  43. }
  44. public async Task<bool> IsSandboxCompleted(string sandboxId)
  45. {
  46. try
  47. {
  48. var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
  49. var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
  50. var collection = resourceGroupResource.GetContainerGroups();
  51. var containerGroup = await collection.GetAsync(sandboxId);
  52. return containerGroup.Value.Data.ProvisioningState == "Succeeded"
  53. && containerGroup.Value.Data.Containers.First().InstanceView.CurrentState.State == "Terminated";
  54. }
  55. catch (Exception ex)
  56. {
  57. _logger.LogError(ex, "Error checking sandbox status");
  58. throw;
  59. }
  60. }
  61. public async Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber)
  62. {
  63. try
  64. {
  65. var runId = $"sk-sandbox-{org}-{repo}-{parentIssueNumber}-{issueNumber}".ToUpperInvariant();
  66. var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
  67. var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
  68. var scriptPath = $"/azfiles/output/{org}-{repo}/{parentIssueNumber}/{issueNumber}/run.sh";
  69. var collection = resourceGroupResource.GetContainerGroups();
  70. var data = new ContainerGroupData(new AzureLocation(_azSettings.Location), new ContainerInstanceContainer[]
  71. {
  72. new ContainerInstanceContainer(runId, _azSettings.SandboxImage,new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5,1)))
  73. {
  74. Command = { "/bin/bash", $"{scriptPath}" },
  75. VolumeMounts =
  76. {
  77. new ContainerVolumeMount("azfiles","/azfiles/")
  78. {
  79. IsReadOnly = false,
  80. }
  81. },
  82. }}, ContainerInstanceOperatingSystemType.Linux)
  83. {
  84. Volumes =
  85. {
  86. new ContainerVolume("azfiles")
  87. {
  88. AzureFile = new ContainerInstanceAzureFileVolume(_azSettings.FilesShareName,_azSettings.FilesAccountName)
  89. {
  90. StorageAccountKey = _azSettings.FilesAccountKey
  91. },
  92. },
  93. },
  94. RestartPolicy = ContainerGroupRestartPolicy.Never,
  95. Sku = ContainerGroupSku.Standard,
  96. Priority = ContainerGroupPriority.Regular
  97. };
  98. await collection.CreateOrUpdateAsync(WaitUntil.Completed, runId, data);
  99. }
  100. catch (Exception ex)
  101. {
  102. _logger.LogError(ex, "Error running sandbox");
  103. throw;
  104. }
  105. }
  106. public async Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output)
  107. {
  108. ArgumentNullException.ThrowIfNull(output);
  109. try
  110. {
  111. var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
  112. var parentDirName = $"{dir}/{org}-{repo}";
  113. var fileName = $"{filename}.{extension}";
  114. var share = new ShareClient(connectionString, _azSettings.FilesShareName);
  115. await share.CreateIfNotExistsAsync();
  116. await share.GetDirectoryClient($"{dir}").CreateIfNotExistsAsync(); ;
  117. var parentDir = share.GetDirectoryClient(parentDirName);
  118. await parentDir.CreateIfNotExistsAsync();
  119. var parentIssueDir = parentDir.GetSubdirectoryClient($"{parentIssueNumber}");
  120. await parentIssueDir.CreateIfNotExistsAsync();
  121. var directory = parentIssueDir.GetSubdirectoryClient($"{issueNumber}");
  122. await directory.CreateIfNotExistsAsync();
  123. var file = directory.GetFileClient(fileName);
  124. // hack to enable script to save files in the same directory
  125. var cwdHack = "#!/bin/bash\n cd $(dirname $0)";
  126. var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack, StringComparison.Ordinal) : output;
  127. using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
  128. {
  129. await file.CreateAsync(stream.Length);
  130. await file.UploadRangeAsync(
  131. new HttpRange(0, stream.Length),
  132. stream);
  133. }
  134. }
  135. catch (Exception ex)
  136. {
  137. _logger.LogError(ex, "Error storing output");
  138. throw;
  139. }
  140. }
  141. }
  142. public interface IManageAzure
  143. {
  144. Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output);
  145. Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber);
  146. Task<bool> IsSandboxCompleted(string sandboxId);
  147. Task DeleteSandbox(string sandboxId);
  148. }