Esempio n. 1
0
        private Task <TileMetadata> CanGenerateDxfTiles(string status)
        {
            //Set up DataOcean stuff
            var expectedTopFolderResult = new DataOceanDirectory {
                Id = Guid.NewGuid(), Name = topLevelFolderName
            };
            var expectedDcFileResult = new DataOceanFile {
                Id = Guid.NewGuid(), Name = dcFileName, ParentId = expectedTopFolderResult.Id
            };
            var expectedDxfFileResult = new DataOceanFile {
                Id = Guid.NewGuid(), Name = dxfFileName, ParentId = expectedTopFolderResult.Id
            };

            var subFolderPath = new DataOceanFileUtil(dxfFullName).GeneratedTilesFolder;
            var parts         = subFolderPath.Split(DataOceanUtil.PathSeparator);
            var subFolderName = parts[parts.Length - 1];

            var dataOceanMock = new Mock <IDataOceanClient>();

            dataOceanMock.Setup(d => d.GetFileId(dcFullName, null)).ReturnsAsync(expectedDcFileResult.Id);
            dataOceanMock.Setup(d => d.GetFileId(dxfFullName, null)).ReturnsAsync(expectedDxfFileResult.Id);
            dataOceanMock.Setup(d => d.MakeFolder(subFolderPath, null)).ReturnsAsync(true);
            dataOceanMock.Setup(d => d.GetFolderId($"{DataOceanUtil.PathSeparator}{topLevelFolderName}", null)).ReturnsAsync(expectedTopFolderResult.Id);

            //Set up Pegasus stuff
            var units             = DxfUnitsType.UsSurveyFeet.ToString();
            var expectedExecution =
                NewDxfPegasusExecution(expectedDcFileResult, expectedDxfFileResult, subFolderName, units, status);
            var expectedExecutionResult = new PegasusExecutionResult {
                Execution = expectedExecution
            };
            var expectedExecutionAttemptResult = new PegasusExecutionAttemptResult
            {
                ExecutionAttempt = new PegasusExecutionAttempt {
                    Id = Guid.NewGuid(), Status = ExecutionStatus.EXECUTING
                }
            };

            var config             = serviceProvider.GetRequiredService <Common.Abstractions.Configuration.IConfigurationStore>();
            var pegasusBaseUrl     = config.GetValueString("PEGASUS_URL");
            var baseRoute          = "/api/executions";
            var createExecutionUrl = $"{pegasusBaseUrl}{baseRoute}";
            var startExecutionUrl  = $"{pegasusBaseUrl}{baseRoute}/{expectedExecution.Id}/start";
            var executionStatusUrl = $"{pegasusBaseUrl}{baseRoute}/{expectedExecution.Id}";

            var gracefulMock = new Mock <IWebRequest>();

            gracefulMock
            .Setup(g => g.ExecuteRequest <PegasusExecutionResult>(createExecutionUrl, It.IsAny <MemoryStream>(), null, HttpMethod.Post, null, 0,
                                                                  false)).ReturnsAsync(expectedExecutionResult);
            gracefulMock
            .Setup(g => g.ExecuteRequest <PegasusExecutionAttemptResult>(startExecutionUrl, null, null, HttpMethod.Post, null, 0,
                                                                         false)).ReturnsAsync(expectedExecutionAttemptResult);
            gracefulMock
            .Setup(g => g.ExecuteRequest <PegasusExecutionResult>(executionStatusUrl, null, null, HttpMethod.Get, null, 0,
                                                                  false)).ReturnsAsync(expectedExecutionResult);

            return(ProcessWithSuccess(gracefulMock, dataOceanMock, subFolderPath, true));
        }
Esempio n. 2
0
        public async Task CanGenerateGeoTiffTilesFailedToStartExecution()
        {
            //Set up DataOcean stuff
            var expectedTopFolderResult = new DataOceanDirectory {
                Id = Guid.NewGuid(), Name = topLevelFolderName
            };
            var expectedFileResult = new DataOceanFile {
                Id = Guid.NewGuid(), Name = geoTiffFileName, ParentId = expectedTopFolderResult.Id
            };

            var subFolderPath = new DataOceanFileUtil(geoTiffFullName).GeneratedTilesFolder;
            var parts         = subFolderPath.Split(DataOceanUtil.PathSeparator);
            var subFolderName = parts[parts.Length - 1];

            var dataOceanMock = new Mock <IDataOceanClient>();

            dataOceanMock.Setup(d => d.GetFileId(geoTiffFullName, null)).ReturnsAsync(expectedFileResult.Id);
            dataOceanMock.Setup(d => d.MakeFolder(subFolderPath, null)).ReturnsAsync(true);
            dataOceanMock.Setup(d => d.GetFolderId($"{DataOceanUtil.PathSeparator}{topLevelFolderName}", null)).ReturnsAsync(expectedTopFolderResult.Id);

            //Set up Pegasus stuff
            var expectedExecution =
                NewGeoTiffPegasusExecution(expectedFileResult, subFolderName, ExecutionStatus.NOT_READY);

            var expectedExecutionResult = new PegasusExecutionResult {
                Execution = expectedExecution
            };

            _ = new PegasusExecutionAttemptResult
            {
                ExecutionAttempt = new PegasusExecutionAttempt {
                    Id = Guid.NewGuid(), Status = ExecutionStatus.EXECUTING
                }
            };

            var config             = serviceProvider.GetRequiredService <Common.Abstractions.Configuration.IConfigurationStore>();
            var pegasusBaseUrl     = config.GetValueString("PEGASUS_URL");
            var baseRoute          = "/api/executions";
            var createExecutionUrl = $"{pegasusBaseUrl}{baseRoute}";
            var startExecutionUrl  = $"{pegasusBaseUrl}{baseRoute}/{expectedExecution.Id}/start";

            var gracefulMock = new Mock <IWebRequest>();

            gracefulMock
            .Setup(g => g.ExecuteRequest <PegasusExecutionResult>(createExecutionUrl, It.IsAny <MemoryStream>(), null, HttpMethod.Post, null, 0,
                                                                  false)).ReturnsAsync(expectedExecutionResult);
            gracefulMock
            .Setup(g => g.ExecuteRequest <PegasusExecutionAttemptResult>(startExecutionUrl, null, null, HttpMethod.Post, null, 0,
                                                                         false)).ReturnsAsync((PegasusExecutionAttemptResult)null);

            await ProcessWithFailure(gracefulMock, dataOceanMock,
                                     $"Failed to start execution for {geoTiffFullName}", false);
        }
Esempio n. 3
0
        /// <summary>
        /// Generates raster tiles using the Pegasus API and stores them in the data ocean.
        /// The source is either a DXF file or a GeoTIFF file.
        /// </summary>
        /// <param name="fileName">The path and file name of the source file</param>
        /// <param name="createExecutionMessage">The details of tile generation for Pegasus</param>
        /// <param name="customHeaders"></param>
        /// <returns>Metadata for the generated tiles including the zoom range</returns>
        private async Task <TileMetadata> GenerateTiles(string fileName, CreateExecutionMessage createExecutionMessage, IHeaderDictionary customHeaders, Action <IHeaderDictionary> setJobIdAction)
        {
            Log.LogDebug($"Pegasus execution: {JsonConvert.SerializeObject(createExecutionMessage)}");

            TileMetadata metadata = null;

            //Delete any old tiles. To avoid 2 traversals just try the delete anyway without checking for existence.
            await DeleteTiles(fileName, customHeaders);

            //In DataOcean this is actually a multifile not a folder
            string tileFolderFullName = new DataOceanFileUtil(fileName).GeneratedTilesFolder;
            //Get the parent folder id
            var parts          = tileFolderFullName.Split(DataOceanUtil.PathSeparator);
            var tileFolderName = parts[parts.Length - 1];
            var parentPath     = tileFolderFullName.Substring(0, tileFolderFullName.Length - tileFolderName.Length - 1);
            var parentId       = await dataOceanClient.GetFolderId(parentPath, customHeaders);

            //Set common parameters
            createExecutionMessage.Execution.Parameters.ParentId  = parentId;
            createExecutionMessage.Execution.Parameters.Name      = tileFolderName;
            createExecutionMessage.Execution.Parameters.TileOrder = TILE_ORDER;
            createExecutionMessage.Execution.Parameters.MultiFile = "true";
            createExecutionMessage.Execution.Parameters.Public    = "false";

            const string           baseRoute = "/api/executions";
            var                    payload   = JsonConvert.SerializeObject(createExecutionMessage);
            PegasusExecutionResult executionResult;

            using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(payload)))
            {
                executionResult = await gracefulClient.ExecuteRequest <PegasusExecutionResult>($"{pegasusBaseUrl}{baseRoute}", ms, customHeaders, HttpMethod.Post);
            }

            if (executionResult == null)
            {
                throw new ServiceException(HttpStatusCode.InternalServerError,
                                           new ContractExecutionResult(ContractExecutionStatesEnum.InternalProcessingError, $"Failed to create execution for {fileName}"));
            }

            setJobIdAction?.Invoke(new HeaderDictionary {
                { PEGASUS_LOG_JOBID_KEY, executionResult.Execution.Id.ToString() }
            });

            //2. Start the execution
            Log.LogDebug($"Starting execution for {fileName}");
            var executionRoute      = $"{baseRoute}/{executionResult.Execution.Id}";
            var startExecutionRoute = $"{executionRoute}/start";
            var startResult         = await gracefulClient.ExecuteRequest <PegasusExecutionAttemptResult>($"{pegasusBaseUrl}{startExecutionRoute}", null, customHeaders, HttpMethod.Post);

            if (startResult == null)
            {
                throw new ServiceException(HttpStatusCode.InternalServerError,
                                           new ContractExecutionResult(ContractExecutionStatesEnum.InternalProcessingError, $"Failed to start execution for {fileName}"));
            }

            //3. Monitor status of execution until done
            Log.LogDebug($"Monitoring execution status for {fileName}");

            var endJob  = DateTime.Now + TimeSpan.FromMinutes(executionTimeout);
            var done    = false;
            var success = true;

            while (!done && DateTime.Now <= endJob)
            {
                if (executionWaitInterval > 0)
                {
                    await Task.Delay(executionWaitInterval);
                }

                var policyResult = await Policy
                                   .Handle <Exception>()
                                   .WaitAndRetryAsync(
                    3,
                    attempt => TimeSpan.FromMilliseconds(1000),
                    (exception, calculatedWaitDuration) =>
                {
                    Log.LogError(exception, $"PollyAsync: Failed attempt to query Pegasus. Jobid {executionResult.Execution.Id.ToString()}");
                })
                                   .ExecuteAndCaptureAsync(async() =>
                {
                    Log.LogDebug($"Executing monitoring request for {fileName} and jobid {executionResult.Execution.Id.ToString()}");
                    executionResult = await gracefulClient.ExecuteRequest <PegasusExecutionResult>($"{pegasusBaseUrl}{executionRoute}", null, customHeaders, HttpMethod.Get);
                    var status      = executionResult.Execution.ExecutionStatus;
                    success         = string.Compare(status, ExecutionStatus.FINISHED, StringComparison.OrdinalIgnoreCase) == 0 ||
                                      string.Compare(status, ExecutionStatus.SUCCEEDED, StringComparison.OrdinalIgnoreCase) == 0;

                    if (string.Compare(status, ExecutionStatus.FAILED, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        //Try to retrieve why it failed
                        var jobEventsStream = await gracefulClient.ExecuteRequestAsStreamContent($"{pegasusBaseUrl}{executionRoute}/events", HttpMethod.Get, customHeaders);

                        if (jobEventsStream != null)
                        {
                            var jobEvents = await jobEventsStream.ReadAsStringAsync();
                            Log.LogError($"Pegasus job {executionResult.Execution.Id} failed to execute with the events: {jobEvents}");
                            setJobIdAction?.Invoke(new HeaderDictionary {
                                { PEGASUS_LOG_EVENTS_KEY, jobEvents }
                            });
                        }
                        else
                        {
                            Log.LogDebug($"Unable to resolve jobEventsStream for execution id {executionResult.Execution.Id}");
                        }
                    }

                    done = success || string.Compare(status, ExecutionStatus.FAILED, StringComparison.OrdinalIgnoreCase) == 0;

                    setJobIdAction?.Invoke(new HeaderDictionary {
                        { PEGASUS_LOG_RESULT_KEY, status }
                    });

                    Log.LogDebug($"Execution status {status} for {fileName} and jobid {executionResult.Execution.Id.ToString()}");
                });

                if (policyResult.FinalException != null)
                {
                    Log.LogCritical(policyResult.FinalException,
                                    $"TileGeneration PollyAsync: {GetType().FullName} failed with exception for jobid {executionResult.Execution.Id.ToString()}: ");
                    throw policyResult.FinalException;
                }
            }

            if (!done)
            {
                Log.LogInformation($"{nameof(GenerateTiles)} timed out: {fileName}");
            }
            else if (!success)
            {
                Log.LogInformation($"{nameof(GenerateTiles)} failed: {fileName}");
                throw new ServiceException(HttpStatusCode.InternalServerError,
                                           new ContractExecutionResult(ContractExecutionStatesEnum.InternalProcessingError, $"Failed to generate tiles for {fileName}"));
            }

            if (success)
            {
                /*
                 * Can't delete as not mutable
                 *
                 * //4. Delete the execution
                 * Log.LogDebug($"Deleting execution for {dxfFileName}");
                 * await gracefulClient.ExecuteRequest($"{pegasusBaseUrl}{executionRoute}", null, customHeaders, HttpMethod.Delete, null, 0, false);
                 */

                //5. Get the zoom range from the tile metadata file
                var metadataFileName = new DataOceanFileUtil(fileName).TilesMetadataFileName;
                Log.LogDebug($"Getting tiles metadata for {metadataFileName}");
                var stream = await dataOceanClient.GetFile(metadataFileName, customHeaders);

                using (var sr = new StreamReader(stream))
                    using (var jtr = new JsonTextReader(sr))
                    {
                        metadata = new JsonSerializer().Deserialize <TileMetadata>(jtr);
                    }
            }

            Log.LogInformation($"{nameof(GenerateTiles)}: returning {(metadata == null ? "null" : JsonConvert.SerializeObject(metadata))}");

            return(metadata);
        }