protected override async Task ExecuteAsync(WorkerRequest @event, ProcessNotificationRequest notificationRequest) { var jobAssignmentId = notificationRequest.JobAssignmentId; var notification = notificationRequest.Notification; var notificationJobPayload = notification.Content.ToMcmaObject <JobBase>(); var table = new DynamoDbTable <JobAssignment>(@event.TableName()); var jobAssignment = await table.GetAsync(jobAssignmentId); jobAssignment.Status = notificationJobPayload.Status; jobAssignment.StatusMessage = notificationJobPayload.StatusMessage; if (notificationJobPayload.Progress != null) { jobAssignment.Progress = notificationJobPayload.Progress; } jobAssignment.JobOutput = notificationJobPayload.JobOutput; jobAssignment.DateModified = DateTime.UtcNow; await table.PutAsync(jobAssignmentId, jobAssignment); var resourceManager = @event.GetAwsV4ResourceManager(); await resourceManager.SendNotificationAsync(jobAssignment, jobAssignment.NotificationEndpoint); }
internal static async Task ProcessNotificationAsync(WorkerRequest @event, ProcessNotificationRequest notificationRequest) { var jobId = notificationRequest.JobId; var notification = notificationRequest.Notification; var notificationJob = notification.Content.ToMcmaObject <JobBase>(); var table = new DynamoDbTable <Job>(@event.TableName()); var job = await table.GetAsync(jobId); // not updating job if it already was marked as completed or failed. if (job.Status == JobStatus.Completed || job.Status == JobStatus.Failed) { Logger.Warn("Ignoring update of job that tried to change state from " + job.Status + " to " + notificationJob.Status); return; } job.Status = notificationJob.Status; job.StatusMessage = notificationJob.StatusMessage; job.Progress = notificationJob.Progress; job.JobOutput = notificationJob.JobOutput; job.DateModified = DateTime.UtcNow; await table.PutAsync(jobId, job); var resourceManager = @event.GetAwsV4ResourceManager(); await resourceManager.SendNotificationAsync(job, job.NotificationEndpoint); }
internal static async Task CreateJobProcessAsync(WorkerRequest @event, CreateJobProcessRequest createRequest) { var jobId = createRequest.JobId; var table = new DynamoDbTable <Job>(@event.TableName()); var job = await table.GetAsync(jobId); var resourceManager = @event.GetAwsV4ResourceManager(); try { var jobProcess = new JobProcess { Job = jobId, NotificationEndpoint = new NotificationEndpoint { HttpEndpoint = jobId + "/notifications" } }; jobProcess = await resourceManager.CreateAsync(jobProcess); job.Status = "QUEUED"; job.JobProcess = jobProcess.Id; } catch (Exception error) { Logger.Error("Failed to create JobProcess."); Logger.Exception(error); job.Status = JobStatus.Failed; job.StatusMessage = $"Failed to create JobProcess due to error '{error}'"; } job.DateModified = DateTime.UtcNow; await table.PutAsync(jobId, job); await resourceManager.SendNotificationAsync(job, job.NotificationEndpoint); }
protected override async Task ExecuteAsync(WorkerRequest request, ProcessRekognitionResult requestInput) { var workerJobHelper = new WorkerJobHelper <AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); var jobInput = workerJobHelper.JobInput; S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var s3Bucket = outputLocation.AwsS3Bucket; var rekoJobId = requestInput.JobInfo.RekoJobId; var rekoJobType = requestInput.JobInfo.RekoJobType; var status = requestInput.JobInfo.Status; if (status != "SUCCEEDED") { throw new Exception($"AI Rekognition failed job info: rekognition status:" + status); } object data = null; switch (rekoJobType) { case "StartCelebrityRecognition": using (var rekognitionClient = new AmazonRekognitionClient()) data = await rekognitionClient.GetCelebrityRecognitionAsync(new GetCelebrityRecognitionRequest { JobId = rekoJobId, /* required */ MaxResults = 1000000, SortBy = "TIMESTAMP" }); break; case "StartLabelDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartContentModeration": throw new NotImplementedException("StartContentModeration has not yet been implemented"); case "StartPersonTracking": throw new NotImplementedException("StartPersonTracking has not yet been implemented"); case "StartFaceDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartFaceSearch": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); default: throw new Exception($"Unknown job type '{rekoJobType}'"); } if (data == null) { throw new Exception($"No data was returned by AWS Rekognition"); } var newS3Key = $"reko_{Guid.NewGuid()}.json"; var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = newS3Key, ContentBody = data.ToMcmaJson().ToString(), ContentType = "application/json" }; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); } catch (Exception error) { Logger.Error("Unable to write output file to bucket '" + s3Bucket + "' with key '" + newS3Key + "'"); Logger.Exception(error); } workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = newS3Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
protected override async Task ExecuteAsync(WorkerRequest request, ProcessNotificationRequest requestInput) { var azureVideoId = requestInput.Notification?.Id; var azureState = requestInput.Notification?.State; if (azureVideoId == null || azureState == null) { Logger.Warn("POST is not coming from Azure Video Indexer. Expected notification to have id and state properties."); return; } var workerJobHelper = new WorkerJobHelper<AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); var authTokenUrl = request.ApiUrl() + "/auth/" + request.Location() + "/Accounts/" + request.AccountID() + "/AccessToken?allowEdit=true"; var customHeaders = new Dictionary<string, string> { ["Ocp-Apim-Subscription-Key"] = request.SubscriptionKey() }; Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}"); var mcmaHttp = new McmaHttpClient(); var response = await mcmaHttp.GetAsync(authTokenUrl, headers: customHeaders).WithErrorHandling(); var apiToken = await response.Content.ReadAsJsonAsync(); Logger.Debug($"Azure API Token: {apiToken}"); var metadataFromAzureVideoIndexer = $"{request.ApiUrl()}/{request.Location()}/Accounts/{request.AccountID()}/Videos/{azureVideoId}/Index?accessToken={apiToken}&language=English"; Logger.Debug($"Getting Azure video metadata from: {metadataFromAzureVideoIndexer}"); var indexedVideoMetadataResponse = await mcmaHttp.GetAsync(metadataFromAzureVideoIndexer, headers: customHeaders).WithErrorHandling(); var videoMetadata = await indexedVideoMetadataResponse.Content.ReadAsJsonAsync(); Logger.Debug($"Azure AI video metadata: {videoMetadata}"); S3Locator outputLocation; if (!workerJobHelper.JobInput.TryGet<S3Locator>(nameof(outputLocation), out outputLocation)) throw new Exception("Invalid or missing output location."); var jobOutputBucket = outputLocation.AwsS3Bucket; var jobOutputKeyPrefix = outputLocation.AwsS3KeyPrefix != null ? outputLocation.AwsS3KeyPrefix : ""; // get the info about the destination bucket to store the result of the job var s3Params = new PutObjectRequest { BucketName = jobOutputBucket, Key = jobOutputKeyPrefix + azureVideoId + "-" + Guid.NewGuid() + ".json", ContentBody = videoMetadata.ToString(), ContentType = "application/json" }; var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); //updating JobAssignment with jobOutput workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
public static async Task CreateJobAssignmentAsync(WorkerRequest request, CreateJobAssignmentRequest createRequest) { var resourceManager = request.GetAwsV4ResourceManager(); var table = new DynamoDbTable <JobProcess>(request.TableName()); var jobProcessId = createRequest.JobProcessId; var jobProcess = await table.GetAsync(jobProcessId); try { // retrieving the job var job = await resourceManager.ResolveAsync <Job>(jobProcess.Job); // retrieving the jobProfile var jobProfile = await resourceManager.ResolveAsync <JobProfile>(job.JobProfile); // validating job.JobInput with required input parameters of jobProfile var jobInput = job.JobInput; //await resourceManager.ResolveAsync<JobParameterBag>(job.JobInput); if (jobInput == null) { throw new Exception("Job is missing jobInput"); } if (jobProfile.InputParameters != null) { foreach (var parameter in jobProfile.InputParameters) { if (!jobInput.HasProperty(parameter.ParameterName)) { throw new Exception("jobInput is missing required input parameter '" + parameter.ParameterName + "'"); } } } // finding a service that is capable of handling the job type and job profile var services = await resourceManager.GetAsync <Service>(); Service selectedService = null; ResourceEndpointClient jobAssignmentResourceEndpoint = null; foreach (var service in services) { var serviceClient = new ServiceClient(service, AwsEnvironment.GetDefaultAwsV4AuthProvider()); jobAssignmentResourceEndpoint = null; if (service.JobType == job.Type) { jobAssignmentResourceEndpoint = serviceClient.GetResourceEndpoint <JobAssignment>(); if (jobAssignmentResourceEndpoint == null) { continue; } if (service.JobProfiles != null) { foreach (var serviceJobProfile in service.JobProfiles) { if (serviceJobProfile == job.JobProfile) { selectedService = service; break; } } } } if (selectedService != null) { break; } } if (jobAssignmentResourceEndpoint == null) { throw new Exception("Failed to find service that could execute the " + job.GetType().Name); } var jobAssignment = new JobAssignment { Job = jobProcess.Job, NotificationEndpoint = new NotificationEndpoint { HttpEndpoint = jobProcessId + "/notifications" } }; jobAssignment = await jobAssignmentResourceEndpoint.PostAsync <JobAssignment>(jobAssignment); jobProcess.Status = "SCHEDULED"; jobProcess.JobAssignment = jobAssignment.Id; } catch (Exception error) { Logger.Error("Failed to create job assignment"); Logger.Exception(error); jobProcess.Status = JobStatus.Failed; jobProcess.StatusMessage = error.ToString(); } jobProcess.DateModified = DateTime.UtcNow; await table.PutAsync(jobProcessId, jobProcess); await resourceManager.SendNotificationAsync(jobProcess, jobProcess.NotificationEndpoint); }
protected override async Task ExecuteAsync(WorkerRequest request, ProcessTranscribeJobResult requestInput) { var workerJobHelper = new WorkerJobHelper <AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); S3Locator outputLocation; if (!workerJobHelper.JobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var copySource = Uri.EscapeDataString(requestInput.OutputFile.AwsS3Bucket + "/" + requestInput.OutputFile.AwsS3Key); var s3Bucket = outputLocation.AwsS3Bucket; var s3Key = (!string.IsNullOrWhiteSpace(outputLocation.AwsS3KeyPrefix) ? outputLocation.AwsS3KeyPrefix : string.Empty) + requestInput.OutputFile.AwsS3Key; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.CopyObjectAsync(new CopyObjectRequest { SourceBucket = requestInput.OutputFile.AwsS3Bucket, SourceKey = requestInput.OutputFile.AwsS3Key, DestinationBucket = s3Bucket, DestinationKey = s3Key }); } catch (Exception error) { throw new Exception("Unable to copy output file to bucket '" + s3Bucket + "' with key'" + s3Key + "'", error); } workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = s3Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } // Cleanup: Deleting original output file try { var sourceS3 = await requestInput.OutputFile.GetClientAsync(); await sourceS3.DeleteObjectAsync(new DeleteObjectRequest { BucketName = requestInput.OutputFile.AwsS3Bucket, Key = requestInput.OutputFile.AwsS3Key, }); } catch (Exception error) { Logger.Error("Failed to cleanup transcribe output file."); Logger.Exception(error); } }