public async Task CreateTaskAsync_ReturnsTesCreateTaskResponse() { // Arrange var mockRepo = new Mock <IRepository <TesTask> >(); var tesTask = new TesTask() { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } } }; var controller = new TaskServiceApiController(mockRepo.Object, new NullLogger <TaskServiceApiController>()); // Act var result = await controller.CreateTaskAsync(tesTask) as ObjectResult; // Assert Assert.IsNotNull(result); mockRepo.Verify(x => x.CreateItemAsync(tesTask)); Assert.AreEqual(32, tesTask.Id.Length); Assert.AreEqual(TesState.QUEUEDEnum, tesTask.State); Assert.AreEqual(200, result.StatusCode); }
public virtual async Task <IActionResult> CreateTaskAsync([FromBody] TesTask tesTask) { if (!string.IsNullOrWhiteSpace(tesTask.Id)) { return(BadRequest("Id should not be included by the client in the request; the server is responsible for generating a unique Id.")); } if (string.IsNullOrWhiteSpace(tesTask.Executors?.FirstOrDefault()?.Image)) { return(BadRequest("Docker container image name is required.")); } // If the description starts with a GUID (Cromwell's job id), prefix the TES task id with first eight characters (of job id) to facilitate easier debugging var tesTaskIdPrefix = tesTask.Description?.Length >= 36 && Guid.TryParse(tesTask.Description.Substring(0, 36), out _) ? $"{tesTask.Description.Substring(0, 8)}_" : ""; tesTask.Id = $"{tesTaskIdPrefix}{Guid.NewGuid().ToString("N")}"; tesTask.State = TesState.QUEUEDEnum; tesTask.CreationTime = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); logger.LogDebug($"Creating task with id {tesTask.Id} state {tesTask.State}"); await repository.CreateItemAsync(tesTask); return(StatusCode(200, new TesCreateTaskResponse { Id = tesTask.Id })); }
public virtual async Task <IActionResult> GetTaskAsync([FromRoute][Required] string id, [FromQuery] string view) { TesTask tesTask = null; var itemFound = await repository.TryGetItemAsync(id, item => tesTask = item); return(itemFound ? TesJsonResult(tesTask, view) : NotFound($"The task with id {id} does not exist.")); }
public async Task CreateTaskAsync_ReturnsBadRequest_ForBackendParametersStrict_DuplicateKeys() { const string backend_parameter_key = "vmsize"; var backendParameters = new Dictionary <string, string> { { backend_parameter_key, Guid.NewGuid().ToString() }, { "VmSize", Guid.NewGuid().ToString() } }; var tesTask = new TesTask { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Resources = new TesResources { BackendParameters = backendParameters, BackendParametersStrict = true } }; var controller = GetTaskServiceApiController(); var result = await controller.CreateTaskAsync(tesTask) as BadRequestObjectResult; Assert.IsNotNull(result); Assert.AreEqual(400, result.StatusCode); }
public async Task CancelTaskAsync_ReturnsEmptyObject() { // Arrange var tesTask = new TesTask() { Id = "testTaskId", State = TesState.QUEUEDEnum }; var repositoryItem = new RepositoryItem <TesTask> { ETag = Guid.NewGuid().ToString(), Value = tesTask }; var mockRepo = new Mock <IRepository <TesTask> >(); mockRepo.Setup(repo => repo.GetItemAsync(tesTask.Id)).ReturnsAsync(repositoryItem); var controller = new TaskServiceApiController(mockRepo.Object, new NullLogger <TaskServiceApiController>()); // Act var result = await controller.CancelTask(tesTask.Id) as ObjectResult; // Assert Assert.IsNotNull(result); Assert.AreEqual(200, result.StatusCode); Assert.AreEqual(TesState.CANCELEDEnum, repositoryItem.Value.State); mockRepo.Verify(x => x.UpdateItemAsync(tesTask.Id, repositoryItem)); }
public async Task GetTaskAsync_ReturnsJsonResult() { var tesTask = new TesTask { State = TesState.RUNNINGEnum }; var mockRepo = new Mock <IRepository <TesTask> >(); mockRepo.Setup(repo => repo.TryGetItemAsync(tesTask.Id, It.IsAny <Action <TesTask> >())) .Callback <string, Action <TesTask> >((id, action) => { action(tesTask); }) .ReturnsAsync(true); var controller = GetTaskServiceApiController(mockRepo.Object); var result = await controller.GetTaskAsync(tesTask.Id, "MINIMAL") as JsonResult; Assert.IsNotNull(result); mockRepo.Verify(x => x.TryGetItemAsync(tesTask.Id, It.IsAny <Action <TesTask> >())); Assert.AreEqual(TesState.RUNNINGEnum, tesTask.State); Assert.AreEqual(200, result.StatusCode); }
public async Task ListTasks_ReturnsJsonResult() { var firstTesTask = new TesTask { Id = "tesTaskId1", State = TesState.COMPLETEEnum, Name = "tesTask", ETag = Guid.NewGuid().ToString() }; var secondTesTask = new TesTask { Id = "tesTaskId2", State = TesState.EXECUTORERROREnum, Name = "tesTask2", ETag = Guid.NewGuid().ToString() }; var thirdTesTask = new TesTask { Id = "tesTaskId3", State = TesState.EXECUTORERROREnum, Name = "someOtherTask2", ETag = Guid.NewGuid().ToString() }; var namePrefix = "tesTask"; var tesTasks = new[] { firstTesTask, secondTesTask, thirdTesTask }; var mockRepo = new Mock <IRepository <TesTask> >(); mockRepo.Setup(repo => repo .GetItemsAsync(It.IsAny <Expression <Func <TesTask, bool> > >(), It.IsAny <int>(), It.IsAny <string>())) .ReturnsAsync((Expression <Func <TesTask, bool> > predicate, int pageSize, string continuationToken) => (string.Empty, tesTasks.Where(i => predicate.Compile().Invoke(i)).Take(pageSize))); var controller = GetTaskServiceApiController(mockRepo.Object); var result = await controller.ListTasks(namePrefix, 1, null, "BASIC") as JsonResult; var listOfTesTasks = (TesListTasksResponse)result.Value; Assert.IsNotNull(result); Assert.AreEqual(1, listOfTesTasks.Tasks.Count); Assert.AreEqual(200, result.StatusCode); }
public async Task TES_Supports_BackendParameter_vmsize() { const string backend_parameter_key = "vm_size"; var backendParameters = new Dictionary <string, string> { { backend_parameter_key, "VmSize1" } }; var tesTask = new TesTask { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Resources = new TesResources { BackendParameters = backendParameters, BackendParametersStrict = true } }; var repository = new Mock <IRepository <TesTask> >(); var controller = GetTaskServiceApiController(repository.Object); var result = await controller.CreateTaskAsync(tesTask) as ObjectResult; Assert.IsNotNull(result); repository.Verify(x => x.CreateItemAsync(tesTask)); Assert.AreEqual(32, tesTask.Id.Length); Assert.AreEqual(TesState.QUEUEDEnum, tesTask.State); Assert.IsTrue(tesTask.Resources.BackendParameters.ContainsKey(backend_parameter_key)); Assert.AreEqual(200, result.StatusCode); }
public async Task CreateTaskAsync_CromwellWorkflowIdIsUsedAsTaskIdPrefix() { var cromwellWorkflowId = Guid.NewGuid().ToString(); var cromwellSubWorkflowId = Guid.NewGuid().ToString(); var taskDescription = $"{cromwellSubWorkflowId}:BackendJobDescriptorKey_CommandCallNode_wf_hello.hello:-1:1"; var tesTask = new TesTask() { Description = taskDescription, Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Inputs = new List <TesInput> { new TesInput { Name = "commandScript", Path = $"/cromwell-executions/test/{cromwellWorkflowId}/call-hello/test-subworkflow/{cromwellSubWorkflowId}/call-subworkflow/shard-8/execution/script" } } }; var controller = GetTaskServiceApiController(); await controller.CreateTaskAsync(tesTask); Assert.AreEqual(41, tesTask.Id.Length); // First eight characters of Cromwell's job id + underscore + GUID without dashes Assert.IsTrue(tesTask.Id.StartsWith(cromwellWorkflowId.Substring(0, 8) + "_")); }
/// <summary> /// Transitions the <see cref="TesTask"/> to the new state, based on the rules defined in the tesTaskStateTransitions list. /// </summary> /// <param name="tesTask">TES task</param> /// <param name="batchTaskState">Current Azure Batch task state</param> /// <returns>True if the TES task was changed.</returns> private async Task <bool> HandleTesTaskTransitionAsync(TesTask tesTask, BatchTaskState batchTaskState) { var tesTaskChanged = false; var mapItem = tesTaskStateTransitions .FirstOrDefault(m => (m.Condition == null || m.Condition(tesTask)) && (m.CurrentBatchTaskState == null || m.CurrentBatchTaskState == batchTaskState)); if (mapItem != null) { if (mapItem.Action != null) { await mapItem.Action(tesTask); tesTaskChanged = true; } if (mapItem.NewTesTaskState != null && mapItem.NewTesTaskState != tesTask.State) { tesTask.State = mapItem.NewTesTaskState.Value; tesTaskChanged = true; } } return(tesTaskChanged); }
/// <summary> /// Adds a new <see cref="TesTaskLog"/> to <see cref="TesTask"/> /// </summary> /// <param name="tesTask"><see cref="TesTask"/></param> /// <returns>Last <see cref="TesTaskLog"/></returns> public static TesTaskLog AddTesTaskLog(this TesTask tesTask) { tesTask.Logs ??= new List <TesTaskLog>(); tesTask.Logs.Add(new TesTaskLog()); return(tesTask.Logs.Last()); }
public virtual async Task <IActionResult> CancelTask([FromRoute][Required] string id) { TesTask tesTask = null; if (await repository.TryGetItemAsync(id, item => tesTask = item)) { if (tesTask.State == TesState.COMPLETEEnum || tesTask.State == TesState.EXECUTORERROREnum || tesTask.State == TesState.SYSTEMERROREnum) { logger.LogInformation($"Task {id} cannot be canceled because it is in {tesTask.State} state."); } else if (tesTask.State != TesState.CANCELEDEnum) { logger.LogInformation("Canceling task"); tesTask.IsCancelRequested = true; tesTask.State = TesState.CANCELEDEnum; await repository.UpdateItemAsync(tesTask); } } else { return(NotFound($"The task with id {id} does not exist.")); } return(StatusCode(200, new object())); }
public async Task CreateTaskAsync_ReturnsTesCreateTaskResponseWithBackendParameters_UnsupportedKey() { const string unsupportedKey = "unsupported_key_2021"; var backendParameters = new Dictionary <string, string> { { unsupportedKey, Guid.NewGuid().ToString() } }; var tesTask = new TesTask { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Resources = new TesResources { BackendParameters = backendParameters } }; var repository = new Mock <IRepository <TesTask> >(); var controller = GetTaskServiceApiController(repository.Object); var result = await controller.CreateTaskAsync(tesTask) as ObjectResult; Assert.IsNotNull(result); repository.Verify(x => x.CreateItemAsync(tesTask)); Assert.AreEqual(32, tesTask.Id.Length); Assert.AreEqual(TesState.QUEUEDEnum, tesTask.State); // Unsupported keys should not be persisted Assert.IsFalse(tesTask?.Resources?.BackendParameters?.ContainsKey(unsupportedKey)); Assert.AreEqual(200, result.StatusCode); }
public async Task CreateTaskAsync_ExtractsWorkflowId() { var cromwellWorkflowId = "daf1a044-d741-4db9-8eb5-d6fd0519b1f1"; var taskDescription = $"{cromwellWorkflowId}:BackendJobDescriptorKey_CommandCallNode_wf_hello.hello:-1:1"; var tesTask = new TesTask() { Description = taskDescription, Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Inputs = new List <TesInput> { new TesInput { Path = "/cromwell-executions/test/daf1a044-d741-4db9-8eb5-d6fd0519b1f1/call-hello/execution/script" } } }; var controller = this.GetTaskServiceApiController(); await controller.CreateTaskAsync(tesTask); Assert.AreEqual(cromwellWorkflowId, tesTask.WorkflowId); }
private async Task <TesTask> CreateCwlTesTaskAsync(string cwlFileContent, TesResources tesResourcesReceivedFromCromwell) { var tesTask = new TesTask() { Name = "test.cwl", Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Inputs = new List <TesInput> { new TesInput { Path = "/cromwell-executions/test.cwl/daf1a044-d741-4db9-8eb5-d6fd0519b1f1/call-hello/execution/script" } }, Resources = tesResourcesReceivedFromCromwell }; var azureProxy = new Mock <IAzureProxy>(); azureProxy.Setup(a => a.TryReadCwlFile(It.IsAny <string>(), out cwlFileContent)).Returns(true); var controller = this.GetTaskServiceApiController(azureProxy.Object); await controller.CreateTaskAsync(tesTask); return(tesTask); }
public async Task CancelTaskAsync_ReturnsEmptyObject() { var tesTask = new TesTask() { Id = "testTaskId", State = TesState.QUEUEDEnum }; var repositoryItem = new RepositoryItem <TesTask> { ETag = Guid.NewGuid().ToString(), Value = tesTask }; var mockRepo = new Mock <IRepository <TesTask> >(); mockRepo.Setup(repo => repo.TryGetItemAsync(tesTask.Id, It.IsAny <Action <RepositoryItem <TesTask> > >())) .Callback <string, Action <RepositoryItem <TesTask> > >((id, action) => { action(repositoryItem); }) .ReturnsAsync(true); var controller = this.GetTaskServiceApiController(mockRepo.Object); var result = await controller.CancelTask(tesTask.Id) as ObjectResult; Assert.IsNotNull(result); Assert.AreEqual(200, result.StatusCode); Assert.AreEqual(TesState.CANCELEDEnum, repositoryItem.Value.State); mockRepo.Verify(x => x.UpdateItemAsync(tesTask.Id, repositoryItem)); }
private async Task DeleteOldBatchJobs() { var jobsToDelete = await azureProxy.ListOldJobsToDeleteAsync(oldestJobAge); foreach (var jobId in jobsToDelete) { logger.LogInformation($"Job Id to delete: {jobId}"); var tesTaskId = jobId.Split(new[] { '-' })[0]; logger.LogInformation($"TES task Id to delete: {tesTaskId}"); TesTask tesTask = null; if (await repository.TryGetItemAsync(tesTaskId, item => tesTask = item)) { if (tesTask.State == TesState.COMPLETEEnum || tesTask.State == TesState.EXECUTORERROREnum || tesTask.State == TesState.SYSTEMERROREnum || tesTask.State == TesState.CANCELEDEnum || tesTask.State == TesState.UNKNOWNEnum) { await azureProxy.DeleteBatchJobAsync(tesTaskId); } } } }
/// <summary> /// Adds a new Azure Batch pool/job/task for the given <see cref="TesTask"/> /// </summary> /// <param name="tesTask">The <see cref="TesTask"/> to schedule on Azure Batch</param> /// <returns>A task to await</returns> private async Task AddBatchJobAsync(TesTask tesTask) { try { var jobId = await azureProxy.GetNextBatchJobIdAsync(tesTask.Id); var virtualMachineInfo = await GetVmSizeAsync(tesTask.Resources); await CheckBatchAccountQuotas((int)tesTask.Resources.CpuCores.GetValueOrDefault(DefaultCoreCount), virtualMachineInfo.LowPriority); // TODO?: Support for multiple executors. Cromwell has single executor per task. var dockerImage = tesTask.Executors.First().Image; var cloudTask = await ConvertTesTaskToBatchTaskAsync(tesTask); var poolInformation = await CreatePoolInformation(dockerImage, virtualMachineInfo.VmSize, virtualMachineInfo.LowPriority); tesTask.Resources.VmInfo = virtualMachineInfo; logger.LogInformation($"Creating batch job for TES task {tesTask.Id}. Using VM size {virtualMachineInfo}."); await azureProxy.CreateBatchJobAsync(jobId, cloudTask, poolInformation); tesTask.State = TesState.INITIALIZINGEnum; } catch (AzureBatchQuotaMaxedOutException exception) { logger.LogInformation($"Not enough quota available for task Id {tesTask.Id}. Reason: {exception.Message}. Task will remain in queue."); } catch (Exception exc) { tesTask.State = TesState.SYSTEMERROREnum; tesTask.WriteToSystemLog(exc.Message, exc.StackTrace); logger.LogError(exc, exc.Message); } }
public async Task CreateTaskAsync_ExtractsWorkflowId() { var cromwellWorkflowId = Guid.NewGuid().ToString(); var cromwellSubWorkflowId = Guid.NewGuid().ToString(); var taskDescription = $"{cromwellSubWorkflowId}:BackendJobDescriptorKey_CommandCallNode_wf_hello.hello:-1:1"; var tesTask = new TesTask() { Description = taskDescription, Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Inputs = new List <TesInput> { new TesInput { Name = "commandScript", Path = $"/cromwell-executions/test/{cromwellWorkflowId}/call-hello/test-subworkflow/{cromwellSubWorkflowId}/call-subworkflow/shard-8/execution/script" } } }; var controller = GetTaskServiceApiController(); await controller.CreateTaskAsync(tesTask); Assert.AreEqual(cromwellWorkflowId, tesTask.WorkflowId); }
public async Task CreateTaskAsync_ReturnsBadRequest_ForBackendParametersStrict_UnsupportedKey() { const string unsupportedKey = "unsupported_key_2021"; var backendParameters = new Dictionary <string, string> { { unsupportedKey, Guid.NewGuid().ToString() } }; var tesTask = new TesTask { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Resources = new TesResources { BackendParameters = backendParameters, BackendParametersStrict = true } }; var controller = GetTaskServiceApiController(); var result = await controller.CreateTaskAsync(tesTask) as BadRequestObjectResult; Assert.IsNotNull(result); // Unsupported keys should cause a bad request when BackendParametersStrict = true Assert.AreEqual(400, result.StatusCode); // Unsupported keys should be returned in the warning message Assert.IsTrue(result.Value.ToString().Contains(unsupportedKey)); }
public async Task TES_Supports_BackendParameter_workflow_execution_identity() { const string backend_parameter_key = "workflow_execution_identity"; var backendParameters = new Dictionary <string, string> { { backend_parameter_key, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/coa/providers/Microsoft.ManagedIdentity/userAssignedIdentities/coa-test-uami" } }; var tesTask = new TesTask { Executors = new List <TesExecutor> { new TesExecutor { Image = "ubuntu" } }, Resources = new TesResources { BackendParameters = backendParameters, BackendParametersStrict = true } }; var repository = new Mock <IRepository <TesTask> >(); var controller = GetTaskServiceApiController(repository.Object); var result = await controller.CreateTaskAsync(tesTask) as ObjectResult; Assert.IsNotNull(result); repository.Verify(x => x.CreateItemAsync(tesTask)); Assert.AreEqual(32, tesTask.Id.Length); Assert.AreEqual(TesState.QUEUEDEnum, tesTask.State); Assert.IsTrue(tesTask.Resources.BackendParameters.ContainsKey(backend_parameter_key)); Assert.AreEqual(200, result.StatusCode); }
public async Task GetTaskAsync_ReturnsJsonResult() { // Arrange var mockRepo = new Mock <IRepository <TesTask> >(); var tesTask = new TesTask { State = TesState.RUNNINGEnum }; var repositoryItem = new RepositoryItem <TesTask> { ETag = Guid.NewGuid().ToString(), Value = tesTask }; mockRepo.Setup(repo => repo.GetItemAsync(tesTask.Id)).ReturnsAsync(repositoryItem); var controller = new TaskServiceApiController(mockRepo.Object, new NullLogger <TaskServiceApiController>()); // Act var result = await controller.GetTaskAsync(tesTask.Id, "MINIMAL") as JsonResult; // Assert Assert.IsNotNull(result); mockRepo.Verify(x => x.GetItemAsync(tesTask.Id)); Assert.AreEqual(TesState.RUNNINGEnum, tesTask.State); Assert.AreEqual(200, result.StatusCode); }
/// <summary> /// Iteratively manages execution of a <see cref="TesTask"/> on Azure Batch until completion or failure /// </summary> /// <param name="tesTask">The <see cref="TesTask"/></param> /// <returns>True if the TES task needs to be persisted.</returns> public async Task <bool> ProcessTesTaskAsync(TesTask tesTask) { var batchTaskState = await GetBatchTaskStateAsync(tesTask.Id); var tesTaskChanged = await HandleTesTaskTransitionAsync(tesTask, batchTaskState); return(tesTaskChanged); }
/// <summary> /// Writes to <see cref="TesTask"/> system log. /// </summary> /// <param name="tesTask"><see cref="TesTask"/></param> /// <param name="logEntries">List of strings to write to the log.</param> public static void AddToSystemLog(this TesTask tesTask, IEnumerable <string> logEntries) { if (logEntries != null && logEntries.Any(e => !string.IsNullOrEmpty(e))) { var tesTaskLog = tesTask.GetOrAddTesTaskLog(); tesTaskLog.SystemLogs ??= new List <string>(); tesTaskLog.SystemLogs.AddRange(logEntries); } }
/// <summary> /// Writes to <see cref="TesTask"/> system log. /// </summary> /// <param name="tesTask"><see cref="TesTask"/></param> /// <param name="logEntries">List of strings to write to the log.</param> public static void WriteToSystemLog(this TesTask tesTask, params string[] logEntries) { if (logEntries != null && logEntries.Any(e => !string.IsNullOrEmpty(e))) { tesTask.Logs = tesTask.Logs ?? new List <TesTaskLog>(); tesTask.Logs.Add(new TesTaskLog { SystemLogs = logEntries.ToList() }); } }
private static async Task <string> ProcessTesTaskAndGetFirstLogMessageAsync(TesTask tesTask, AzureProxy.AzureBatchJobAndTaskState?azureBatchJobAndTaskState = null) { var azureProxyReturnValues = AzureProxyReturnValues.Defaults; azureProxyReturnValues.BatchJobAndTaskState = azureBatchJobAndTaskState ?? azureProxyReturnValues.BatchJobAndTaskState; await ProcessTesTaskAndGetBatchJobArgumentsAsync(tesTask, GetMockConfig(), GetMockAzureProxy(azureProxyReturnValues)); return(tesTask.Logs?.FirstOrDefault()?.SystemLogs?.FirstOrDefault()); }
public async Task CreateTaskAsync_ReturnsBadRequest_ForMissingDockerImage() { var tesTask = new TesTask(); var controller = GetTaskServiceApiController(); var result = await controller.CreateTaskAsync(tesTask) as ObjectResult; Assert.IsNotNull(result); Assert.AreEqual(400, result.StatusCode); }
/// <summary> /// Returns the last <see cref="TesTaskLog"/>. Adds it if none exist. /// </summary> /// <param name="tesTask"><see cref="TesTask"/></param> /// <returns>Last <see cref="TesTaskLog"/></returns> public static TesTaskLog GetOrAddTesTaskLog(this TesTask tesTask) { if (tesTask.Logs == null || !tesTask.Logs.Any()) { tesTask.Logs = new List <TesTaskLog> { new TesTaskLog() }; } return(tesTask.Logs.Last()); }
public virtual async Task <IActionResult> CreateTaskAsync([FromBody] TesTask tesTask) { if (!string.IsNullOrWhiteSpace(tesTask.Id)) { return(BadRequest("Id should not be included by the client in the request; the server is responsible for generating a unique Id.")); } if (string.IsNullOrWhiteSpace(tesTask.Executors?.FirstOrDefault()?.Image)) { return(BadRequest("Docker container image name is required.")); } tesTask.State = TesState.QUEUEDEnum; tesTask.CreationTime = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); // example: /cromwell-executions/test/daf1a044-d741-4db9-8eb5-d6fd0519b1f1/call-hello/execution/script tesTask.WorkflowId = tesTask ?.Inputs ?.FirstOrDefault(i => i?.Path?.StartsWith(rootExecutionPath, StringComparison.OrdinalIgnoreCase) == true) ?.Path ?.Split('/', StringSplitOptions.RemoveEmptyEntries) ?.Skip(2) ?.FirstOrDefault(); // Prefix the TES task id with first eight characters of root Cromwell job id to facilitate easier debugging var tesTaskIdPrefix = tesTask.WorkflowId != null && Guid.TryParse(tesTask.WorkflowId, out _) ? $"{tesTask.WorkflowId.Substring(0, 8)}_" : ""; tesTask.Id = $"{tesTaskIdPrefix}{Guid.NewGuid().ToString("N")}"; // For CWL workflows, if disk size is not specified in TES object (always), try to retrieve it from the corresponding workflow stored by Cromwell in /cromwell-tmp directory // Also allow for TES-style "memory" and "cpu" hints in CWL. if (tesTask.Name != null && tesTask.Inputs.Any(i => i.Path.Contains(".cwl/")) && tesTask.WorkflowId != null && azureProxy.TryReadCwlFile(tesTask.WorkflowId, out var cwlContent) && CwlDocument.TryCreate(cwlContent, out var cwlDocument)) { tesTask.Resources = tesTask.Resources ?? new TesResources(); tesTask.Resources.DiskGb = tesTask.Resources.DiskGb ?? cwlDocument.DiskGb; tesTask.Resources.CpuCores = tesTask.Resources.CpuCores ?? cwlDocument.Cpu; tesTask.Resources.RamGb = tesTask.Resources.RamGb ?? cwlDocument.MemoryGb; // Preemptible is not passed on from CWL workflows to Cromwell, so Cromwell sends the default (TRUE) to TES, // instead of NULL like the other values above. // If CWL document has it specified, override the value sent by Cromwell tesTask.Resources.Preemptible = cwlDocument.Preemptible ?? tesTask.Resources.Preemptible; } logger.LogDebug($"Creating task with id {tesTask.Id} state {tesTask.State}"); await repository.CreateItemAsync(tesTask); return(StatusCode(200, new TesCreateTaskResponse { Id = tesTask.Id })); }
public async Task TryGetItemAsync_ThrowsException_DoesNotSetCache() { SystemClock.SleepAsync = (_, __) => Task.FromResult(true); SystemClock.Sleep = (_, __) => { }; var repository = GetMockRepository(); var cachingRepository = new CachingWithRetriesRepository <TesTask>(repository.Object); TesTask tesTask = null; await Assert.ThrowsExceptionAsync <Exception>(async() => await cachingRepository.TryGetItemAsync("throws", item => tesTask = item)); repository.Verify(mock => mock.TryGetItemAsync("throws", It.IsAny <Action <TesTask> >()), Times.Exactly(4)); }