diff --git a/doc/hosts/appservice.md b/doc/hosts/appservice.md index eef75631b..dcb95a00e 100644 --- a/doc/hosts/appservice.md +++ b/doc/hosts/appservice.md @@ -152,5 +152,5 @@ path, removing `/home/site/wwwroot` from it. When using App Service with a Virtual Network or an App Service Environment, you will need to allow outbound access from the webapp to `oryx-cdn.microsoft.io` on port `443`. `oryx-cdn.microsoft.io` hosts the Oryx packages corresponding -to each SDK language and version. If this network dependency is blocked, then App Servicewill not be able to build your +to each SDK language and version. If this network dependency is blocked, then App Service will not be able to build your application using Oryx. diff --git a/src/BuildScriptGenerator/Constants.cs b/src/BuildScriptGenerator/Constants.cs index 7f58a8d6c..b224b9c10 100644 --- a/src/BuildScriptGenerator/Constants.cs +++ b/src/BuildScriptGenerator/Constants.cs @@ -33,5 +33,9 @@ namespace Microsoft.Oryx.BuildScriptGenerator public const string BenvDynamicInstallRootDirKey = "dynamic_install_root_dir"; public const string BuildConfigurationFileHelp = "https://aka.ms/troubleshoot-buildconfig"; + + public const string NetworkConfigurationHelpText = "Please ensure that your " + + "network configuration allows traffic to required Oryx dependencies, as documented in " + + "'https://github.com/microsoft/Oryx/blob/main/doc/hosts/appservice.md#network-dependencies'"; } } diff --git a/src/BuildScriptGenerator/Helpers/ListBlobsHelper.cs b/src/BuildScriptGenerator/Helpers/ListBlobsHelper.cs index e13f15847..d74bf32e1 100644 --- a/src/BuildScriptGenerator/Helpers/ListBlobsHelper.cs +++ b/src/BuildScriptGenerator/Helpers/ListBlobsHelper.cs @@ -2,7 +2,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. // -------------------------------------------------------------------------------------------- +using System; using System.Linq; +using System.Net.Http; using System.Xml.Linq; using Microsoft.Oryx.BuildScriptGenerator.Common; @@ -10,14 +12,31 @@ namespace Microsoft.Oryx.BuildScriptGenerator { internal static class ListBlobsHelper { - public static XDocument GetAllBlobs(string sdkStorageBaseUrl, string platform, System.Net.Http.HttpClient httpClient, string oryxSdkStorageAccountAccessToken) + public static XDocument GetAllBlobs(string sdkStorageBaseUrl, string platform, HttpClient httpClient, string oryxSdkStorageAccountAccessToken) { var oryxSdkStorageAccountAccessArgs = oryxSdkStorageAccountAccessToken?.TrimStart(new char[] { '?' }) ?? string.Empty; var url = string.Format(SdkStorageConstants.ContainerMetadataUrlFormat, sdkStorageBaseUrl, platform, string.Empty, oryxSdkStorageAccountAccessArgs); - var blobList = httpClient - .GetStringAsync(url) - .Result; + string blobList; + try + { + blobList = httpClient + .GetStringAsync(url) + .Result; + } + catch (AggregateException ae) + { + throw new AggregateException( + $"Http request to retrieve the SDKs available to download from '{sdkStorageBaseUrl}' " + + $"failed. {Constants.NetworkConfigurationHelpText}{Environment.NewLine}{ae}"); + } + + if (string.IsNullOrEmpty(blobList)) + { + throw new InvalidOperationException( + $"Http request to retrieve the SDKs available to download from'{sdkStorageBaseUrl}' cannot return an empty result."); + } + var xdoc = XDocument.Parse(blobList); var marker = xdoc.Root.Element("NextMarker").Value; diff --git a/src/BuildScriptGenerator/SdkStorageVersionProviderBase.cs b/src/BuildScriptGenerator/SdkStorageVersionProviderBase.cs index 8228bcc26..7df49457c 100644 --- a/src/BuildScriptGenerator/SdkStorageVersionProviderBase.cs +++ b/src/BuildScriptGenerator/SdkStorageVersionProviderBase.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Threading.Tasks; using System.Xml.XPath; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -97,9 +98,25 @@ namespace Microsoft.Oryx.BuildScriptGenerator this.logger.LogDebug("Getting the default version from url {defaultVersionUrl}.", defaultVersionUrl); // get default version - var defaultVersionContent = httpClient - .GetStringAsync($"{defaultVersionUrl}{this.commonOptions.OryxSdkStorageAccountAccessToken}") - .Result; + string defaultVersionContent; + try + { + defaultVersionContent = httpClient + .GetStringAsync($"{defaultVersionUrl}{this.commonOptions.OryxSdkStorageAccountAccessToken}") + .Result; + } + catch (AggregateException ae) + { + throw new AggregateException( + $"Http request to retrieve the default version from '{defaultVersionUrl}' failed. " + + $"{Constants.NetworkConfigurationHelpText}{Environment.NewLine}{ae}"); + } + + if (string.IsNullOrEmpty(defaultVersionContent)) + { + throw new InvalidOperationException( + $"Http request to retrieve the default version from '{defaultVersionUrl}' cannot return an empty result."); + } string defaultVersion = null; using (var stringReader = new StringReader(defaultVersionContent)) diff --git a/tests/Oryx.Integration.Tests/StorageAccountSanityTestBase.cs b/tests/Oryx.Integration.Tests/StorageAccountSanityTestBase.cs index 2e2b3ed86..23691cad2 100644 --- a/tests/Oryx.Integration.Tests/StorageAccountSanityTestBase.cs +++ b/tests/Oryx.Integration.Tests/StorageAccountSanityTestBase.cs @@ -23,6 +23,8 @@ namespace Oryx.Integration.Tests public abstract class StorageAccountSanityTestBase : PlatformEndToEndTestsBase, IClassFixture { + private const string _fakeStorageUrl = "https://oryx-cdn-fake.microsoft.io"; + private readonly string _storageUrl; private readonly string _repoRootDir; private readonly string _sdkStorageAccountAccessToken; @@ -171,6 +173,17 @@ namespace Oryx.Integration.Tests AssertExpectedDefaultVersion("maven", "java", "maven"); } + [Fact] + public void Throws_CorrectHttpErrorMessage() + { + // Act + var error = Assert.Throws(() => + ListBlobsHelper.GetAllBlobs(_fakeStorageUrl, "dotnet", _httpClient, _sdkStorageAccountAccessToken)); + + // Assert + Assert.Contains(Microsoft.Oryx.BuildScriptGenerator.Constants.NetworkConfigurationHelpText, error.Message); + } + private void AssertExpectedDefaultVersion(string platformName, params string[] expectedPlatformPath) { foreach (var debianFlavor in _debianFlavors)