Skip to content

Initialize environment client

Field Value
Owner BaseEnvironmentClient..ctor and virtual Setup()
Trigger DI resolution of IEnvironmentClient (singleton via ServiceExtensions), or direct new
Inputs IConfiguration, IHostEnvironment.ApplicationName, IHttpJsonClient, IHostResolver, ISystemHashService, and the OS environment variable EnvironmentApiUrl
Outputs Populated ApplicationName, ApplicationHost, TrackName, DeveloperSettingsOverrideFile, TimeToWaitSeconds = 60, and a configured HTTP client

Steps

  1. Initialize fields: ApplicationName = IHostEnvironment.ApplicationName; create empty Settings and Sections. The ServiceUrl field initializer reads Environment.GetEnvironmentVariable("EnvironmentApiUrl") (BR-EC-01).
  2. If ServiceUrl is null or empty, throw MissingEnvironmentUrlVariableException (BR-EC-01). Construction fails.
  3. Pre-check client setup: HttpClient.SetClientInfo(ServiceUrl, "NetCore.EnvironmentClient", HostResolver.System.GetComputerName(), ApplicationName).
  4. Fetch tracks with retry (GetHostsByTrackWithResilience). Read EnvironmentClient:GetHostsByTrack:RetryCount (default 3) and :RetrySleepMs (default 3000). Loop up to RetryCount times: GET /v2/tracks/{ApplicationName}/value. A call fails if it is not successful or returns an empty list (BR-EC-02). On failure, append to the error log and Thread.Sleep(attempt × RetrySleepMs) (linear back-off: 3 s, 6 s, 9 s by default) (BR-EC-03). After all retries fail, throw EnvironmentClientException with the full error log.
  5. Resolve application host (ResolveApplicationHost). Collect the distinct, lowercased, non-blank hosts from all tracks, then call HostResolver.ResolveHost(hosts) and set ApplicationHost = HostResolver.Host (BR-EC-04 through BR-EC-07).
  6. 5.1. The resolved host is determined by the standard HostResolver (the concrete IHostResolver implementation registered by the separate SAIF.NetCore.ServiceExtensions package's RegisterEnvironmentClient/RegisterSecureEnvironmentClient, not a registration owned by this library, and not necessarily the implementation present for any arbitrary injected IHostResolver) as follows. First, a permission check (IsConfigOverrideAllowed): read AppSettings:EnvApiDeveloperOverrideAllowed; if it is empty/missing, overrides are allowed by default; if it is present, bool.TryParse it — an explicit "false" or any malformed/non-boolean value both leave the parsed flag false (indistinguishable from each other), which skips the permission check entirely and denies the override. When the flag is true, the permission check opens a PrincipalContext(ContextType.Domain) and queries Active Directory for the current computer account and the Developer_Machine_Users group; this AD call is not wrapped in a try/catch anywhere in HostResolver — if the domain is unreachable or the lookup otherwise throws (for example a PrincipalServerDownException), that exception propagates uncaught through ResolveHost/ResolveApplicationHost, Setup(), and the constructor, rather than being translated into an EnvironmentClientException. If both AD lookups instead return (neither throws), and either returns null (including a missing/renamed group) or the computer is not a member, the permission check denies the override by returning false — this path alone does not throw. AppConfigOverride is a field initialized to false in the constructor and is only ever reassigned inside TryResolveHostFromAppConfig, which runs only when the permission check above returned true; when the permission check denies the override (or the override is skipped because the flag is already false), TryResolveHostFromAppConfig is never called and AppConfigOverride retains whatever value it already held — false on a fresh HostResolver, but an injected resolver whose public Setup() ran before with permission granted can already be true, and that prior value survives untouched; it is not reset to false by a later denied resolution — rather than being set based on the (unread) AppSettings:ApplicationHost value. Only when permission is granted is AppSettings:ApplicationHost read; a non-empty value there sets the host and AppConfigOverride = true. If permission was denied, or it was granted but the config value was empty, resolution falls back to matching a known track host against a segment of the application's content-root path; if that also finds no match, it falls back again to the local machine name (DefaultedToMachineName = true).
  7. Origin: Step 5.1 is implemented in Resolve host and executed on behalf of this operation.

  8. Resolve track (ResolveEnvironmentTrack). For each track whose Hosts contains the resolved host (case-insensitive), set TrackName = track.Name. If several tracks match, the last one wins (BR-EC-08). If no track matches, throw EnvironmentClientException("No environment track found for host! Host value: {ApplicationHost}").
  9. Developer settings override (ResolveDeveloperSettingsOverride). Read AppSettings:EnvApiDeveloperSettingsOverride. If the file exists and IsDeveloperMachine() is true, set DeveloperSettingsOverrideFile (BR-EC-09). Otherwise silently ignored.
  10. Reconfigure the client for settings retrieval: SetClientInfo(ServiceUrl, "NetCore.EnvironmentClient", ApplicationHost, ApplicationName). Set TimeToWaitSeconds = 60 (BR-EC-10).
  11. (SecureEnvironmentClient only) Gate construction on COREAPIENABLED == "1" or IsDeveloperMachine() (BR-EC-30); throws EnvironmentClientException("This server is not enabled for CORE APIs.") if neither is true. This runs after steps 1–8 have already made network calls.

No settings are loaded during setup; loading is lazy (see Get setting value).

Process flow

flowchart TD
    A["Ctor: read EnvironmentApiUrl"] --> B{ServiceUrl empty?}
    B -- Yes --> C[MissingEnvironmentUrlVariableException]
    B -- No --> D[SetClientInfo with machine name]
    D --> E[GetHostsByTrackWithResilience]
    E -->|all retries fail| F[EnvironmentClientException]
    E -->|success| G[ResolveApplicationHost via HostResolver]
    G --> H[ResolveEnvironmentTrack]
    H -->|no match| I["EnvironmentClientException: no track found"]
    H -->|match| J[ResolveDeveloperSettingsOverride]
    J --> K[SetClientInfo with ApplicationHost/ApplicationName]
    K --> L{SecureEnvironmentClient?}
    L -- Yes --> M{COREAPIENABLED=1 or developer machine?}
    M -- No --> N["EnvironmentClientException: not enabled for CORE APIs"]
    M -- Yes --> O[Client ready]
    L -- No --> O

Sources

  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SecureEnvironmentClient.cs
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/ClientConfigs/GetHostsByTrackConfig.cs
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/Exceptions/MissingEnvironmentUrlVariableException.cs
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/HostResolver.cs