diff --git a/Src/StackifyLib/Models/AzureConfig.cs b/Src/StackifyLib/Models/AzureConfig.cs index 8dad9a0..7f1eefa 100644 --- a/Src/StackifyLib/Models/AzureConfig.cs +++ b/Src/StackifyLib/Models/AzureConfig.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using System; using StackifyLib.Utils; -using System.Linq; #if NETFULL using Microsoft.Win32; #endif @@ -13,22 +11,26 @@ namespace StackifyLib.Models { public class AzureConfig { - private static bool? _inAzure; - private static AzureRoleType _azureRoleType = AzureRoleType.Unknown; - private static string _azureRoleName; - private static string _azureInstanceName; - private static string _entryPoint; + private static AzureConfig _instance = new AzureConfig(); + public static AzureConfig Instance => _instance; - private static readonly DateTime AppStarted = DateTime.UtcNow; + private bool? _inAzure; + private AzureRoleType _azureRoleType = AzureRoleType.Unknown; + private string _azureRoleName; + private string _azureInstanceName; + private string _entryPoint; - private static readonly object Locker = new object(); + private readonly DateTime AppStarted = DateTime.UtcNow; + private readonly object Locker = new object(); + public const string ProductionEnvName = "Production"; - public static string AzureAppWebConfigEnvironment { get; set; } - public static string AzureAppWebConfigApiKey { get; set; } - public static string AzureDriveLetter { get; set; } - public static string AzureInstanceName + public string AzureAppWebConfigEnvironment { get; set; } + public string AzureAppWebConfigApiKey { get; set; } + public string AzureDriveLetter { get; set; } + + public string AzureInstanceName { get { @@ -37,7 +39,7 @@ public static string AzureInstanceName } } - public static bool InAzure + public bool InAzure { get { @@ -62,7 +64,7 @@ public static bool InAzure } } - public static bool IsWebsite + public bool IsWebsite { get { @@ -75,12 +77,24 @@ public static bool IsWebsite } } - private static void EnsureInAzureRan() + private EnvironmentDetail _environmentDetail = null; + + public AzureConfig() + { + + } + + public AzureConfig(EnvironmentDetail environmentDetail) + { + _environmentDetail = environmentDetail; + } + + private void EnsureInAzureRan() { bool ensureTestHasBeenDone = InAzure; } - public static void LoadAzureSettings() + public void LoadAzureSettings() { if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")) == false && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID")) == false) { @@ -90,13 +104,13 @@ public static void LoadAzureSettings() var slotId = GetDeploymentSlotId() ?? "0000"; _azureRoleName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); - _azureInstanceName = $"{Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")} {ServerConfigHelper.GetEnvironment()} [{slotId}-{Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID").Left(6)}]"; + _azureInstanceName = $"{Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")} {GetEnvironment()} [{slotId}-{Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID").Left(6)}]"; return; } } - public static string GetDeploymentSlotId() + public string GetDeploymentSlotId() { try { @@ -113,6 +127,32 @@ public static string GetDeploymentSlotId() return null; } + + public string GetEnvironment() + { + if (IsWebsite) + { + if (_environmentDetail == null) + { + _environmentDetail = EnvironmentDetail.Get(); + } + + var key = Environment.GetEnvironmentVariable("Stackify.Environment"); + if (string.IsNullOrEmpty(key)) + { + key = _environmentDetail.ConfiguredEnvironmentName; + } + + if (string.IsNullOrEmpty(key) == false) + { + return key; + } + + return ProductionEnvName; + } + + return string.Empty; + } } public enum AzureRoleType : short diff --git a/Src/StackifyLib/Models/EnvironmentDetail.cs b/Src/StackifyLib/Models/EnvironmentDetail.cs index 9ed9954..9fbbd04 100644 --- a/Src/StackifyLib/Models/EnvironmentDetail.cs +++ b/Src/StackifyLib/Models/EnvironmentDetail.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2024 BMC Software, Inc. +// Copyright (c) 2024-2025 BMC Software, Inc. // Copyright (c) 2021-2024 Netreo // Copyright (c) 2019 Stackify @@ -41,9 +41,9 @@ private void GetAzureInfo() { //IsAzureWorkerRole is set when instantiating EnvironmentDetail //Useful in other parts directly referencing AzureInstanceName - if(!IsAzureWorkerRole && AzureConfig.InAzure && AzureConfig.IsWebsite) + if(!IsAzureWorkerRole && AzureConfig.Instance.InAzure && AzureConfig.Instance.IsWebsite) { - AzureInstanceName = AzureConfig.AzureInstanceName; + AzureInstanceName = AzureConfig.Instance.AzureInstanceName; } diff --git a/Src/StackifyLib/StackifyLib.csproj b/Src/StackifyLib/StackifyLib.csproj index 36c165e..10eb0d0 100644 --- a/Src/StackifyLib/StackifyLib.csproj +++ b/Src/StackifyLib/StackifyLib.csproj @@ -2,8 +2,8 @@ Stackify API - 2.2.16 - + 2.2.17 + beta1 netstandard2.0;net40;net45;net451;net452;net46;net461;net462 StackifyLib StackifyLib @@ -14,15 +14,15 @@ false false false - 2.2.16 + 2.2.17-beta1 StackifyLib https://github.com/stackify/stackify-api-dotnet https://github.com/stackify/stackify-api-dotnet/blob/master/LICENSE https://github.com/stackify/stackify-api-dotnet git https://stackify.com/wp-content/uploads/2017/02/stk.png - 2.2.16.0 - 2.2.16.0 + 2.2.17.0 + 2.2.17.0 Remove default internal file logger false diff --git a/Src/StackifyLib/Utils/HttpClient.cs b/Src/StackifyLib/Utils/HttpClient.cs index c8619d2..937fdae 100644 --- a/Src/StackifyLib/Utils/HttpClient.cs +++ b/Src/StackifyLib/Utils/HttpClient.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) 2024-2025 BMC Software, Inc. +// Copyright (c) 2021-2024 Netreo +// Copyright (c) 2019 Stackify +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -313,9 +316,9 @@ public bool IdentifyApp() } //Applicable only for Azure AppService - if(AzureConfig.InAzure && AzureConfig.IsWebsite) + if(AzureConfig.Instance.InAzure && AzureConfig.Instance.IsWebsite) { - env.DeviceName = AzureConfig.AzureInstanceName; + env.DeviceName = AzureConfig.Instance.AzureInstanceName; } string jsonData = JsonConvert.SerializeObject(env, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); diff --git a/Src/StackifyLib/Utils/ServerConfigHelper.cs b/Src/StackifyLib/Utils/ServerConfigHelper.cs deleted file mode 100644 index 67834c6..0000000 --- a/Src/StackifyLib/Utils/ServerConfigHelper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using StackifyLib.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace StackifyLib.Utils -{ - public class ServerConfigHelper - { - public const string ProductionEnvName = "Production"; - - public static string GetEnvironment() - { - if (AzureConfig.IsWebsite) - { - var key = Environment.GetEnvironmentVariable("Stackify.Environment"); - - if (string.IsNullOrEmpty(key) == false) - { - return key; - } - - if (string.IsNullOrEmpty(AzureConfig.AzureAppWebConfigEnvironment) == false) - { - return AzureConfig.AzureAppWebConfigEnvironment; - } - - return ProductionEnvName; - } - - return string.Empty; - } - } -} diff --git a/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs new file mode 100644 index 0000000..7ef334e --- /dev/null +++ b/test/StackifyLib.UnitTests/Models/AzureConfig_Tests.cs @@ -0,0 +1,319 @@ +// Copyright (c) 2024-2025 BMC Software, Inc. +using StackifyLib.Models; +using System; +using System.Collections.Generic; +using Xunit; +using Moq; +#if NETCORE || NETCOREX +using Microsoft.Extensions.Configuration; +#endif + +namespace StackifyLib.UnitTests.Models +{ + public class AzureConfig_Tests + { + private readonly MockRepository _mockRepository; + private readonly Dictionary _originalEnvironment = new Dictionary(); + + public AzureConfig_Tests() + { + _mockRepository = new MockRepository(MockBehavior.Strict); + BackupEnvironmentVariables(); + } + + public void Dispose() + { + RestoreEnvironmentVariables(); + } + + private void BackupEnvironmentVariables() + { + _originalEnvironment["WEBSITE_SITE_NAME"] = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); + _originalEnvironment["WEBSITE_INSTANCE_ID"] = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"); + _originalEnvironment["WEBSITE_IIS_SITE_NAME"] = Environment.GetEnvironmentVariable("WEBSITE_IIS_SITE_NAME"); + _originalEnvironment["Stackify.Environment"] = Environment.GetEnvironmentVariable("Stackify.Environment"); + } + + private void RestoreEnvironmentVariables() + { + foreach (var kvp in _originalEnvironment) + { + Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); + } + } + + private void ResetAzureConfig() + { + var instanceField = typeof(AzureConfig).GetField("_instance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + instanceField.SetValue(null, new AzureConfig()); +#if NETCORE || NETCOREX + Config.SetConfiguration(null); +#endif + } + + private void SetEnvironmentVariables(string siteName, string instanceId, string iisSiteName = null) + { + Environment.SetEnvironmentVariable("WEBSITE_SITE_NAME", siteName); + Environment.SetEnvironmentVariable("WEBSITE_INSTANCE_ID", instanceId); + Environment.SetEnvironmentVariable("WEBSITE_IIS_SITE_NAME", iisSiteName); + } + + [Fact] + public void InAzure_ReturnsTrue_WhenAzureEnvironmentDetected() + { + SetEnvironmentVariables("my-site", "instance-id"); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).InAzure; + Assert.True(result); + } + + [Fact] + public void InAzure_ReturnsFalse_WhenNotInAzure() + { + SetEnvironmentVariables(null, null); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).InAzure; + Assert.False(result); + } + + [Fact] + public void IsWebsite_ReturnsTrue_WhenWebAppDetected() + { + SetEnvironmentVariables("my-site", "instance-id"); + ResetAzureConfig(); + + var result = new AzureConfig(new EnvironmentDetail()).IsWebsite; + Assert.True(result); + } + + [Fact] + public void AzureInstanceName_CorrectFormat_WhenInWebApp() + { + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site Production [1234-full-i]", instanceName); + } + + [Fact] + public void GetDeploymentSlotId_ReturnsLast4Chars_WhenSlotPresent() + { + SetEnvironmentVariables(null, null, "site-name__abcd"); + ResetAzureConfig(); + + var slotId = new AzureConfig(new EnvironmentDetail()).GetDeploymentSlotId(); + Assert.Equal("abcd", slotId); + } + + [Fact] + public void GetDeploymentSlotId_ReturnsNull_WhenNoSlotPresent() + { + SetEnvironmentVariables(null, null, "site-name"); + ResetAzureConfig(); + + var slotId = new AzureConfig(new EnvironmentDetail()).GetDeploymentSlotId(); + Assert.Null(slotId); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsConfiguredEnvironment_WhenSet() + { + SetEnvironmentVariables("site", "instance"); + Environment.SetEnvironmentVariable("Stackify.Environment", "Staging"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Staging", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvStagingTestSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingTest"); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingTest [1234-full-i]", instanceName); + } + + [Fact] + public void GetEnvironment_ReturnsProduction_WhenNoConfigurationSet() + { + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Production", env); + } + + [Fact] + public void GetEnvironment_ReturnsEmptyString_WhenNotWebsite() + { + SetEnvironmentVariables(null, null); // Not in Azure + ResetAzureConfig(); + + var env = new AzureConfig().GetEnvironment(); + Assert.Equal(string.Empty, env); + } + +#if NETFULL + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTest_WhenAppSettingsSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "Test"; + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("Test", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTest_WhenEnvSetFirst_AppSettingsSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingFirstApp"); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "Test"; + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("StagingFirstApp", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenAppSettingsEnvStagingAppSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "StagingApp"; + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingApp [1234-full-i]", instanceName); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvSetFirst_AppSettings() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingFirst"); + System.Configuration.ConfigurationManager.AppSettings["Stackify.Environment"] = "StagingApp"; + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingFirst [1234-full-i]", instanceName); + } +#endif + +#if NETCORE || NETCOREX + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTestCore_WhenIConfigurationSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "TestCore" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("TestCore", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void GetEnvironment_ReturnsTestCore_WhenEnvFirst_IConfigurationSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "TestCoreFirst"); + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "TestCore" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var env = new AzureConfig(new EnvironmentDetail()).GetEnvironment(); + Assert.Equal("TestCoreFirst", env); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenEnvSetFirst_IConfigurationEnvStagingConfigSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", "StagingConfigFirst"); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "StagingConfig" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingConfigFirst [1234-full-i]", instanceName); + } + + [Fact(Skip = "Environment set is dynamic. Skip and run manually")] + public void AzureInstanceName_CorrectFormat_WhenIConfigurationEnvStagingConfigSet() + { + Environment.SetEnvironmentVariable("Stackify.Environment", ""); + SetEnvironmentVariables("my-site", "full-instance-id-12345", "site__1234"); + ResetAzureConfig(); + + var iconfig = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Stackify:Environment"] = "StagingConfig" + }) + .Build(); + Config.SetConfiguration(iconfig); + Config.LoadSettings(); + + var instanceName = new AzureConfig(new EnvironmentDetail()).AzureInstanceName; + Assert.Equal("my-site StagingConfig [1234-full-i]", instanceName); + } +#endif + + [Fact] + public void AzureRoleType_SetToWebApp_WhenWebsiteDetected() + { + // Arrange + SetEnvironmentVariables("site", "instance"); + ResetAzureConfig(); + + // Act + bool _ = AzureConfig.Instance.InAzure; // Force initialization + var roleType = GetPrivateField("_azureRoleType"); + + // Assert + Assert.Equal(AzureRoleType.WebApp, roleType); + } + + // Helper to access private fields + private T GetPrivateField(string fieldName) + { + var field = typeof(AzureConfig).GetField(fieldName, + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return (T)field.GetValue(AzureConfig.Instance); + } + } +}