/// <summary> /// Pending licenses can offer grace periods. Logs a local issue; trusts the instance (and issue) will be cleared /// when the returned DateTime passes. May subdivide a grace period for more granular issue text. /// </summary> /// <param name="chain"></param> /// <returns></returns> DateTimeOffset?GetGracePeriodFor(ILicenseChain chain) { // If the placeholder license fails its own constraints, don't add a grace period if (chain.Licenses().All(b => !IsLicenseValid(b))) { return(null); } var graceMinutes = chain.Licenses() .Where(IsLicenseValid) .Select(b => b.Fields.NetworkGraceMinutes()) .OrderByDescending(v => v) .FirstOrDefault() ?? DefaultNetworkGraceMinutes; // Success will automatically replace this instance. Warn immediately. Debug.Assert(mgr.FirstHeartbeat != null, "mgr.FirstHeartbeat != null"); // NetworkGraceMinutes Expired? var expires = mgr.FirstHeartbeat.Value.AddMinutes(graceMinutes); if (expires < clock.GetUtcNow()) { permanentIssues.AcceptIssue(new Issue($"Grace period of {graceMinutes}m expired for license {chain.Id}", $"License {chain.Id} was not found in the disk cache and could not be retrieved from the remote server within {graceMinutes} minutes.", IssueSeverity.Error)); return(null); } // Less than 30 seconds since boot time? var thirtySeconds = mgr.FirstHeartbeat.Value.AddSeconds(30); if (thirtySeconds > clock.GetUtcNow()) { AcceptIssue(new Issue($"Fetching license {chain.Id} (not found in disk cache).", $"Network grace period expires in {graceMinutes} minutes", IssueSeverity.Warning)); return(thirtySeconds); } // Otherwise in grace period AcceptIssue(new Issue( $"Grace period of {graceMinutes}m will expire for license {chain.Id} at UTC {expires:HH:mm} on {expires:D}", $"License {chain.Id} was not found in the disk cache and could not be retrieved from the remote server.", IssueSeverity.Error)); return(expires); }
bool IsPendingLicense(ILicenseChain chain) { return(chain.IsRemote && chain.Licenses().All(b => b.Fields.IsRemotePlaceholder())); }