diff --git a/Src/Support/Google.Apis.Auth/OAuth2/ComputeCredential.cs b/Src/Support/Google.Apis.Auth/OAuth2/ComputeCredential.cs
index 1c709560e29..257868e3968 100644
--- a/Src/Support/Google.Apis.Auth/OAuth2/ComputeCredential.cs
+++ b/Src/Support/Google.Apis.Auth/OAuth2/ComputeCredential.cs
@@ -21,9 +21,11 @@ limitations under the License.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -46,8 +48,7 @@ public class ComputeCredential : ServiceCredential, IOidcTokenProvider, IGoogleC
public const string MetadataServerUrl = GoogleAuthConsts.DefaultMetadataServerUrl;
/// Caches result from first call to IsRunningOnComputeEngine
- private readonly static Lazy> isRunningOnComputeEngineCached = new Lazy>(
- () => IsRunningOnComputeEngineNoCache());
+ private readonly static Lazy> isRunningOnComputeEngineCached = new Lazy>(IsRunningOnComputeEngineNoCacheAsync);
///
/// Originally 1000ms was used without a retry. This proved inadequate; even 2000ms without
@@ -295,7 +296,11 @@ public static Task IsRunningOnComputeEngine()
return isRunningOnComputeEngineCached.Value;
}
- private static async Task IsRunningOnComputeEngineNoCache()
+ private static async Task IsRunningOnComputeEngineNoCacheAsync() =>
+ await IsMetadataServerAvailableAsync().ConfigureAwait(false)
+ || await IsGoogleBiosAsync().ConfigureAwait(false);
+
+ private static async Task IsMetadataServerAvailableAsync()
{
Logger.Info("Checking connectivity to ComputeEngine metadata server.");
@@ -337,8 +342,83 @@ private static async Task IsRunningOnComputeEngineNoCache()
}
}
// Only log after all attempts have failed.
- Logger.Debug("Could not reach the Google Compute Engine metadata service. That is expected if this application is not running on GCE.");
+ Logger.Debug("Could not reach the Google Compute Engine metadata service. " +
+ "That is expected if this application is not running on GCE " +
+ "or on some cases where the metadata service is not available during application startup.");
return false;
}
+
+ private async static Task IsGoogleBiosAsync()
+ {
+ Logger.Info("Checking BIOS values to determine GCE residency.");
+ try
+ {
+ if (IsLinux())
+ {
+ return await IsLinuxGoogleBiosAsync().ConfigureAwait(false);
+ }
+ else if (IsWindows())
+ {
+ return IsWindowsGoogleBios();
+ }
+ else
+ {
+ Logger.Info("GCE residency detection through BIOS checking is only supported in Windows and Linux platforms.");
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug($"Could not read BIOS: {ex}");
+ return false;
+ }
+
+ // Some of these will be simpler once we have acted on
+ // https://github.com/googleapis/google-api-dotnet-client/issues/2561.
+
+ bool IsWindows()
+ {
+#if NET45 || NET461
+ // RuntimeInformation.IsOsPlatform is not available for these targets.
+ // But we know we are on Windows.
+ return true;
+#else
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+#endif
+ }
+
+ bool IsLinux()
+ {
+#if NET45 || NET461
+ // RuntimeInformation.IsOsPlatform is not available for these targets.
+ // But we know we are on Windows.
+ return false;
+#else
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+#endif
+ }
+
+ bool IsWindowsGoogleBios() => throw new NotImplementedException();
+
+ async Task IsLinuxGoogleBiosAsync()
+ {
+ Logger.Info("Checking BIOS values on Linux.");
+
+ string fileName = "/sys/class/dmi/id/product_name";
+ if (!File.Exists(fileName))
+ {
+ Logger.Debug($"Couldn't read file {fileName} containing BIOS mapped values.");
+ return false;
+ }
+
+ string productName;
+ using (var streamReader = new StreamReader(new FileStream("/sys/class/dmi/id/product_name", FileMode.Open, FileAccess.Read)))
+ {
+ productName = await streamReader.ReadLineAsync().ConfigureAwait(false);
+ }
+ productName = productName?.Trim();
+ return productName == "Google" || productName == "Google Compute Engine";
+ }
+ }
}
}