mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-25 05:56:58 +01:00
Merge branch 'dev' into 1495-NonGitHubUpdates
This commit is contained in:
+30
-7
@@ -41,14 +41,36 @@ The recommended IDE is Visual Studio 2019 which has installation options for bot
|
||||
In order to run the integration tests you must have the following environment variables set. To run them more accurately, include the optional ones.
|
||||
- `TGS_TEST_DATABASE_TYPE`: `MySql`, `MariaDB`, `PostgresSql`, or `SqlServer`.
|
||||
- `TGS_TEST_CONNECTION_STRING`: To a valid database connection string. You can use the setup wizard to create one.
|
||||
- `TGS_TEST_BRANCH`: Should be either `dev` or `master` depending on what you are working off of. Used for repository tests.
|
||||
- (Optional) `TGS_TEST_GITHUB_TOKEN`: A GitHub personal access token with no scopes used to bypass rate limits.
|
||||
- (Optional) The following variables are all interdependent, so if one is set they all must be.
|
||||
- `TSG_TEST_DISCORD_TOKEN`: To a valid discord bot token.
|
||||
- `TGS_TEST_DISCORD_TOKEN`: To a valid discord bot token.
|
||||
- `TGS_TEST_DISCORD_CHANNEL`: To a valid discord channel ID that the above bot can access.
|
||||
- `TGS_TEST_IRC_CONNECTION_STRING`: To a valid IRC connection string. See the code for [IrcConnectionStringBuilder](../src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs) for details.
|
||||
- `TGS_TEST_IRC_CHANNEL`: To a valid IRC channel accessible with the above connection.
|
||||
|
||||
### Notes About Forks
|
||||
|
||||
For the full CI gambit, the following repository configuration must be set:
|
||||
|
||||
- Setting `Workflow Permissions` to `Read and write permissions`: Enables CodeQL uploads and GitHub Actions comments.
|
||||

|
||||
- Label `CI Cleared`: To allow PRs from forks to run CI with secrets after approval.
|
||||
- Variable `TGS_ENABLE_CODE_QL` to `true`: Enables CodeQL scanning in actions.
|
||||
- Integration [CodeCov](https://github.com/apps/codecov): Enables CodeCov status checks.
|
||||
- Secret `CODECOV_TOKEN`: A CodeCov repo token to work around https://github.com/codecov/codecov-action/issues/837.
|
||||
- Secret `LIVE_TESTS_TOKEN`: A GitHub token with read access to the repository and write access to https://github.com/Cyberboss/common_core (TODO: Make the target repository here configurable). Despite it's name, it may be used across the entire test suite.
|
||||
- Secret `TGS_TEST_DISCORD_TOKEN`: See above note about test environment variables.
|
||||
- Secret `TGS_TEST_DISCORD_CHANNEL`: See above note about test environment variables.
|
||||
- Secret `TGS_TEST_IRC_CONNECTION_STRING`: See above note about test environment variables.
|
||||
- Secret `TGS_TEST_IRC_CHANNEL`: See above note about test environment variables.
|
||||
|
||||
If you don't plan on deploying TGS, the following secrets can be omitted:
|
||||
|
||||
- Secret `DEV_PUSH_TOKEN`: A GitHub token with write access to the repository. Enables doxygen pushes to `gh-pages` branch, and releases creation.
|
||||
- Secret `DOCKER_USERNAME`: Login username for Docker image push.
|
||||
- Secret `DOCKER_PASSWORD`: Login password for Docker image push.
|
||||
- Secret `NUGET_API_KEY`: Nuget.org API Key for client libraries push.
|
||||
|
||||
### Know your Code
|
||||
|
||||
- All feature work should be submitted to the `dev` branch for the next minor release.
|
||||
@@ -224,6 +246,7 @@ We have several subcomponent APIs we ship with the core server that have their o
|
||||
|
||||
- HTTP API
|
||||
- DreamMaker API
|
||||
- Interop API
|
||||
- Configuration File
|
||||
- Host Watchdog
|
||||
- Web Control Panel
|
||||
@@ -237,19 +260,19 @@ All versions are stored in the master file [build/Version.props](../build/Versio
|
||||
The NuGet package Tgstation.Server.Client is another part of the suite which should be versioned separately. However, Tgstation.Server.Api is also a package that is published, and breaking changes can happen independantly of each other.
|
||||
|
||||
- Consider Tgstation.Server.Client it's own product, perform major and minor bumps according to semver semantics including the Tgstation.Server.Api code (but not the version).
|
||||
- Tgstation.Server.Api is a bit tricky as breaking code changes may occur without affecting the actual HTTP contract. For this reason, all code changes that do this should be pushed out as patches, even if they contain breaking changes.
|
||||
- Tgstation.Server.Api is a bit tricky as breaking code changes may occur without affecting the actual HTTP contract. For this reason, the library itself is versioned separately from the API contract.
|
||||
|
||||
## Triage, Deployment, and Releasing
|
||||
|
||||
_This section mainly applies to people with write access to the repository. Anyone is free to propose their work and maintainers will triage it appropriately._
|
||||
|
||||
When issues affecting the server come in, they should be lebeled appropriately and either put into the `Backlog` milestone or current patch milestone depending on if it's a feature request or bug.
|
||||
When issues affecting the server come in, they should be labeled appropriately and either put into the `Backlog` milestone or current patch milestone depending on if it's a feature request or bug.
|
||||
|
||||
After a minor release, the team should decide at that time what will go into it and setup the milestone accordingly. At this point the `Backlog` label should be removed and replaced with `Ready` and the milestone changed from `Backlog` to `vX.Y.0` with X/Y being the major/minor release versions respectively.
|
||||
|
||||
Assign work before beginning on it. When work is started, replace the `Ready` label with the `Work In Progress` label.
|
||||
|
||||
Word commit names descriptively. Only submit work through pull requests. When doing so, link the issue you'll be closing and set the milestone appropriately. Don't forget a changelog. **WARNING** Remember to submit patches to the `master` branch. Also at the time of this writing there appears to be an issue where GitHub won't close issues with PRs to the non-default branch. Maintainers may need to do this manually after merging.
|
||||
Word commit names descriptively. Only submit work through pull requests (With the exception of resolving conflicts in the `master` -> `dev` automatic merge). When doing so, link the issue you'll be closing and set the milestone appropriately. Don't forget a changelog. **WARNING** Remember to submit patches to the `master` branch. Also at the time of this writing there appears to be an issue where GitHub won't close issues with PRs to the non-default branch. Maintainers may need to do this manually after merging. Alternatively, add closing keywords to the commit message and let the `master` -> `dev` merge do the closing.
|
||||
|
||||
At the time of this writing, the repository is configured to automate much of the deployment/release process.
|
||||
|
||||
@@ -259,10 +282,10 @@ That step should be taken for the latest API and client before releasing the cor
|
||||
|
||||
Before releasing the core version, ensure the following:
|
||||
|
||||
- For minor releases, ensure your changes are merging `dev` into `master`.
|
||||
- For minor/major releases, ensure your changes are merging `dev` into `master`.
|
||||
- Ensure all issues and pull requests in the associated milestone are closed (aside from the PR you are using to cut the release).
|
||||
|
||||
To perform the release, merge the PR with `[TGSDeploy]` in the commit message. The build system will handle generating release notes, packaging, and pushing the build to GitHub releases. This will also make it available for servers to self update.
|
||||
To perform the release, merge the PR with `[TGSDeploy]` in the commit message. The build system will handle generating release notes, packaging, and pushing the build to GitHub releases. This will also make it available for servers to self update. Note that `[TGSDeploy]` only affects commits on the `master` branch.
|
||||
|
||||
The build system will also handle closing the current milestone and creating new minor/patch milestones where applicable.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ concurrency:
|
||||
jobs:
|
||||
approve-pr-if-dominion-is-author:
|
||||
name: Approve PR if Dominion is Author
|
||||
if: github.event.pull_request.user.login == 'Cyberboss'
|
||||
if: github.event.pull_request.user.login == 'Cyberboss' && (github.event.pull_request.base.repo.owner.login == 'tgstation' || && github.event.pull_request.base.repo.owner.login == 'Cyberboss')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: GitHub API Call
|
||||
|
||||
@@ -21,6 +21,7 @@ env:
|
||||
TGS_DOTNET_VERSION: 6.0.x
|
||||
TGS_TEST_GITHUB_TOKEN: ${{ secrets.LIVE_TESTS_TOKEN }}
|
||||
TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }}
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: "ci-${{ github.head_ref || github.run_id }}-${{ github.event_name }}"
|
||||
@@ -57,44 +58,6 @@ jobs:
|
||||
- name: GitHub Requires at Least One Step for a Job
|
||||
run: exit 0
|
||||
|
||||
analyze:
|
||||
name: Code Scanning
|
||||
needs: start-ci-run-gate
|
||||
if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Node 12.X
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
|
||||
- name: Upgrade NPM
|
||||
run: npm install -g npm
|
||||
|
||||
- name: Checkout (Branch)
|
||||
uses: actions/checkout@v3
|
||||
if: github.event_name == 'push' || github.event_name == 'schedule'
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Checkout (PR Merge)
|
||||
uses: actions/checkout@v3
|
||||
if: github.event_name != 'push' && github.event_name != 'schedule'
|
||||
with:
|
||||
fetch-depth: 2
|
||||
ref: "refs/pull/${{ github.event.number }}/merge"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: csharp
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
dmapi-build:
|
||||
name: Build DMAPI
|
||||
needs: start-ci-run-gate
|
||||
@@ -114,7 +77,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
id: cache-byond
|
||||
with:
|
||||
path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-dmapibyond
|
||||
|
||||
- name: Install BYOND
|
||||
@@ -186,7 +149,7 @@ jobs:
|
||||
doxyfile-path: 'docs/Doxyfile'
|
||||
|
||||
- name: gh-pages push
|
||||
if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev'
|
||||
if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev' && env.TGS_RELEASE_NOTES_TOKEN != ''
|
||||
run: |
|
||||
git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox
|
||||
pushd $HOME/tgsdox
|
||||
@@ -324,7 +287,7 @@ jobs:
|
||||
database-type: [ 'SqlServer', 'Sqlite', 'PostgresSql', 'MariaDB', 'MySql' ]
|
||||
watchdog-type: [ 'Basic', 'System' ]
|
||||
configuration: [ 'Debug', 'Release' ]
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
BYOND_MAJOR: 514
|
||||
BYOND_MINOR: 1588
|
||||
@@ -338,7 +301,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
id: cache-byond
|
||||
with:
|
||||
path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-livebyond
|
||||
|
||||
- name: Download BYOND
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
name: 'Code Scanning'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 23 * * 1
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: "code-scanning-${{ github.head_ref || github.run_id }}-${{ github.event_name }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Code Scanning
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
if: ${{ vars.TGS_ENABLE_CODE_QL }} == 'true'
|
||||
steps:
|
||||
- name: Install Node 12.X
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
|
||||
- name: Upgrade NPM
|
||||
run: npm install -g npm
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: csharp
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:csharp"
|
||||
+1
-1
@@ -16,6 +16,6 @@ Vulnerabilities should ideally be reported by directly messaging one of the TGS
|
||||
|
||||
Here is a list of their discord IDs.
|
||||
|
||||
@Cyberboss - Dominion#0444 (<@133295178197893120>)
|
||||
@Cyberboss - dominion (<@133295178197893120>)
|
||||
|
||||
Once reported, they will handle the processing of the security advisory.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<!-- Integration tests will ensure they match across the board -->
|
||||
<Import Project="ControlPanelVersion.props" />
|
||||
<PropertyGroup>
|
||||
<TgsCoreVersion>5.12.4</TgsCoreVersion>
|
||||
<TgsCoreVersion>5.12.5</TgsCoreVersion>
|
||||
<TgsConfigVersion>4.6.0</TgsConfigVersion>
|
||||
<TgsApiVersion>9.10.2</TgsApiVersion>
|
||||
<TgsApiLibraryVersion>11.0.0</TgsApiLibraryVersion>
|
||||
|
||||
@@ -70,8 +70,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
IOManager.ConcatPath(
|
||||
PathToUserByondFolder,
|
||||
CacheDirectoryName),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -383,8 +383,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -480,8 +479,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
await HardDisconnect(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
cancellationToken);
|
||||
|
||||
// DCT: Always wait for the job to complete here
|
||||
await jobManager.WaitForJobCompletion(job, null, cancellationToken, default);
|
||||
await jobManager.WaitForJobCompletion(job, null, cancellationToken, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException e)
|
||||
|
||||
@@ -145,8 +145,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
job);
|
||||
await remoteDeploymentManager.StageDeployment(
|
||||
newProvider.CompileJob,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
lock (jobLockCounts)
|
||||
@@ -188,10 +187,8 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
.AsQueryable()
|
||||
.Where(x => x.Job.Instance.Id == metadata.Id)
|
||||
.OrderByDescending(x => x.Job.StoppedAt)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
})
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
});
|
||||
|
||||
if (cj == default(CompileJob))
|
||||
return;
|
||||
@@ -267,8 +264,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
ioManager.ConcatPath(
|
||||
newProvider.Directory,
|
||||
newProvider.DmbName),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
if (!dmbExistsAtRoot)
|
||||
{
|
||||
|
||||
@@ -241,8 +241,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
StartupTimeout = x.StartupTimeout,
|
||||
LogOutput = x.LogOutput,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (ddSettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingDreamDaemonSettings);
|
||||
|
||||
@@ -250,8 +249,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
.DreamMakerSettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == metadata.Id)
|
||||
.FirstAsync(cancellationToken)
|
||||
;
|
||||
.FirstAsync(cancellationToken);
|
||||
if (dreamMakerSettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingDreamMakerSettings);
|
||||
|
||||
@@ -267,8 +265,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
PushTestMergeCommits = x.PushTestMergeCommits,
|
||||
PostTestMergeComment = x.PostTestMergeComment,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (repositorySettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingRepositorySettings);
|
||||
|
||||
@@ -291,8 +288,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
.Include(x => x.ActiveTestMerges)
|
||||
.ThenInclude(x => x.TestMerge)
|
||||
.ThenInclude(x => x.MergedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (revInfo == default)
|
||||
{
|
||||
@@ -319,8 +315,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
repo?.Dispose();
|
||||
throw;
|
||||
}
|
||||
})
|
||||
;
|
||||
});
|
||||
|
||||
var likelyPushedTestMergeCommit =
|
||||
repositorySettings.PushTestMergeCommits.Value
|
||||
@@ -336,8 +331,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
progressReporter,
|
||||
averageSpan,
|
||||
likelyPushedTestMergeCommit,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
var activeCompileJob = compileJobConsumer.LatestCompileJob();
|
||||
try
|
||||
@@ -379,8 +373,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
|
||||
compileJob.Job = fullJob;
|
||||
compileJob.RevisionInformation = fullRevInfo;
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -447,8 +440,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
StoppedAt = x.Job.StoppedAt,
|
||||
StartedAt = x.Job.StartedAt,
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
TimeSpan? averageSpan = null;
|
||||
if (previousCompileJobs.Count != 0)
|
||||
@@ -605,8 +597,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
repoOrigin.ToString(),
|
||||
$"{byondLock.Version.Major}.{byondLock.Version.Minor}",
|
||||
},
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
// determine the dme
|
||||
progressReporter.StageName = "Determining .dme";
|
||||
@@ -978,8 +969,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
remoteDeploymentManager.FailDeployment(
|
||||
job,
|
||||
FormatExceptionForUsers(exception),
|
||||
default))
|
||||
;
|
||||
default));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
await symlinkFactory.CreateSymbolicLink(
|
||||
ioManager.ResolvePath(baseProvider.Directory),
|
||||
ioManager.ResolvePath(LiveGameDirectory),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,8 +185,7 @@ namespace Tgstation.Server.Host.Components
|
||||
Configuration.StartAsync(cancellationToken),
|
||||
ByondManager.StartAsync(cancellationToken),
|
||||
Chat.StartAsync(cancellationToken),
|
||||
dmbFactory.StartAsync(cancellationToken))
|
||||
;
|
||||
dmbFactory.StartAsync(cancellationToken));
|
||||
|
||||
// dependent on so many things, its just safer this way
|
||||
await Watchdog.StartAsync(cancellationToken);
|
||||
@@ -310,8 +309,7 @@ namespace Tgstation.Server.Host.Components
|
||||
repositorySettings.AccessUser,
|
||||
repositorySettings.AccessToken,
|
||||
NextProgressReporter("Fetch Origin"),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
var hasDbChanges = false;
|
||||
RevisionInformation currentRevInfo = null;
|
||||
@@ -385,8 +383,7 @@ namespace Tgstation.Server.Host.Components
|
||||
repositorySettings.CommitterName,
|
||||
repositorySettings.CommitterEmail,
|
||||
NextProgressReporter("Merge Origin"),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
var preserveTestMerges = repositorySettings.AutoUpdatesKeepTestMerges.Value;
|
||||
var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager(
|
||||
@@ -435,16 +432,14 @@ namespace Tgstation.Server.Host.Components
|
||||
repositorySettings.AccessToken,
|
||||
repositorySettings.UpdateSubmodules.Value,
|
||||
NextProgressReporter(StageName),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
var currentHead = repo.Head;
|
||||
|
||||
currentRevInfo = await databaseContext.RevisionInformations
|
||||
.AsQueryable()
|
||||
.Where(x => x.CommitSha == currentHead && x.Instance.Id == metadata.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentHead != startSha && currentRevInfo == default)
|
||||
await UpdateRevInfo(currentHead, true, null);
|
||||
@@ -514,11 +509,9 @@ namespace Tgstation.Server.Host.Components
|
||||
await jobManager.RegisterOperation(
|
||||
repositoryUpdateJob,
|
||||
RepositoryAutoUpdateJob,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
// DCT: First token will cancel the job, second is for cancelling the cancellation, unwanted
|
||||
await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, default);
|
||||
await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, cancellationToken);
|
||||
|
||||
Job compileProcessJob;
|
||||
using (var repo = await RepositoryManager.LoadRepository(cancellationToken))
|
||||
@@ -557,8 +550,7 @@ namespace Tgstation.Server.Host.Components
|
||||
progressReporter,
|
||||
jobCancellationToken);
|
||||
},
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await jobManager.WaitForJobCompletion(compileProcessJob, null, default, cancellationToken);
|
||||
|
||||
@@ -284,8 +284,7 @@ namespace Tgstation.Server.Host.Components
|
||||
await ioManager.WriteAllBytes(
|
||||
ioManager.ConcatPath(oldPath, InstanceController.InstanceAttachFileName),
|
||||
Array.Empty<byte>(),
|
||||
default)
|
||||
;
|
||||
default);
|
||||
}
|
||||
catch (Exception tripleEx)
|
||||
{
|
||||
|
||||
@@ -66,8 +66,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
var mr = await client
|
||||
.MergeRequests
|
||||
.GetAsync($"{RemoteRepositoryOwner}/{RemoteRepositoryName}", parameters.Number)
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
var revisionToUse = parameters.TargetCommitSha == null
|
||||
|| mr.Sha.StartsWith(parameters.TargetCommitSha, StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
@@ -356,8 +356,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
}),
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
|
||||
if (updateSubmodules)
|
||||
{
|
||||
@@ -412,8 +411,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
|
||||
if (updateSubmodules)
|
||||
await UpdateSubmodules(
|
||||
@@ -463,8 +461,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -485,8 +482,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
await ResetToSha(
|
||||
trackedBranch.Tip.Sha,
|
||||
progressReporter.CreateSection(null, updateSubmodules ? 2.0 / 3 : 1.0),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
if (updateSubmodules)
|
||||
await UpdateSubmodules(
|
||||
@@ -619,8 +615,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
|
||||
if (result.Status == MergeStatus.Conflicts)
|
||||
{
|
||||
@@ -684,8 +679,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
{
|
||||
ioMananger.ResolvePath(),
|
||||
},
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -702,8 +696,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
}
|
||||
|
||||
var remainingProgressFactor = 0.9;
|
||||
@@ -759,8 +752,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
},
|
||||
cancellationToken,
|
||||
DefaultIOManager.BlockingTaskCreationOptions,
|
||||
TaskScheduler.Current)
|
||||
;
|
||||
TaskScheduler.Current);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1040,8 +1032,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
|
||||
await Task.WhenAll(
|
||||
ioMananger.DeleteDirectory($".git/modules/{submodule.Path}", cancellationToken),
|
||||
ioMananger.DeleteDirectory(submodule.Path, cancellationToken))
|
||||
;
|
||||
ioMananger.DeleteDirectory(submodule.Path, cancellationToken));
|
||||
|
||||
logger.LogTrace("Second update attempt for submodule {submoduleName}...", submodule.Name);
|
||||
try
|
||||
|
||||
@@ -270,8 +270,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
currentModel.CommitterEmail,
|
||||
NextProgressReporter("Sychronize"),
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
postUpdateSha = repo.Head;
|
||||
}
|
||||
else
|
||||
@@ -305,8 +304,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
currentModel.AccessToken,
|
||||
updateSubmodules,
|
||||
NextProgressReporter("Checkout"),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await CallLoadRevInfo(); // we've either seen origin before or what we're checking out is on origin
|
||||
}
|
||||
else
|
||||
@@ -321,8 +319,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
currentModel.AccessToken,
|
||||
updateSubmodules,
|
||||
NextProgressReporter("Reset to Origin"),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await repo.Sychronize(
|
||||
currentModel.AccessUser,
|
||||
currentModel.AccessToken,
|
||||
@@ -330,8 +327,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
currentModel.CommitterEmail,
|
||||
NextProgressReporter("Synchronize"),
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await CallLoadRevInfo();
|
||||
|
||||
// repo head is on origin so force this
|
||||
@@ -545,8 +541,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
currentModel.CommitterEmail,
|
||||
NextProgressReporter("Synchronize"),
|
||||
false,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await UpdateRevInfo();
|
||||
}
|
||||
|
||||
|
||||
@@ -444,8 +444,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
{
|
||||
var commandResult = await SendCommand(
|
||||
new TopicParameters(port),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
if (commandResult?.ErrorMessage != null)
|
||||
return false;
|
||||
@@ -478,8 +477,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
ReattachInformation.RebootState = newRebootState;
|
||||
var result = await SendCommand(
|
||||
new TopicParameters(newRebootState),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
return result?.ErrorMessage == null;
|
||||
}
|
||||
|
||||
@@ -140,8 +140,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == metadata.Id)
|
||||
.Select(x => x.DreamDaemonSettings.TopicRequestTimeout)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (timeoutMilliseconds == default)
|
||||
{
|
||||
@@ -238,8 +237,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
if (instant)
|
||||
await baseQuery
|
||||
.DeleteAsync(cancellationToken)
|
||||
;
|
||||
.DeleteAsync(cancellationToken);
|
||||
else
|
||||
{
|
||||
var results = await baseQuery.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -652,8 +652,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
await Task.WhenAll(
|
||||
ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken),
|
||||
ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken),
|
||||
ValidateStaticFolder())
|
||||
;
|
||||
ValidateStaticFolder());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -102,8 +102,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
await GameIOManager.MoveDirectory(
|
||||
ActiveSwappable.CompileJob.DirectoryName.ToString(),
|
||||
ActiveSwappable.Directory,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
hardLinkedDmb = ActiveSwappable;
|
||||
}
|
||||
@@ -131,8 +130,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
await GameIOManager.MoveDirectory(
|
||||
hardLink,
|
||||
originalPosition,
|
||||
default)
|
||||
;
|
||||
default);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -465,8 +465,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
var notification = new EventNotification(eventType, parameters);
|
||||
var result = await activeServer.SendCommand(
|
||||
new TopicParameters(notification),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
HandleChatResponses(result);
|
||||
}
|
||||
@@ -853,7 +852,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
var healthCheckSeconds = ActiveLaunchParameters.HeartbeatSeconds.Value;
|
||||
var healthCheck = healthCheckSeconds == 0
|
||||
|| !controller.DMApiAvailable
|
||||
? Extensions.TaskExtensions.InfiniteTask()
|
||||
? Extensions.TaskExtensions.InfiniteTask
|
||||
: Task.Delay(
|
||||
TimeSpan.FromSeconds(healthCheckSeconds),
|
||||
cancellationToken);
|
||||
|
||||
@@ -125,8 +125,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await this.StatusCode(
|
||||
HttpStatusCode.UpgradeRequired,
|
||||
new ErrorMessageResponse(ErrorCode.ApiMismatch))
|
||||
.ExecuteResultAsync(context)
|
||||
;
|
||||
.ExecuteResultAsync(context);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,8 +141,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (requireHeaders)
|
||||
{
|
||||
await HeadersIssue(false)
|
||||
.ExecuteResultAsync(context)
|
||||
;
|
||||
.ExecuteResultAsync(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -218,8 +216,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Generic 501 response.
|
||||
/// </summary>
|
||||
/// <param name="ex">The <see cref="NotImplementedException"/> that was thrown.</param>
|
||||
/// <returns>An <see cref="ObjectResult"/> with <see cref="HttpStatusCode.NotImplemented"/>.</returns>
|
||||
protected ObjectResult RequiresPosixSystemIdentity() => this.StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity));
|
||||
protected ObjectResult RequiresPosixSystemIdentity(NotImplementedException ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "System identities not implemented!");
|
||||
return this.StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strongly type calls to <see cref="ControllerBase.StatusCode(int)"/>.
|
||||
@@ -384,8 +387,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
totalResults = await paginationResult.Results.CountAsync(cancellationToken);
|
||||
pagedResults = await queriedResults
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -189,8 +189,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.ChatBots
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == id)
|
||||
.DeleteAsync(cancellationToken))
|
||||
;
|
||||
.DeleteAsync(cancellationToken));
|
||||
return null;
|
||||
})
|
||||
|
||||
@@ -365,8 +364,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await chat.ChangeChannels(current.Id.Value, current.Channels, cancellationToken);
|
||||
|
||||
return null;
|
||||
})
|
||||
;
|
||||
});
|
||||
if (earlyOut != null)
|
||||
return earlyOut;
|
||||
|
||||
|
||||
@@ -85,12 +85,10 @@ namespace Tgstation.Server.Host.Controllers
|
||||
model.Path,
|
||||
systemIdentity,
|
||||
model.LastReadHash,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
return model.LastReadHash == null ? Accepted(newFile) : Json(newFile);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -100,9 +98,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
AdditionalData = e.Message,
|
||||
});
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
return RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,14 +128,12 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
var result = await instance
|
||||
.Configuration
|
||||
.Read(filePath, systemIdentity, cancellationToken)
|
||||
;
|
||||
.Read(filePath, systemIdentity, cancellationToken);
|
||||
if (result == null)
|
||||
return this.Gone();
|
||||
|
||||
return Json(result);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -147,9 +143,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
AdditionalData = e.Message,
|
||||
});
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
return RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +180,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
var result = await instance
|
||||
.Configuration
|
||||
.ListDirectory(directoryPath, systemIdentity, cancellationToken)
|
||||
;
|
||||
.ListDirectory(directoryPath, systemIdentity, cancellationToken);
|
||||
if (result == null)
|
||||
return new PaginatableResult<ConfigurationFileResponse>(this.Gone());
|
||||
|
||||
@@ -194,10 +189,10 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.OrderBy(x => x.Path));
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return new PaginatableResult<ConfigurationFileResponse>(
|
||||
RequiresPosixSystemIdentity());
|
||||
RequiresPosixSystemIdentity(ex));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
@@ -258,8 +253,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Configuration
|
||||
.CreateDirectory(model.Path, systemIdentity, cancellationToken)
|
||||
? Json(resultModel)
|
||||
: Created(resultModel))
|
||||
;
|
||||
: Created(resultModel));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -269,9 +263,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
Message = e.Message,
|
||||
});
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
return RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
@@ -309,9 +303,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
? NoContent()
|
||||
: Conflict(new ErrorMessageResponse(ErrorCode.ConfigurationDirectoryNotEmpty)));
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
return RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
|
||||
@@ -95,8 +95,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
(core, databaseContextFactory, paramJob, progressHandler, innerCt) => core.Watchdog.Launch(innerCt),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
return Accepted(job.ToApi());
|
||||
});
|
||||
|
||||
@@ -242,8 +241,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await watchdog.ResetRebootState(cancellationToken);
|
||||
|
||||
return await ReadImpl(current, cancellationToken);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1502
|
||||
@@ -277,8 +275,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
(core, paramJob, databaseContextFactory, progressReporter, ct) => core.Watchdog.Restart(false, ct),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
return Accepted(job.ToApi());
|
||||
});
|
||||
|
||||
@@ -311,8 +308,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
(core, databaseContextFactory, paramJob, progressReporter, ct) => core.Watchdog.CreateDump(ct),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
return Accepted(job.ToApi());
|
||||
});
|
||||
|
||||
@@ -337,8 +333,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.Select(x => x.DreamDaemonSettings)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (settings == default)
|
||||
return this.Gone();
|
||||
}
|
||||
|
||||
@@ -80,8 +80,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.DreamMakerSettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
return Json(dreamMakerSettings.ToApi());
|
||||
}
|
||||
|
||||
@@ -153,8 +152,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
job,
|
||||
(core, databaseContextFactory, paramJob, progressReporter, jobCancellationToken)
|
||||
=> core.DreamMaker.DeploymentProcess(paramJob, databaseContextFactory, progressReporter, jobCancellationToken),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
return Accepted(job.ToApi());
|
||||
}
|
||||
|
||||
@@ -189,8 +187,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.DreamMakerSettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (hostModel == null)
|
||||
return this.Gone();
|
||||
|
||||
@@ -215,8 +212,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.GetAvailablePort(
|
||||
model.ApiValidationPort.Value,
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
if (verifiedPort != model.ApiValidationPort)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable));
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
}
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "System identities not implemented!");
|
||||
RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
|
||||
using (systemIdentity)
|
||||
@@ -298,9 +298,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
PasswordHash = x.PasswordHash,
|
||||
Enabled = x.Enabled,
|
||||
Name = x.Name,
|
||||
SystemIdentifier = x.SystemIdentifier,
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Pick the DB user first
|
||||
var user = users
|
||||
@@ -317,13 +317,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
// FALLBACK TO THE DB USER HERE, DO NOT REVEAL A SYSTEM LOGIN!!!
|
||||
// This of course, allows system users to discover TGS users in this (HIGHLY IMPROBABLE) case but that is not our fault
|
||||
var originalHash = user.PasswordHash;
|
||||
var isDbUser = originalHash != null;
|
||||
bool usingSystemIdentity = systemIdentity != null && !isDbUser;
|
||||
var isLikelyDbUser = originalHash != null;
|
||||
bool usingSystemIdentity = systemIdentity != null && !isLikelyDbUser;
|
||||
if (!oAuthLogin)
|
||||
if (!usingSystemIdentity)
|
||||
{
|
||||
// DB User password check and update
|
||||
if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
|
||||
if (!isLikelyDbUser || !cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
|
||||
return Unauthorized();
|
||||
if (user.PasswordHash != originalHash)
|
||||
{
|
||||
@@ -337,14 +337,30 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (systemIdentity.Username != user.Name)
|
||||
else
|
||||
{
|
||||
// System identity username change update
|
||||
Logger.LogDebug("User ID {userId}'s system identity needs a refresh, updating database.", user.Id);
|
||||
DatabaseContext.Users.Attach(user);
|
||||
user.Name = systemIdentity.Username;
|
||||
user.CanonicalName = Models.User.CanonicalizeName(user.Name);
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
var usernameMismatch = systemIdentity.Username != user.Name;
|
||||
if (isLikelyDbUser || usernameMismatch)
|
||||
{
|
||||
DatabaseContext.Users.Attach(user);
|
||||
if (isLikelyDbUser)
|
||||
{
|
||||
// cleanup from https://github.com/tgstation/tgstation-server/issues/1528
|
||||
Logger.LogDebug("System user ID {userId}'s PasswordHash is polluted, updating database.", user.Id);
|
||||
user.PasswordHash = null;
|
||||
user.LastPasswordUpdate = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
if (usernameMismatch)
|
||||
{
|
||||
// System identity username change update
|
||||
Logger.LogDebug("User ID {userId}'s system identity needs a refresh, updating database.", user.Id);
|
||||
user.Name = systemIdentity.Username;
|
||||
user.CanonicalName = Models.User.CanonicalizeName(user.Name);
|
||||
}
|
||||
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the bookeeping is done, tell them to fuck off if necessary
|
||||
|
||||
@@ -183,8 +183,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (earlyOut != null && !newCancellationToken.IsCancellationRequested)
|
||||
cts.Cancel();
|
||||
},
|
||||
newCancellationToken)
|
||||
;
|
||||
newCancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -420,8 +419,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.ChatBots
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == originalModel.Id)
|
||||
.CountAsync(cancellationToken)
|
||||
;
|
||||
.CountAsync(cancellationToken);
|
||||
|
||||
if (countOfExistingChatBots > model.ChatBotLimit.Value)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.ChatBotMax));
|
||||
@@ -487,8 +485,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
job,
|
||||
(core, databaseContextFactory, paramJob, progressHandler, ct) // core will be null here since the instance is offline
|
||||
=> InstanceOperations.MoveInstance(originalModel, originalModelPath, ct),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
api.MoveJob = job.ToApi();
|
||||
}
|
||||
|
||||
@@ -549,8 +546,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.Include(x => x.Instance)
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var needsUpdate = false;
|
||||
var result = await Paginated<Models.Instance, InstanceResponse>(
|
||||
@@ -566,8 +562,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
},
|
||||
page,
|
||||
pageSize,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
if (needsUpdate)
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
@@ -626,8 +621,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.SelectMany(x => x.Jobs)
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
api.MoveJob = moveJob?.ToApi();
|
||||
await CheckAccessible(api, cancellationToken);
|
||||
return Json(api);
|
||||
@@ -655,14 +649,12 @@ namespace Tgstation.Server.Host.Controllers
|
||||
var usersInstancePermissionSet = await BaseQuery()
|
||||
.SelectMany(x => x.InstancePermissionSets)
|
||||
.Where(x => x.PermissionSetId == AuthenticationContext.PermissionSet.Id.Value)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (usersInstancePermissionSet == default)
|
||||
{
|
||||
// does the instance actually exist?
|
||||
var instanceExists = await BaseQuery()
|
||||
.AnyAsync(cancellationToken)
|
||||
;
|
||||
.AnyAsync(cancellationToken);
|
||||
|
||||
if (!instanceExists)
|
||||
return this.Gone();
|
||||
@@ -701,8 +693,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.GetAvailablePort(
|
||||
Math.Min((ushort)(ddPort.Value + 1), DefaultApiValidationPort),
|
||||
false,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
if (!dmPort.HasValue)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -74,8 +74,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
UserId = x.UserId,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (existingPermissionSet == default)
|
||||
return this.Gone();
|
||||
@@ -87,8 +86,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == existingPermissionSet.UserId.Value)
|
||||
.Select(x => x.CanonicalName)
|
||||
.FirstAsync(cancellationToken)
|
||||
;
|
||||
.FirstAsync(cancellationToken);
|
||||
|
||||
if (userCanonicalName == Models.User.CanonicalizeName(Models.User.TgsSystemUserName))
|
||||
return Forbid();
|
||||
@@ -138,8 +136,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.SelectMany(x => x.InstancePermissionSets)
|
||||
.Where(x => x.PermissionSetId == model.PermissionSetId)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (originalPermissionSet == null)
|
||||
return this.Gone();
|
||||
|
||||
@@ -220,8 +217,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.SelectMany(x => x.InstancePermissionSets)
|
||||
.Where(x => x.PermissionSetId == id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (permissionSet == default)
|
||||
return this.Gone();
|
||||
return Json(permissionSet.ToApi());
|
||||
@@ -247,8 +243,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == Instance.Id)
|
||||
.SelectMany(x => x.InstancePermissionSets)
|
||||
.Where(x => x.PermissionSetId == id)
|
||||
.DeleteAsync(cancellationToken)
|
||||
;
|
||||
.DeleteAsync(cancellationToken);
|
||||
|
||||
return numDeleted > 0 ? NoContent() : this.Gone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Include(x => x.StartedBy)
|
||||
.Where(x => x.Id == id && x.Instance.Id == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (job == default)
|
||||
return NotFound();
|
||||
|
||||
@@ -163,8 +162,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == id && x.Instance.Id == Instance.Id)
|
||||
.Include(x => x.StartedBy)
|
||||
.Include(x => x.CancelledBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (job == default)
|
||||
return NotFound();
|
||||
var api = job.ToApi();
|
||||
|
||||
@@ -104,8 +104,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.RepositorySettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentModel == default)
|
||||
return this.Gone();
|
||||
@@ -179,19 +178,16 @@ namespace Tgstation.Server.Host.Controllers
|
||||
databaseContext.Instances.Attach(instance);
|
||||
if (await PopulateApi(api, repos, databaseContext, instance, ct))
|
||||
await databaseContext.Save(ct);
|
||||
})
|
||||
;
|
||||
});
|
||||
},
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
api.Origin = model.Origin;
|
||||
api.Reference = model.Reference;
|
||||
api.ActiveJob = job.ToApi();
|
||||
|
||||
return Created(api);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -211,8 +207,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.RepositorySettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentModel == default)
|
||||
return this.Gone();
|
||||
@@ -234,8 +229,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
(core, databaseContextFactory, paramJob, progressReporter, ct) => core.RepositoryManager.DeleteRepository(ct),
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
api.ActiveJob = job.ToApi();
|
||||
return Accepted(api);
|
||||
}
|
||||
@@ -259,8 +253,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.RepositorySettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentModel == default)
|
||||
return this.Gone();
|
||||
@@ -287,8 +280,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
}
|
||||
|
||||
return Json(api);
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -348,8 +340,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.RepositorySettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.InstanceId == Instance.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentModel == default)
|
||||
return this.Gone();
|
||||
@@ -413,8 +404,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken);
|
||||
|
||||
return null;
|
||||
})
|
||||
;
|
||||
});
|
||||
|
||||
if (earlyOut != null)
|
||||
return earlyOut;
|
||||
|
||||
@@ -128,9 +128,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
dbUser.Name = sysIdentity.Username;
|
||||
dbUser.SystemIdentifier = sysIdentity.Uid;
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
return RequiresPosixSystemIdentity(ex);
|
||||
}
|
||||
else if (!(model.Password?.Length == 0 && model.OAuthConnections?.Any() == true))
|
||||
{
|
||||
@@ -145,7 +145,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
|
||||
Logger.LogInformation("Created new user {0} ({1})", dbUser.Name, dbUser.Id);
|
||||
Logger.LogInformation("Created new user {name} ({id})", dbUser.Name, dbUser.Id);
|
||||
|
||||
return Created(dbUser.ToApi());
|
||||
}
|
||||
@@ -196,8 +196,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Include(x => x.Group)
|
||||
.ThenInclude(x => x.PermissionSet)
|
||||
.Include(x => x.PermissionSet)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (originalUser == default)
|
||||
return NotFound();
|
||||
@@ -216,12 +215,21 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|| (!oAuthEdit && model.OAuthConnections != null))
|
||||
return Forbid();
|
||||
|
||||
var originalUserHasSid = originalUser.SystemIdentifier != null;
|
||||
if (originalUserHasSid && originalUser.PasswordHash != null)
|
||||
{
|
||||
// cleanup from https://github.com/tgstation/tgstation-server/issues/1528
|
||||
Logger.LogDebug("System user ID {userId}'s PasswordHash is polluted, updating database.", originalUser.Id);
|
||||
originalUser.PasswordHash = null;
|
||||
originalUser.LastPasswordUpdate = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier)
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.UserSidChange));
|
||||
|
||||
if (model.Password != null)
|
||||
{
|
||||
if (model.SystemIdentifier != null)
|
||||
if (originalUserHasSid)
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchPasswordSid));
|
||||
|
||||
var result = TrySetPassword(originalUser, model.Password, false);
|
||||
@@ -266,8 +274,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == model.Group.Id)
|
||||
.Include(x => x.PermissionSet)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (originalUser.Group == default)
|
||||
return this.Gone();
|
||||
@@ -409,8 +416,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == model.Group.Id)
|
||||
.Include(x => x.PermissionSet)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
else
|
||||
permissionSet = new Models.PermissionSet
|
||||
{
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
var totalGroups = await DatabaseContext
|
||||
.Groups
|
||||
.AsQueryable()
|
||||
.CountAsync(cancellationToken)
|
||||
;
|
||||
.CountAsync(cancellationToken);
|
||||
if (totalGroups >= generalConfiguration.UserGroupLimit)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.UserGroupLimitReached));
|
||||
|
||||
@@ -122,8 +121,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == model.Id)
|
||||
.Include(x => x.PermissionSet)
|
||||
.Include(x => x.Users)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (currentGroup == default)
|
||||
return this.Gone();
|
||||
@@ -168,8 +166,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.Id == id)
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.PermissionSet)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (group == default)
|
||||
return this.Gone();
|
||||
return Json(group.ToApi(true));
|
||||
@@ -221,8 +218,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Groups
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == id && x.Users.Count == 0)
|
||||
.DeleteAsync(cancellationToken)
|
||||
;
|
||||
.DeleteAsync(cancellationToken);
|
||||
|
||||
if (numDeleted > 0)
|
||||
return NoContent();
|
||||
@@ -232,8 +228,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Groups
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == id)
|
||||
.AnyAsync(cancellationToken)
|
||||
;
|
||||
.AnyAsync(cancellationToken);
|
||||
|
||||
return groupExists
|
||||
? Conflict(new ErrorMessageResponse(ErrorCode.UserGroupNotEmpty))
|
||||
|
||||
@@ -215,8 +215,7 @@ namespace Tgstation.Server.Host.Database
|
||||
.Users
|
||||
.AsQueryable()
|
||||
.Where(x => x.CanonicalName == User.CanonicalizeName(User.TgsSystemUserName))
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (tgsUser != null)
|
||||
logger.LogError(
|
||||
@@ -233,8 +232,7 @@ namespace Tgstation.Server.Host.Database
|
||||
var allInstances = await databaseContext
|
||||
.Instances
|
||||
.AsQueryable()
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
foreach (var instance in allInstances)
|
||||
instance.Path = instance.Path.Replace('\\', '/');
|
||||
}
|
||||
@@ -246,8 +244,7 @@ namespace Tgstation.Server.Host.Database
|
||||
.AsQueryable()
|
||||
.Where(x => x.TopicRequestTimeout == 0)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var rowsUpdated = ids.Count;
|
||||
foreach (var id in ids)
|
||||
@@ -318,8 +315,7 @@ namespace Tgstation.Server.Host.Database
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.PermissionSet)
|
||||
.Include(x => x.Group)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (admin == default)
|
||||
SeedAdminUser(databaseContext);
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace Tgstation.Server.Host.Extensions
|
||||
/// </summary>
|
||||
static readonly TaskCompletionSource InfiniteTaskCompletionSource = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Task"/> that never completes.
|
||||
/// </summary>
|
||||
public static Task InfiniteTask => InfiniteTaskCompletionSource.Task;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="Task"/> that can be awaited while respecting a given <paramref name="cancellationToken"/>.
|
||||
/// </summary>
|
||||
@@ -53,11 +58,5 @@ namespace Tgstation.Server.Host.Extensions
|
||||
|
||||
return await task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Task"/> that never completes.
|
||||
/// </summary>
|
||||
/// <returns>A never ending <see cref="Task"/>.</returns>
|
||||
public static Task InfiniteTask() => InfiniteTaskCompletionSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,21 @@ namespace Tgstation.Server.Host.Jobs
|
||||
/// </summary>
|
||||
sealed class JobHandler : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// If the job has started.
|
||||
/// </summary>
|
||||
public bool Started => task != null;
|
||||
|
||||
/// <summary>
|
||||
/// The progress of the job.
|
||||
/// </summary>
|
||||
public int? Progress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The stage of the job.
|
||||
/// </summary>
|
||||
public string Stage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CancellationTokenSource"/> for <see cref="task"/>.
|
||||
/// </summary>
|
||||
@@ -39,16 +54,6 @@ namespace Tgstation.Server.Host.Jobs
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => cancellationTokenSource.Dispose();
|
||||
|
||||
/// <summary>
|
||||
/// The progress of the job.
|
||||
/// </summary>
|
||||
public int? Progress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The stage of the job.
|
||||
/// </summary>
|
||||
public string Stage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wait for <see cref="task"/> to complete.
|
||||
/// </summary>
|
||||
|
||||
@@ -108,8 +108,7 @@ namespace Tgstation.Server.Host.Jobs
|
||||
if (job.StartedBy == null)
|
||||
job.StartedBy = await databaseContext
|
||||
.Users
|
||||
.GetTgsUser(cancellationToken)
|
||||
;
|
||||
.GetTgsUser(cancellationToken);
|
||||
else
|
||||
job.StartedBy = new User
|
||||
{
|
||||
@@ -256,13 +255,23 @@ namespace Tgstation.Server.Host.Jobs
|
||||
{
|
||||
if (job == null)
|
||||
throw new ArgumentNullException(nameof(job));
|
||||
|
||||
if (!cancellationToken.CanBeCanceled)
|
||||
throw new ArgumentException("A cancellable CancellationToken should be provided!", nameof(cancellationToken));
|
||||
|
||||
JobHandler handler;
|
||||
bool noMoreJobsShouldStart;
|
||||
lock (synchronizationLock)
|
||||
{
|
||||
if (!jobs.TryGetValue(job.Id.Value, out handler))
|
||||
return;
|
||||
|
||||
noMoreJobsShouldStart = this.noMoreJobsShouldStart;
|
||||
}
|
||||
|
||||
if (noMoreJobsShouldStart && !handler.Started)
|
||||
await Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken);
|
||||
|
||||
Task cancelTask = null;
|
||||
using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken)))
|
||||
await handler.Wait(cancellationToken);
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace Tgstation.Server.Host.Security
|
||||
.Include(x => x.Group)
|
||||
.ThenInclude(x => x.PermissionSet)
|
||||
.Include(x => x.OAuthConnections)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (user == default)
|
||||
{
|
||||
logger.LogWarning("Unable to find user with ID {0}!", userId);
|
||||
@@ -110,8 +109,7 @@ namespace Tgstation.Server.Host.Security
|
||||
.AsQueryable()
|
||||
.Where(x => x.PermissionSetId == userPermissionSet.Id && x.InstanceId == instanceId && x.Instance.SwarmIdentifer == swarmConfiguration.Identifier)
|
||||
.Include(x => x.Instance)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (instancePermissionSet == null)
|
||||
logger.LogDebug("User {0} does not have permissions on instance {1}!", userId, instanceId.Value);
|
||||
|
||||
@@ -68,8 +68,7 @@ namespace Tgstation.Server.Host.Security
|
||||
userId,
|
||||
apiHeaders.InstanceId,
|
||||
tokenValidatedContext.SecurityToken.ValidFrom,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext;
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <inheritdoc />
|
||||
public bool CheckUserPassword(User user, string password)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
if (password == null)
|
||||
throw new ArgumentNullException(nameof(password));
|
||||
|
||||
var result = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
|
||||
switch (result)
|
||||
{
|
||||
|
||||
@@ -63,11 +63,15 @@ namespace Tgstation.Server.Host.Security
|
||||
PrincipalContext pc = null;
|
||||
UserPrincipal principal = null;
|
||||
|
||||
GetUserAndDomainName(user.SystemIdentifier, out _, out var domainName);
|
||||
|
||||
bool TryGetPrincipalFromContextType(ContextType contextType)
|
||||
{
|
||||
try
|
||||
{
|
||||
pc = new PrincipalContext(contextType);
|
||||
pc = domainName != null
|
||||
? new PrincipalContext(contextType, domainName)
|
||||
: new PrincipalContext(contextType);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
|
||||
}
|
||||
@@ -75,9 +79,13 @@ namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(e, "Error loading user for context type {0}!", contextType);
|
||||
logger.LogDebug(
|
||||
ex,
|
||||
"Error loading user for context type {contextType} and principal \"{domainName}\"!",
|
||||
contextType,
|
||||
domainName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -176,8 +176,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
await console.WriteAsync(
|
||||
$"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ",
|
||||
false,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
var portString = await console.ReadLineAsync(false, cancellationToken);
|
||||
if (String.IsNullOrWhiteSpace(portString))
|
||||
return null;
|
||||
@@ -319,8 +318,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
await console.WriteAsync(resolvedPath, true, cancellationToken);
|
||||
bool writeResolved = await PromptYesNo(
|
||||
"Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ",
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
|
||||
if (writeResolved)
|
||||
databaseName = resolvedPath;
|
||||
@@ -344,23 +342,19 @@ namespace Tgstation.Server.Host.Setup
|
||||
await console.WriteAsync(
|
||||
"NOTE: It is HIGHLY reccommended that TGS runs on a complete relational database, specfically *NOT* Sqlite.",
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await console.WriteAsync(
|
||||
"Sqlite, by nature cannot perform several DDL operations. Because of this future compatiblility cannot be guaranteed.",
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await console.WriteAsync(
|
||||
"This means that you may not be able to update to the next minor version of TGS without a clean re-installation!",
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await console.WriteAsync(
|
||||
"Please consider taking the time to set up a relational database if this is meant to be a long-standing server.",
|
||||
true,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
await console.WriteAsync(String.Empty, true, cancellationToken);
|
||||
|
||||
await asyncDelayer.Delay(TimeSpan.FromSeconds(3), cancellationToken);
|
||||
@@ -379,8 +373,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
DatabaseType.SqlServer,
|
||||
DatabaseType.Sqlite),
|
||||
false,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
var databaseTypeString = await console.ReadLineAsync(false, cancellationToken);
|
||||
if (Enum.TryParse<DatabaseType>(databaseTypeString, out var databaseType))
|
||||
return databaseType;
|
||||
@@ -1007,8 +1000,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
elasticSearchConfiguration,
|
||||
controlPanelConfiguration,
|
||||
swarmConfiguration,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -380,7 +380,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
? asyncDelayer.Delay(
|
||||
TimeSpan.FromMinutes(UpdateCommitTimeoutMinutes),
|
||||
cancellationToken)
|
||||
: Extensions.TaskExtensions.InfiniteTask().WithToken(cancellationToken);
|
||||
: Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken);
|
||||
|
||||
var commitTask = Task.WhenAny(commitTcsTask, timeoutTask);
|
||||
|
||||
@@ -965,8 +965,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
await Task.WhenAll(
|
||||
currentSwarmServers
|
||||
.Where(x => !x.Controller)
|
||||
.Select(HealthRequestForServer))
|
||||
;
|
||||
.Select(HealthRequestForServer));
|
||||
|
||||
lock (swarmServers)
|
||||
if (swarmServers.Count != currentSwarmServers.Count)
|
||||
|
||||
@@ -67,16 +67,14 @@ namespace Tgstation.Server.Host.Utils
|
||||
.AsQueryable()
|
||||
.Where(x => x.Instance.SwarmIdentifer == swarmConfiguration.Identifier)
|
||||
.Select(x => x.Port)
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var dmPorts = await databaseContext
|
||||
.DreamMakerSettings
|
||||
.AsQueryable()
|
||||
.Where(x => x.Instance.SwarmIdentifer == swarmConfiguration.Identifier)
|
||||
.Select(x => x.ApiValidationPort)
|
||||
.ToListAsync(cancellationToken)
|
||||
;
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
ushort port = 0;
|
||||
|
||||
@@ -59,8 +59,8 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
|
||||
if (!targetActiveJob.Progress.HasValue)
|
||||
{
|
||||
// give it 15 more seconds
|
||||
targetActiveJob = await WaitForJobProgress(targetActiveJob, 15, cancellationToken);
|
||||
// give it a few more seconds
|
||||
targetActiveJob = await WaitForJobProgress(targetActiveJob, 30, cancellationToken);
|
||||
allJobs = await JobsClient.List(null, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,29 +224,47 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
Assert.AreEqual(1, dumpFiles.Length);
|
||||
File.Delete(dumpFiles.Single());
|
||||
|
||||
KillDD(true);
|
||||
var jobTcs = new TaskCompletionSource();
|
||||
var killTaskStarted = new TaskCompletionSource();
|
||||
var killTask = Task.Run(() =>
|
||||
{
|
||||
killTaskStarted.SetResult();
|
||||
while (!jobTcs.Task.IsCompleted)
|
||||
KillDD(false);
|
||||
}, cancellationToken);
|
||||
|
||||
JobResponse job;
|
||||
try
|
||||
while (true)
|
||||
{
|
||||
await killTaskStarted.Task;
|
||||
var dumpTask = instanceClient.DreamDaemon.CreateDump(cancellationToken);
|
||||
job = await WaitForJob(await dumpTask, 20, true, null, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
jobTcs.SetResult();
|
||||
await killTask;
|
||||
KillDD(true);
|
||||
var jobTcs = new TaskCompletionSource();
|
||||
var killTaskStarted = new TaskCompletionSource();
|
||||
var killTask = Task.Run(() =>
|
||||
{
|
||||
killTaskStarted.SetResult();
|
||||
while (!jobTcs.Task.IsCompleted)
|
||||
KillDD(false);
|
||||
}, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await killTaskStarted.Task;
|
||||
var dumpTask = instanceClient.DreamDaemon.CreateDump(cancellationToken);
|
||||
job = await WaitForJob(await dumpTask, 20, true, null, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
jobTcs.SetResult();
|
||||
await killTask;
|
||||
}
|
||||
|
||||
// these can also happen
|
||||
|
||||
if (!(new PlatformIdentifier().IsWindows
|
||||
&& (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.")
|
||||
|| job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.")
|
||||
|| job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)")
|
||||
|| job.ExceptionDetails.Contains("System.InvalidOperationException: No process is associated with this object.")
|
||||
|| job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 2147942424: The program issued a command but the command length is incorrect."))))
|
||||
break;
|
||||
|
||||
var restartJob = await instanceClient.DreamDaemon.Restart(cancellationToken);
|
||||
await WaitForJob(restartJob, 20, false, null, cancellationToken);
|
||||
}
|
||||
|
||||
Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}");
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken);
|
||||
|
||||
var ddStatus = await instanceClient.DreamDaemon.Read(cancellationToken);
|
||||
@@ -543,7 +561,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
TopicResponse topicRequestResult = null;
|
||||
try
|
||||
{
|
||||
System.Console.WriteLine($"Topic limit test S:{payloadSize}...");
|
||||
System.Console.WriteLine($"Topic send limit test S:{currentSize}...");
|
||||
topicRequestResult = await TopicClientNoLogger.SendTopic(
|
||||
IPAddress.Loopback,
|
||||
$"tgs_integration_test_tactics3={TopicClient.SanitizeString(JsonConvert.SerializeObject(topic, DMApiConstants.SerializerSettings))}",
|
||||
@@ -575,6 +593,8 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
|
||||
Assert.AreEqual(DMApiConstants.MaximumTopicRequestLength, (uint)lastSize);
|
||||
|
||||
System.Console.WriteLine("TEST: Receiving Topic tests topics...");
|
||||
|
||||
// Receive
|
||||
baseSize = 1;
|
||||
nextPow = 0;
|
||||
@@ -582,6 +602,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var currentSize = baseSize + (int)Math.Pow(2, nextPow);
|
||||
System.Console.WriteLine($"Topic recieve limit test S:{currentSize}...");
|
||||
var topicRequestResult = await TopicClientNoLogger.SendTopic(
|
||||
IPAddress.Loopback,
|
||||
$"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}",
|
||||
|
||||
Reference in New Issue
Block a user