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¶
- Initialize fields:
ApplicationName = IHostEnvironment.ApplicationName; create emptySettingsandSections. TheServiceUrlfield initializer readsEnvironment.GetEnvironmentVariable("EnvironmentApiUrl")(BR-EC-01). - If
ServiceUrlis null or empty, throwMissingEnvironmentUrlVariableException(BR-EC-01). Construction fails. - Pre-check client setup:
HttpClient.SetClientInfo(ServiceUrl, "NetCore.EnvironmentClient", HostResolver.System.GetComputerName(), ApplicationName). - Fetch tracks with retry (
GetHostsByTrackWithResilience). ReadEnvironmentClient:GetHostsByTrack:RetryCount(default 3) and:RetrySleepMs(default 3000). Loop up toRetryCounttimes: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 andThread.Sleep(attempt × RetrySleepMs)(linear back-off: 3 s, 6 s, 9 s by default) (BR-EC-03). After all retries fail, throwEnvironmentClientExceptionwith the full error log. - Resolve application host (
ResolveApplicationHost). Collect the distinct, lowercased, non-blank hosts from all tracks, then callHostResolver.ResolveHost(hosts)and setApplicationHost = HostResolver.Host(BR-EC-04 through BR-EC-07). - 5.1. The resolved host is determined by the standard
HostResolver(the concreteIHostResolverimplementation registered by the separateSAIF.NetCore.ServiceExtensionspackage'sRegisterEnvironmentClient/RegisterSecureEnvironmentClient, not a registration owned by this library, and not necessarily the implementation present for any arbitrary injectedIHostResolver) as follows. First, a permission check (IsConfigOverrideAllowed): readAppSettings:EnvApiDeveloperOverrideAllowed; if it is empty/missing, overrides are allowed by default; if it is present,bool.TryParseit — an explicit"false"or any malformed/non-boolean value both leave the parsed flagfalse(indistinguishable from each other), which skips the permission check entirely and denies the override. When the flag istrue, the permission check opens aPrincipalContext(ContextType.Domain)and queries Active Directory for the current computer account and theDeveloper_Machine_Usersgroup; this AD call is not wrapped in a try/catch anywhere inHostResolver— if the domain is unreachable or the lookup otherwise throws (for example aPrincipalServerDownException), that exception propagates uncaught throughResolveHost/ResolveApplicationHost,Setup(), and the constructor, rather than being translated into anEnvironmentClientException. If both AD lookups instead return (neither throws), and either returnsnull(including a missing/renamed group) or the computer is not a member, the permission check denies the override by returningfalse— this path alone does not throw.AppConfigOverrideis a field initialized tofalsein the constructor and is only ever reassigned insideTryResolveHostFromAppConfig, which runs only when the permission check above returnedtrue; when the permission check denies the override (or the override is skipped because the flag is alreadyfalse),TryResolveHostFromAppConfigis never called andAppConfigOverrideretains whatever value it already held —falseon a freshHostResolver, but an injected resolver whose publicSetup()ran before with permission granted can already betrue, and that prior value survives untouched; it is not reset tofalseby a later denied resolution — rather than being set based on the (unread)AppSettings:ApplicationHostvalue. Only when permission is granted isAppSettings:ApplicationHostread; a non-empty value there sets the host andAppConfigOverride = 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). -
Origin: Step 5.1 is implemented in Resolve host and executed on behalf of this operation.
- Resolve track (
ResolveEnvironmentTrack). For each track whoseHostscontains the resolved host (case-insensitive), setTrackName = track.Name. If several tracks match, the last one wins (BR-EC-08). If no track matches, throwEnvironmentClientException("No environment track found for host! Host value: {ApplicationHost}"). - Developer settings override (
ResolveDeveloperSettingsOverride). ReadAppSettings:EnvApiDeveloperSettingsOverride. If the file exists andIsDeveloperMachine()is true, setDeveloperSettingsOverrideFile(BR-EC-09). Otherwise silently ignored. - Reconfigure the client for settings retrieval:
SetClientInfo(ServiceUrl, "NetCore.EnvironmentClient", ApplicationHost, ApplicationName). SetTimeToWaitSeconds = 60(BR-EC-10). - (
SecureEnvironmentClientonly) Gate construction onCOREAPIENABLED == "1"orIsDeveloperMachine()(BR-EC-30); throwsEnvironmentClientException("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.cssrc/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SecureEnvironmentClient.cssrc/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/ClientConfigs/GetHostsByTrackConfig.cssrc/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/Exceptions/MissingEnvironmentUrlVariableException.cssrc/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/HostResolver.cs