1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using Microsoft.Extensions.Primitives;
7 using System.Collections.Generic;
12 using System.Threading.Tasks;
32 const string OctokitException =
"Bad GitHub API response, check configuration! Exception: {0}";
90 ILogger<AdministrationController> logger,
91 IOptions<UpdatesConfiguration> updatesConfigurationOptions,
92 IOptions<GeneralConfiguration> generalConfigurationOptions)
95 authenticationContextFactory,
100 this.gitHubClientFactory = gitHubClientFactory ??
throw new ArgumentNullException(nameof(gitHubClientFactory));
101 this.serverUpdater = serverUpdater ??
throw new ArgumentNullException(nameof(serverUpdater));
102 this.assemblyInformationProvider = assemblyInformationProvider ??
throw new ArgumentNullException(nameof(assemblyInformationProvider));
103 this.ioManager = ioManager ??
throw new ArgumentNullException(nameof(ioManager));
104 this.platformIdentifier = platformIdentifier ??
throw new ArgumentNullException(nameof(platformIdentifier));
105 updatesConfiguration = updatesConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(updatesConfigurationOptions));
106 generalConfiguration = generalConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(generalConfigurationOptions));
109 StatusCodeResult
RateLimit(RateLimitExceededException exception)
111 Logger.LogWarning(
"Exceeded GitHub rate limit! Exception {0}", exception);
112 var secondsString = Math.Ceiling((exception.Reset - DateTimeOffset.Now).TotalSeconds).ToString(CultureInfo.InvariantCulture);
113 Response.Headers.Add(
"Retry-After",
new StringValues(secondsString));
114 return StatusCode(429);
125 Logger.LogDebug(
"Looking for GitHub releases version {0}...", newVersion);
126 IEnumerable<Release> releases;
129 var gitHubClient = GetGitHubClient();
130 releases = await gitHubClient
133 .GetAll(updatesConfiguration.GitHubRepositoryId)
134 .WithToken(cancellationToken)
135 .ConfigureAwait(
false);
137 catch (RateLimitExceededException e)
141 catch (ApiException e)
143 Logger.LogWarning(OctokitException, e);
144 return StatusCode((
int)HttpStatusCode.FailedDependency);
147 releases = releases.Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture));
149 Logger.LogTrace(
"Release query complete!");
151 foreach (var release
in releases)
152 if (Version.TryParse(release.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version) && version == newVersion)
154 var asset = release.Assets.Where(x => x.Name.Equals(updatesConfiguration.UpdatePackageAssetName, StringComparison.Ordinal)).FirstOrDefault();
155 if (asset ==
default)
158 if (!serverUpdater.ApplyUpdate(version,
new Uri(asset.BrowserDownloadUrl), ioManager))
162 WindowsHost = platformIdentifier.IsWindows,
163 NewVersion = newVersion
167 return StatusCode((
int)HttpStatusCode.Gone);
170 IGitHubClient GetGitHubClient() => String.IsNullOrEmpty(generalConfiguration.GitHubAccessToken) ? gitHubClientFactory.CreateClient() : gitHubClientFactory.CreateClient(generalConfiguration.GitHubAccessToken);
182 [ProducesResponseType(424)]
184 public async Task<IActionResult>
Read()
188 Version greatestVersion = null;
192 var gitHubClient = GetGitHubClient();
193 var repositoryTask = gitHubClient.Repository.Get(updatesConfiguration.GitHubRepositoryId);
194 var releases = (await gitHubClient.Repository.Release.GetAll(updatesConfiguration.GitHubRepositoryId).ConfigureAwait(
false)).Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture));
196 foreach (var I
in releases)
197 if (Version.TryParse(I.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version)
198 && version.Major == assemblyInformationProvider.Version.Major
199 && (greatestVersion == null || version > greatestVersion))
200 greatestVersion = version;
201 repoUrl =
new Uri((await repositoryTask.ConfigureAwait(
false)).HtmlUrl);
203 catch (NotFoundException e)
205 Logger.LogWarning(
"Not found exception while retrieving upstream repository info: {0}", e);
210 LatestVersion = greatestVersion,
211 TrackedRepositoryUrl = repoUrl,
212 WindowsHost = platformIdentifier.IsWindows
215 catch (RateLimitExceededException e)
219 catch (ApiException e)
221 Logger.LogWarning(OctokitException, e);
224 AdditionalData = e.Message
243 [ProducesResponseType(410)]
245 [ProducesResponseType(424)]
250 throw new ArgumentNullException(nameof(model));
252 if (model.NewVersion == null)
255 AdditionalData =
"newVersion is required!" 258 if (model.NewVersion.Major != assemblyInformationProvider.Version.Major)
261 if(!serverUpdater.WatchdogPresent)
264 return await CheckReleasesAndApplyUpdate(model.NewVersion, cancellationToken).ConfigureAwait(
false);
275 [ProducesResponseType(204)]
277 public async Task<IActionResult>
Delete()
281 if (!serverUpdater.WatchdogPresent)
283 Logger.LogDebug(
"Restart request failed due to lack of host watchdog!");
287 await serverUpdater.Restart().ConfigureAwait(
false);
290 catch (InvalidOperationException)
292 return StatusCode((
int)HttpStatusCode.ServiceUnavailable);
AdministrationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IGitHubClientFactory gitHubClientFactory, IServerControl serverUpdater, IAssemblyInformationProvider assemblyInformationProvider, IIOManager ioManager, IPlatformIdentifier platformIdentifier, ILogger< AdministrationController > logger, IOptions< UpdatesConfiguration > updatesConfigurationOptions, IOptions< GeneralConfiguration > generalConfigurationOptions)
Construct an AdministrationController
For creating and accessing authentication contexts
async Task< IActionResult > Read()
Get Administration server information.
ErrorCode
Types of ErrorMessages that the API may return.
async Task< IActionResult > Delete()
Attempts to restart the server.
readonly IIOManager ioManager
The IIOManager for the AdministrationController
readonly IGitHubClientFactory gitHubClientFactory
The IGitHubClientFactory for the AdministrationController
readonly UpdatesConfiguration updatesConfiguration
The UpdatesConfiguration for the AdministrationController
ApiController for Administration purposes
AdministrationRights
Rights for Models.Administration
const string Administration
The Models.Administration controller
For creating IGitHubClients
StatusCodeResult RateLimit(RateLimitExceededException exception)
Represents administrative server information
Configuration for the automatic update system
Routes to a server actions
async Task< IActionResult > Update([FromBody] Administration model, CancellationToken cancellationToken)
Attempt to perform a server upgrade.
async Task< IActionResult > CheckReleasesAndApplyUpdate(Version newVersion, CancellationToken cancellationToken)
Try to download and apply an update with a given newVersion .
A Controller for API functions
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the AdministrationController
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the AdministrationController
General configuration options
Interface for using filesystems
Represents an error message returned by the server
readonly IServerControl serverUpdater
The IServerControl for the AdministrationController
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the AdministrationController
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...