diff --git a/_api_root_controller_8cs_source.html b/_api_root_controller_8cs_source.html index 713e18a1e1..d17b46008c 100644 --- a/_api_root_controller_8cs_source.html +++ b/_api_root_controller_8cs_source.html @@ -463,9 +463,9 @@
Tgstation.Server.Host.Security.ISystemIdentity
Represents a user on the current global::System.Runtime.InteropServices.OSPlatform.
Definition ISystemIdentity.cs:11
Tgstation.Server.Host.Security.ISystemIdentity.Uid
string Uid
A unique identifier for the user.
Definition ISystemIdentity.cs:15
Tgstation.Server.Host.Security.ISystemIdentity.Username
string Username
The user's name.
Definition ISystemIdentity.cs:20
-
Tgstation.Server.Host.Security.ITokenFactory
For creating TokenResponses.
Definition ITokenFactory.cs:11
+
Tgstation.Server.Host.Security.ITokenFactory
For creating TokenResponses.
Definition ITokenFactory.cs:13
Tgstation.Server.Host.Security.ITokenFactory.CreateToken
TokenResponse CreateToken(Models.User user, bool oAuth)
Create a TokenResponse for a given user .
-
Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
Definition ITokenFactory.cs:15
+
Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
Definition ITokenFactory.cs:22
Tgstation.Server.Host.Security.OAuth.IOAuthProviders
Contains IOAuthValidators.
Definition IOAuthProviders.cs:11
Tgstation.Server.Host.Security.OAuth.IOAuthProviders.GetValidator
IOAuthValidator? GetValidator(OAuthProvider oAuthProvider)
Gets the IOAuthValidator for a given oAuthProvider .
Tgstation.Server.Host.Security.OAuth.IOAuthProviders.ProviderInfos
Dictionary< OAuthProvider, OAuthProviderInfo > ProviderInfos()
Gets a Dictionary<TKey, TValue> of the provider client IDs.
diff --git a/_application_8cs_source.html b/_application_8cs_source.html index a76db02991..21115f626d 100644 --- a/_application_8cs_source.html +++ b/_application_8cs_source.html @@ -825,12 +825,12 @@
Tgstation.Server.Host.Security.IdentityCache
Definition IdentityCache.cs:16
Tgstation.Server.Host.Security.OAuth.OAuthProviders
Definition OAuthProviders.cs:17
Tgstation.Server.Host.Security.PosixSystemIdentityFactory
ISystemIdentityFactory for posix systems.
Definition PosixSystemIdentityFactory.cs:14
-
Tgstation.Server.Host.Security.TokenFactory
Definition TokenFactory.cs:20
+
Tgstation.Server.Host.Security.TokenFactory
Definition TokenFactory.cs:21
Tgstation.Server.Host.Security.WindowsSystemIdentityFactory
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
Definition WindowsSystemIdentityFactory.cs:23
Tgstation.Server.Host.ServerFactory
Implementation of IServerFactory.
Definition ServerFactory.cs:26
Tgstation.Server.Host.Setup.SetupApplication
DI root for configuring a SetupWizard.
Definition SetupApplication.cs:21
Tgstation.Server.Host.Setup.SetupApplication.Configuration
IConfiguration Configuration
The IConfiguration for the SetupApplication.
Definition SetupApplication.cs:25
-
Tgstation.Server.Host.Swarm.SwarmService
Helps keep servers connected to the same database in sync by coordinating updates.
Definition SwarmService.cs:37
+
Tgstation.Server.Host.Swarm.SwarmService
Helps keep servers connected to the same database in sync by coordinating updates.
Definition SwarmService.cs:41
Tgstation.Server.Host.System.AssemblyInformationProvider
Definition AssemblyInformationProvider.cs:12
Tgstation.Server.Host.System.DotnetDumpService
Definition DotnetDumpService.cs:15
Tgstation.Server.Host.System.PosixNetworkPromptReaper
Definition PosixNetworkPromptReaper.cs:5
@@ -889,8 +889,8 @@
Tgstation.Server.Host.Security.IIdentityCache
For caching ISystemIdentitys.
Definition IIdentityCache.cs:12
Tgstation.Server.Host.Security.IPermissionsUpdateNotifyee
Receives notifications about permissions updates.
Definition IPermissionsUpdateNotifyee.cs:12
Tgstation.Server.Host.Security.ISystemIdentityFactory
Factory for ISystemIdentitys.
Definition ISystemIdentityFactory.cs:12
-
Tgstation.Server.Host.Security.ITokenFactory
For creating TokenResponses.
Definition ITokenFactory.cs:11
-
Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
Definition ITokenFactory.cs:15
+
Tgstation.Server.Host.Security.ITokenFactory
For creating TokenResponses.
Definition ITokenFactory.cs:13
+
Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
Definition ITokenFactory.cs:22
Tgstation.Server.Host.Security.OAuth.IOAuthProviders
Contains IOAuthValidators.
Definition IOAuthProviders.cs:11
Tgstation.Server.Host.Setup.IPostSetupServices
Set of objects needed to configure an Core.Application.
Definition IPostSetupServices.cs:10
Tgstation.Server.Host.Setup.IPostSetupServices.GeneralConfiguration
GeneralConfiguration GeneralConfiguration
The Configuration.GeneralConfiguration.
Definition IPostSetupServices.cs:14
diff --git a/_database_context_8cs_source.html b/_database_context_8cs_source.html index b27b2ead7b..e5e8aaea9d 100644 --- a/_database_context_8cs_source.html +++ b/_database_context_8cs_source.html @@ -401,200 +401,202 @@
445 // HEY YOU
446 // IF YOU HAVE A TEST THAT'S CREATING ERRORS BECAUSE THESE VALUES AREN'T SET CORRECTLY THERE'S MORE TO FIXING IT THAN JUST UPDATING THEM
447 // IN THE FUNCTION BELOW YOU ALSO NEED TO CORRECTLY SET THE RIGHT MIGRATION TO DOWNGRADE TO FOR THE LAST TGS VERSION
-
448 // IF THIS BREAKS AGAIN I WILL PERSONALLY HAUNT YOUR ASS WHEN I DIE
-
449
-
453 internal static readonly Type MSLatestMigration = typeof(MSAddDMApiValidationMode);
-
454
-
458 internal static readonly Type MYLatestMigration = typeof(MYAddDMApiValidationMode);
-
459
-
463 internal static readonly Type PGLatestMigration = typeof(PGAddDMApiValidationMode);
-
464
-
468 internal static readonly Type SLLatestMigration = typeof(SLAddDMApiValidationMode);
-
469
-
-
476 private string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
-
477 {
-
478 // Update this with new migrations as they are made
-
479 string? targetMigration = null;
-
480
-
481 string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
-
482
-
483 if (targetVersion < new Version(6, 7, 0))
-
484 targetMigration = currentDatabaseType switch
-
485 {
-
486 DatabaseType.MySql => nameof(MSAddCronAutoUpdates),
-
487 DatabaseType.PostgresSql => nameof(PGAddCronAutoUpdates),
-
488 DatabaseType.SqlServer => nameof(MSAddCronAutoUpdates),
-
489 DatabaseType.Sqlite => nameof(SLAddCronAutoUpdates),
-
490 _ => BadDatabaseType(),
-
491 };
-
492
-
493 if (targetVersion < new Version(6, 6, 0))
-
494 targetMigration = currentDatabaseType switch
-
495 {
-
496 DatabaseType.MySql => nameof(MYAddCompilerAdditionalArguments),
-
497 DatabaseType.PostgresSql => nameof(PGAddCompilerAdditionalArguments),
-
498 DatabaseType.SqlServer => nameof(MSAddCompilerAdditionalArguments),
-
499 DatabaseType.Sqlite => nameof(SLAddCompilerAdditionalArguments),
-
500 _ => BadDatabaseType(),
-
501 };
-
502
-
503 if (targetVersion < new Version(6, 5, 0))
-
504 targetMigration = currentDatabaseType switch
-
505 {
-
506 DatabaseType.MySql => nameof(MYAddMinidumpsOption),
-
507 DatabaseType.PostgresSql => nameof(PGAddMinidumpsOption),
-
508 DatabaseType.SqlServer => nameof(MSAddMinidumpsOption),
-
509 DatabaseType.Sqlite => nameof(SLAddMinidumpsOption),
-
510 _ => BadDatabaseType(),
-
511 };
-
512
-
513 if (targetVersion < new Version(6, 2, 0))
-
514 targetMigration = currentDatabaseType switch
-
515 {
-
516 DatabaseType.MySql => nameof(MYAddTopicPort),
-
517 DatabaseType.PostgresSql => nameof(PGAddTopicPort),
-
518 DatabaseType.SqlServer => nameof(MSAddTopicPort),
-
519 DatabaseType.Sqlite => nameof(SLAddTopicPort),
-
520 _ => BadDatabaseType(),
-
521 };
-
522
-
523 if (targetVersion < new Version(6, 0, 0))
-
524 targetMigration = currentDatabaseType switch
-
525 {
-
526 DatabaseType.MySql => nameof(MYAddJobCodes),
-
527 DatabaseType.PostgresSql => nameof(PGAddJobCodes),
-
528 DatabaseType.SqlServer => nameof(MSAddJobCodes),
-
529 DatabaseType.Sqlite => nameof(SLAddJobCodes),
-
530 _ => BadDatabaseType(),
-
531 };
-
532 if (targetVersion < new Version(5, 17, 0))
-
533 targetMigration = currentDatabaseType switch
-
534 {
-
535 DatabaseType.MySql => nameof(MYAddMapThreads),
-
536 DatabaseType.PostgresSql => nameof(PGAddMapThreads),
-
537 DatabaseType.SqlServer => nameof(MSAddMapThreads),
-
538 DatabaseType.Sqlite => nameof(SLAddMapThreads),
-
539 _ => BadDatabaseType(),
-
540 };
-
541 if (targetVersion < new Version(5, 13, 0))
-
542 targetMigration = currentDatabaseType switch
-
543 {
-
544 DatabaseType.MySql => nameof(MYAddReattachInfoInitialCompileJob),
-
545 DatabaseType.PostgresSql => nameof(PGAddReattachInfoInitialCompileJob),
-
546 DatabaseType.SqlServer => nameof(MSAddReattachInfoInitialCompileJob),
-
547 DatabaseType.Sqlite => nameof(SLAddReattachInfoInitialCompileJob),
-
548 _ => BadDatabaseType(),
-
549 };
-
550 if (targetVersion < new Version(5, 7, 3))
-
551 targetMigration = currentDatabaseType switch
-
552 {
-
553 DatabaseType.MySql => nameof(MYAddDreamDaemonLogOutput),
-
554 DatabaseType.PostgresSql => nameof(PGAddDreamDaemonLogOutput),
-
555 DatabaseType.SqlServer => nameof(MSAddDreamDaemonLogOutput),
-
556 DatabaseType.Sqlite => nameof(SLAddDreamDaemonLogOutput),
-
557 _ => BadDatabaseType(),
-
558 };
-
559 if (targetVersion < new Version(5, 7, 0))
-
560 targetMigration = currentDatabaseType switch
-
561 {
-
562 DatabaseType.MySql => nameof(MYAddProfiler),
-
563 DatabaseType.PostgresSql => nameof(PGAddProfiler),
-
564 DatabaseType.SqlServer => nameof(MSAddProfiler),
-
565 DatabaseType.Sqlite => nameof(SLAddProfiler),
-
566 _ => BadDatabaseType(),
-
567 };
-
568 if (targetVersion < new Version(4, 19, 0))
-
569 targetMigration = currentDatabaseType switch
-
570 {
-
571 DatabaseType.MySql => nameof(MYAddDumpOnHeartbeatRestart),
-
572 DatabaseType.PostgresSql => nameof(PGAddDumpOnHeartbeatRestart),
-
573 DatabaseType.SqlServer => nameof(MSAddDumpOnHeartbeatRestart),
-
574 DatabaseType.Sqlite => nameof(SLAddDumpOnHeartbeatRestart),
-
575 _ => BadDatabaseType(),
-
576 };
-
577 if (targetVersion < new Version(4, 18, 0))
-
578 targetMigration = currentDatabaseType switch
-
579 {
-
580 DatabaseType.MySql => nameof(MYAddUpdateSubmodules),
-
581 DatabaseType.PostgresSql => nameof(PGAddUpdateSubmodules),
-
582 DatabaseType.SqlServer => nameof(MSAddUpdateSubmodules),
-
583 DatabaseType.Sqlite => nameof(SLAddUpdateSubmodules),
-
584 _ => BadDatabaseType(),
-
585 };
-
586 if (targetVersion < new Version(4, 14, 0))
-
587 targetMigration = currentDatabaseType switch
-
588 {
-
589 DatabaseType.MySql => nameof(MYTruncateInstanceNames),
-
590 DatabaseType.PostgresSql => nameof(PGTruncateInstanceNames),
-
591 DatabaseType.SqlServer => nameof(MSTruncateInstanceNames),
-
592 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
-
593 _ => BadDatabaseType(),
-
594 };
-
595 if (targetVersion < new Version(4, 10, 0))
-
596 targetMigration = currentDatabaseType switch
-
597 {
-
598 DatabaseType.MySql => nameof(MSAddRevInfoTimestamp),
-
599 DatabaseType.PostgresSql => nameof(PGAddRevInfoTimestamp),
-
600 DatabaseType.SqlServer => nameof(MSAddRevInfoTimestamp),
-
601 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
-
602 _ => BadDatabaseType(),
-
603 };
-
604 if (targetVersion < new Version(4, 8, 0))
-
605 targetMigration = currentDatabaseType switch
-
606 {
-
607 DatabaseType.MySql => nameof(MYAddSwarmIdentifer),
-
608 DatabaseType.PostgresSql => nameof(PGAddSwarmIdentifer),
-
609 DatabaseType.SqlServer => nameof(MSAddSwarmIdentifer),
-
610 DatabaseType.Sqlite => nameof(SLAddSwarmIdentifer),
-
611 _ => BadDatabaseType(),
-
612 };
-
613 if (targetVersion < new Version(4, 7, 0))
-
614 targetMigration = currentDatabaseType switch
-
615 {
-
616 DatabaseType.MySql => nameof(MYAddAdditionalDDParameters),
-
617 DatabaseType.PostgresSql => nameof(PGAddAdditionalDDParameters),
-
618 DatabaseType.SqlServer => nameof(MSAddAdditionalDDParameters),
-
619 DatabaseType.Sqlite => nameof(SLAddAdditionalDDParameters),
-
620 _ => BadDatabaseType(),
-
621 };
-
622 if (targetVersion < new Version(4, 6, 0))
-
623 targetMigration = currentDatabaseType switch
-
624 {
-
625 DatabaseType.MySql => nameof(MYAddDeploymentColumns),
-
626 DatabaseType.PostgresSql => nameof(PGAddDeploymentColumns),
-
627 DatabaseType.SqlServer => nameof(MSAddDeploymentColumns),
-
628 DatabaseType.Sqlite => nameof(SLAddDeploymentColumns),
-
629 _ => BadDatabaseType(),
-
630 };
-
631 if (targetVersion < new Version(4, 5, 0))
-
632 targetMigration = currentDatabaseType switch
-
633 {
-
634 DatabaseType.MySql => nameof(MYAllowNullDMApi),
-
635 DatabaseType.PostgresSql => nameof(PGAllowNullDMApi),
-
636 DatabaseType.SqlServer => nameof(MSAllowNullDMApi),
-
637 DatabaseType.Sqlite => nameof(SLAllowNullDMApi),
-
638 _ => BadDatabaseType(),
-
639 };
-
640 if (targetVersion < new Version(4, 4, 0))
-
641 targetMigration = currentDatabaseType switch
-
642 {
-
643 DatabaseType.MySql => nameof(MYFixForeignKey),
-
644 DatabaseType.PostgresSql => nameof(PGCreate),
-
645 DatabaseType.SqlServer => nameof(MSRemoveSoftColumns),
-
646 DatabaseType.Sqlite => nameof(SLRemoveSoftColumns),
-
647 _ => BadDatabaseType(),
-
648 };
-
649
-
650 if (targetVersion < new Version(4, 2, 0))
-
651 targetMigration = currentDatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
-
652
-
653 return targetMigration;
-
654 }
+
448 // YOU ALSO NEED TO UPDATE THE SWARM PROTOCOL MAJOR VERSION
+
449 // IF THIS BREAKS AGAIN I WILL PERSONALLY HAUNT YOUR ASS WHEN I DIE
+
450
+
454 internal static readonly Type MSLatestMigration = typeof(MSAddDMApiValidationMode);
+
455
+
459 internal static readonly Type MYLatestMigration = typeof(MYAddDMApiValidationMode);
+
460
+
464 internal static readonly Type PGLatestMigration = typeof(PGAddDMApiValidationMode);
+
465
+
469 internal static readonly Type SLLatestMigration = typeof(SLAddDMApiValidationMode);
+
470
+
+
477 private string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
+
478 {
+
479 // Update this with new migrations as they are made
+
480 string? targetMigration = null;
+
481
+
482 string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
+
483
+
484 // !!! DON'T FORGET TO UPDATE THE SWARM PROTOCOL MAJOR VERSION !!!
+
485 if (targetVersion < new Version(6, 7, 0))
+
486 targetMigration = currentDatabaseType switch
+
487 {
+
488 DatabaseType.MySql => nameof(MSAddCronAutoUpdates),
+
489 DatabaseType.PostgresSql => nameof(PGAddCronAutoUpdates),
+
490 DatabaseType.SqlServer => nameof(MSAddCronAutoUpdates),
+
491 DatabaseType.Sqlite => nameof(SLAddCronAutoUpdates),
+
492 _ => BadDatabaseType(),
+
493 };
+
494
+
495 if (targetVersion < new Version(6, 6, 0))
+
496 targetMigration = currentDatabaseType switch
+
497 {
+
498 DatabaseType.MySql => nameof(MYAddCompilerAdditionalArguments),
+
499 DatabaseType.PostgresSql => nameof(PGAddCompilerAdditionalArguments),
+
500 DatabaseType.SqlServer => nameof(MSAddCompilerAdditionalArguments),
+
501 DatabaseType.Sqlite => nameof(SLAddCompilerAdditionalArguments),
+
502 _ => BadDatabaseType(),
+
503 };
+
504
+
505 if (targetVersion < new Version(6, 5, 0))
+
506 targetMigration = currentDatabaseType switch
+
507 {
+
508 DatabaseType.MySql => nameof(MYAddMinidumpsOption),
+
509 DatabaseType.PostgresSql => nameof(PGAddMinidumpsOption),
+
510 DatabaseType.SqlServer => nameof(MSAddMinidumpsOption),
+
511 DatabaseType.Sqlite => nameof(SLAddMinidumpsOption),
+
512 _ => BadDatabaseType(),
+
513 };
+
514
+
515 if (targetVersion < new Version(6, 2, 0))
+
516 targetMigration = currentDatabaseType switch
+
517 {
+
518 DatabaseType.MySql => nameof(MYAddTopicPort),
+
519 DatabaseType.PostgresSql => nameof(PGAddTopicPort),
+
520 DatabaseType.SqlServer => nameof(MSAddTopicPort),
+
521 DatabaseType.Sqlite => nameof(SLAddTopicPort),
+
522 _ => BadDatabaseType(),
+
523 };
+
524
+
525 if (targetVersion < new Version(6, 0, 0))
+
526 targetMigration = currentDatabaseType switch
+
527 {
+
528 DatabaseType.MySql => nameof(MYAddJobCodes),
+
529 DatabaseType.PostgresSql => nameof(PGAddJobCodes),
+
530 DatabaseType.SqlServer => nameof(MSAddJobCodes),
+
531 DatabaseType.Sqlite => nameof(SLAddJobCodes),
+
532 _ => BadDatabaseType(),
+
533 };
+
534 if (targetVersion < new Version(5, 17, 0))
+
535 targetMigration = currentDatabaseType switch
+
536 {
+
537 DatabaseType.MySql => nameof(MYAddMapThreads),
+
538 DatabaseType.PostgresSql => nameof(PGAddMapThreads),
+
539 DatabaseType.SqlServer => nameof(MSAddMapThreads),
+
540 DatabaseType.Sqlite => nameof(SLAddMapThreads),
+
541 _ => BadDatabaseType(),
+
542 };
+
543 if (targetVersion < new Version(5, 13, 0))
+
544 targetMigration = currentDatabaseType switch
+
545 {
+
546 DatabaseType.MySql => nameof(MYAddReattachInfoInitialCompileJob),
+
547 DatabaseType.PostgresSql => nameof(PGAddReattachInfoInitialCompileJob),
+
548 DatabaseType.SqlServer => nameof(MSAddReattachInfoInitialCompileJob),
+
549 DatabaseType.Sqlite => nameof(SLAddReattachInfoInitialCompileJob),
+
550 _ => BadDatabaseType(),
+
551 };
+
552 if (targetVersion < new Version(5, 7, 3))
+
553 targetMigration = currentDatabaseType switch
+
554 {
+
555 DatabaseType.MySql => nameof(MYAddDreamDaemonLogOutput),
+
556 DatabaseType.PostgresSql => nameof(PGAddDreamDaemonLogOutput),
+
557 DatabaseType.SqlServer => nameof(MSAddDreamDaemonLogOutput),
+
558 DatabaseType.Sqlite => nameof(SLAddDreamDaemonLogOutput),
+
559 _ => BadDatabaseType(),
+
560 };
+
561 if (targetVersion < new Version(5, 7, 0))
+
562 targetMigration = currentDatabaseType switch
+
563 {
+
564 DatabaseType.MySql => nameof(MYAddProfiler),
+
565 DatabaseType.PostgresSql => nameof(PGAddProfiler),
+
566 DatabaseType.SqlServer => nameof(MSAddProfiler),
+
567 DatabaseType.Sqlite => nameof(SLAddProfiler),
+
568 _ => BadDatabaseType(),
+
569 };
+
570 if (targetVersion < new Version(4, 19, 0))
+
571 targetMigration = currentDatabaseType switch
+
572 {
+
573 DatabaseType.MySql => nameof(MYAddDumpOnHeartbeatRestart),
+
574 DatabaseType.PostgresSql => nameof(PGAddDumpOnHeartbeatRestart),
+
575 DatabaseType.SqlServer => nameof(MSAddDumpOnHeartbeatRestart),
+
576 DatabaseType.Sqlite => nameof(SLAddDumpOnHeartbeatRestart),
+
577 _ => BadDatabaseType(),
+
578 };
+
579 if (targetVersion < new Version(4, 18, 0))
+
580 targetMigration = currentDatabaseType switch
+
581 {
+
582 DatabaseType.MySql => nameof(MYAddUpdateSubmodules),
+
583 DatabaseType.PostgresSql => nameof(PGAddUpdateSubmodules),
+
584 DatabaseType.SqlServer => nameof(MSAddUpdateSubmodules),
+
585 DatabaseType.Sqlite => nameof(SLAddUpdateSubmodules),
+
586 _ => BadDatabaseType(),
+
587 };
+
588 if (targetVersion < new Version(4, 14, 0))
+
589 targetMigration = currentDatabaseType switch
+
590 {
+
591 DatabaseType.MySql => nameof(MYTruncateInstanceNames),
+
592 DatabaseType.PostgresSql => nameof(PGTruncateInstanceNames),
+
593 DatabaseType.SqlServer => nameof(MSTruncateInstanceNames),
+
594 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
+
595 _ => BadDatabaseType(),
+
596 };
+
597 if (targetVersion < new Version(4, 10, 0))
+
598 targetMigration = currentDatabaseType switch
+
599 {
+
600 DatabaseType.MySql => nameof(MSAddRevInfoTimestamp),
+
601 DatabaseType.PostgresSql => nameof(PGAddRevInfoTimestamp),
+
602 DatabaseType.SqlServer => nameof(MSAddRevInfoTimestamp),
+
603 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
+
604 _ => BadDatabaseType(),
+
605 };
+
606 if (targetVersion < new Version(4, 8, 0))
+
607 targetMigration = currentDatabaseType switch
+
608 {
+
609 DatabaseType.MySql => nameof(MYAddSwarmIdentifer),
+
610 DatabaseType.PostgresSql => nameof(PGAddSwarmIdentifer),
+
611 DatabaseType.SqlServer => nameof(MSAddSwarmIdentifer),
+
612 DatabaseType.Sqlite => nameof(SLAddSwarmIdentifer),
+
613 _ => BadDatabaseType(),
+
614 };
+
615 if (targetVersion < new Version(4, 7, 0))
+
616 targetMigration = currentDatabaseType switch
+
617 {
+
618 DatabaseType.MySql => nameof(MYAddAdditionalDDParameters),
+
619 DatabaseType.PostgresSql => nameof(PGAddAdditionalDDParameters),
+
620 DatabaseType.SqlServer => nameof(MSAddAdditionalDDParameters),
+
621 DatabaseType.Sqlite => nameof(SLAddAdditionalDDParameters),
+
622 _ => BadDatabaseType(),
+
623 };
+
624 if (targetVersion < new Version(4, 6, 0))
+
625 targetMigration = currentDatabaseType switch
+
626 {
+
627 DatabaseType.MySql => nameof(MYAddDeploymentColumns),
+
628 DatabaseType.PostgresSql => nameof(PGAddDeploymentColumns),
+
629 DatabaseType.SqlServer => nameof(MSAddDeploymentColumns),
+
630 DatabaseType.Sqlite => nameof(SLAddDeploymentColumns),
+
631 _ => BadDatabaseType(),
+
632 };
+
633 if (targetVersion < new Version(4, 5, 0))
+
634 targetMigration = currentDatabaseType switch
+
635 {
+
636 DatabaseType.MySql => nameof(MYAllowNullDMApi),
+
637 DatabaseType.PostgresSql => nameof(PGAllowNullDMApi),
+
638 DatabaseType.SqlServer => nameof(MSAllowNullDMApi),
+
639 DatabaseType.Sqlite => nameof(SLAllowNullDMApi),
+
640 _ => BadDatabaseType(),
+
641 };
+
642 if (targetVersion < new Version(4, 4, 0))
+
643 targetMigration = currentDatabaseType switch
+
644 {
+
645 DatabaseType.MySql => nameof(MYFixForeignKey),
+
646 DatabaseType.PostgresSql => nameof(PGCreate),
+
647 DatabaseType.SqlServer => nameof(MSRemoveSoftColumns),
+
648 DatabaseType.Sqlite => nameof(SLRemoveSoftColumns),
+
649 _ => BadDatabaseType(),
+
650 };
+
651
+
652 if (targetVersion < new Version(4, 2, 0))
+
653 targetMigration = currentDatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
+
654
+
655 return targetMigration;
+
656 }
-
655 }
+
657 }
-
656}
+
658}
DbContext
Exception
Tgstation.Server.Host.Database.DatabaseCollection
Definition DatabaseCollection.cs:15
@@ -630,7 +632,7 @@
Tgstation.Server.Host.Database.DatabaseContext.permissionSets
readonly IDatabaseCollection< PermissionSet > permissionSets
Backing field for IDatabaseContext.PermissionSets.
Definition DatabaseContext.cs:250
Tgstation.Server.Host.Database.DatabaseContext.chatBotsCollection
readonly IDatabaseCollection< ChatBot > chatBotsCollection
Backing field for IDatabaseContext.ChatBots.
Definition DatabaseContext.cs:220
Tgstation.Server.Host.Database.DatabaseContext.compileJobsCollection
readonly IDatabaseCollection< CompileJob > compileJobsCollection
Backing field for IDatabaseContext.CompileJobs.
Definition DatabaseContext.cs:180
-
Tgstation.Server.Host.Database.DatabaseContext.GetTargetMigration
string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
Gets the name of the migration to run for migrating down to a given targetVersion for the currentDat...
Definition DatabaseContext.cs:476
+
Tgstation.Server.Host.Database.DatabaseContext.GetTargetMigration
string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
Gets the name of the migration to run for migrating down to a given targetVersion for the currentDat...
Definition DatabaseContext.cs:477
Tgstation.Server.Host.Database.DatabaseContext.Users
DbSet< User > Users
The Users in the DatabaseContext.
Definition DatabaseContext.cs:29
Tgstation.Server.Host.Database.DatabaseContext.ChatBots
DbSet< ChatBot > ChatBots
The ChatBots in the DatabaseContext.
Definition DatabaseContext.cs:54
Tgstation.Server.Host.Database.DatabaseContext.RevInfoTestMerges
DbSet< RevInfoTestMerge > RevInfoTestMerges
The RevInfoTestMerges in the DatabaseContext.
Definition DatabaseContext.cs:94
diff --git a/_i_swarm_operations_8cs_source.html b/_i_swarm_operations_8cs_source.html index c581cca559..453747cf30 100644 --- a/_i_swarm_operations_8cs_source.html +++ b/_i_swarm_operations_8cs_source.html @@ -101,7 +101,7 @@
28
34 bool ValidateRegistration(Guid registrationId);
35
-
43 ValueTask<bool> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
+
43 ValueTask<SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
44
51 ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken);
52
@@ -115,9 +115,9 @@
Tgstation.Server.Host.Swarm.ISwarmOperations
Swarm service operations for the Controllers.SwarmController.
Definition ISwarmOperations.cs:14
Tgstation.Server.Host.Swarm.ISwarmOperations.UnregisterNode
ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
Attempt to unregister a node with a given registrationId with the controller.
Tgstation.Server.Host.Swarm.ISwarmOperations.RemoteCommitReceived
ValueTask< bool > RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
Notify the controller that the node with the given registrationId is ready to commit or notify the n...
-
Tgstation.Server.Host.Swarm.ISwarmOperations.RegisterNode
ValueTask< bool > RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.
Tgstation.Server.Host.Swarm.ISwarmOperations.PrepareUpdateFromController
ValueTask< bool > PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Notify the node of an update request from the controller.
Tgstation.Server.Host.Swarm.ISwarmOperations.ValidateRegistration
bool ValidateRegistration(Guid registrationId)
Validate a given registrationId .
+
Tgstation.Server.Host.Swarm.ISwarmOperations.RegisterNode
ValueTask< SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.
Tgstation.Server.Host.Swarm.ISwarmOperations.UpdateSwarmServersList
void UpdateSwarmServersList(IEnumerable< SwarmServerInformation > swarmServers)
Pass in an updated list of swarmServers to the node.
Tgstation.Server.Host.Swarm.ISwarmUpdateAborter
Allows aborting a swarm distributed update operation.
Definition ISwarmUpdateAborter.cs:9
Tgstation.Server.Api.Models.Internal
Definition ChatBotApiBase.cs:6
diff --git a/_i_token_factory_8cs_source.html b/_i_token_factory_8cs_source.html index 86322d5319..ce23b60f94 100644 --- a/_i_token_factory_8cs_source.html +++ b/_i_token_factory_8cs_source.html @@ -82,25 +82,30 @@
ITokenFactory.cs
-Go to the documentation of this file.
1using Microsoft.IdentityModel.Tokens;
+Go to the documentation of this file.
1using System;
2
- +
3using Microsoft.IdentityModel.Tokens;
4
- -
6{
-
-
10 public interface ITokenFactory
-
11 {
-
15 TokenValidationParameters ValidationParameters { get; }
-
16
-
23 TokenResponse CreateToken(Models.User user, bool oAuth);
-
24 }
+ +
6
+ +
8{
+
+
12 public interface ITokenFactory
+
13 {
+
17 ReadOnlySpan<byte> SigningKeyBytes { get; set; }
+
18
+
22 TokenValidationParameters ValidationParameters { get; }
+
23
+
30 TokenResponse CreateToken(Models.User user, bool oAuth);
+
31 }
-
25}
+
32}
Represents a JWT returned by the API.
- +
TokenResponse CreateToken(Models.User user, bool oAuth)
Create a TokenResponse for a given user .
-
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
+
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
+
ReadOnlySpan< byte > SigningKeyBytes
Gets or sets the ITokenFactory's signing key bytes.
diff --git a/_master_versions_attribute_8cs_source.html b/_master_versions_attribute_8cs_source.html index c238e39eb0..13239c3935 100644 --- a/_master_versions_attribute_8cs_source.html +++ b/_master_versions_attribute_8cs_source.html @@ -106,32 +106,37 @@
38
42 public string RawMariaDBRedistVersion { get; }
43
-
- -
53 string rawConfigurationVersion,
-
54 string rawInteropVersion,
-
55 string rawWebpanelVersion,
-
56 string rawHostWatchdogVersion,
-
57 string rawMariaDBRedistVersion)
-
58 {
-
59 RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion));
-
60 RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion));
-
61 RawWebpanelVersion = rawWebpanelVersion ?? throw new ArgumentNullException(nameof(rawWebpanelVersion));
-
62 RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion));
-
63 RawMariaDBRedistVersion = rawMariaDBRedistVersion ?? throw new ArgumentNullException(nameof(rawMariaDBRedistVersion));
-
64 }
+
47 public string RawSwarmProtocolVersion { get; }
+
48
+
+ +
59 string rawConfigurationVersion,
+
60 string rawInteropVersion,
+
61 string rawWebpanelVersion,
+
62 string rawHostWatchdogVersion,
+
63 string rawMariaDBRedistVersion,
+
64 string rawSwarmProtocolVersion)
+
65 {
+
66 RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion));
+
67 RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion));
+
68 RawWebpanelVersion = rawWebpanelVersion ?? throw new ArgumentNullException(nameof(rawWebpanelVersion));
+
69 RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion));
+
70 RawMariaDBRedistVersion = rawMariaDBRedistVersion ?? throw new ArgumentNullException(nameof(rawMariaDBRedistVersion));
+
71 RawSwarmProtocolVersion = rawSwarmProtocolVersion ?? throw new ArgumentNullException(nameof(rawSwarmProtocolVersion));
+
72 }
-
65 }
+
73 }
-
66}
+
74}
Attribute for bringing in the master versions list from MSBuild that aren't embedded into assemblies ...
-
MasterVersionsAttribute(string rawConfigurationVersion, string rawInteropVersion, string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion)
Initializes a new instance of the MasterVersionsAttribute class.
+
MasterVersionsAttribute(string rawConfigurationVersion, string rawInteropVersion, string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion, string rawSwarmProtocolVersion)
Initializes a new instance of the MasterVersionsAttribute class.
string RawMariaDBRedistVersion
The Version string of the MariaDB server bundled with TGS installs.
string RawHostWatchdogVersion
The Version string of the control panel version built.
string RawWebpanelVersion
The Version string of the control panel version built.
string RawInteropVersion
The Version string of the DMAPI interop version used.
+
string RawSwarmProtocolVersion
The Version string of the MariaDB server bundled with TGS installs.
string RawConfigurationVersion
The Version string of the Configuration version built.
static MasterVersionsAttribute Instance
Return the Assembly's instance of the MasterVersionsAttribute.
diff --git a/_server_update_operation_8cs_source.html b/_server_update_operation_8cs_source.html index 3a060f0e84..cd0ecdcbd5 100644 --- a/_server_update_operation_8cs_source.html +++ b/_server_update_operation_8cs_source.html @@ -113,7 +113,7 @@
IFileStreamProvider FileStreamProvider
The IFileStreamProvider that contains the update zip file.
ServerUpdateOperation(IFileStreamProvider fileStreamProvider, ISwarmService swarmService, Version targetVersion)
Initializes a new instance of the ServerUpdateOperation class.
Version TargetVersion
The Version being updated to.
-
Helps keep servers connected to the same database in sync by coordinating updates.
+
Helps keep servers connected to the same database in sync by coordinating updates.
Interface for asynchronously consuming Streams of files.
Used for swarm operations. Functions may be no-op based on configuration.
diff --git a/_swarm_controller_8cs_source.html b/_swarm_controller_8cs_source.html index e71ad009c9..ffa3606bcc 100644 --- a/_swarm_controller_8cs_source.html +++ b/_swarm_controller_8cs_source.html @@ -98,8 +98,8 @@
14
15using Tgstation.Server.Host.Configuration;
16using Tgstation.Server.Host.Extensions;
-
17using Tgstation.Server.Host.Swarm;
-
18using Tgstation.Server.Host.System;
+
17using Tgstation.Server.Host.Properties;
+
18using Tgstation.Server.Host.Swarm;
19using Tgstation.Server.Host.Transfer;
20using Tgstation.Server.Host.Utils;
21
@@ -117,185 +117,186 @@
41
45 readonly IFileTransferStreamHandler transferService;
46
-
50 readonly IAssemblyInformationProvider assemblyInformationProvider;
+
50 readonly ILogger<SwarmController> logger;
51
-
55 readonly ILogger<SwarmController> logger;
+
55 readonly SwarmConfiguration swarmConfiguration;
56
-
60 readonly SwarmConfiguration swarmConfiguration;
-
61
-
- - - - -
74 IOptions<SwarmConfiguration> swarmConfigurationOptions,
-
75 ILogger<SwarmController> logger)
-
76 {
-
77 this.swarmOperations = swarmOperations ?? throw new ArgumentNullException(nameof(swarmOperations));
-
78 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
-
79 this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
-
80 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
-
81 this.logger = logger;
-
82 }
+
+ + + +
67 IOptions<SwarmConfiguration> swarmConfigurationOptions,
+
68 ILogger<SwarmController> logger)
+
69 {
+
70 this.swarmOperations = swarmOperations ?? throw new ArgumentNullException(nameof(swarmOperations));
+
71 this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
+
72 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
+
73 this.logger = logger;
+
74 }
-
83
- -
-
91 public async ValueTask<IActionResult> Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)
-
92 {
-
93 ArgumentNullException.ThrowIfNull(registrationRequest);
+
75
+ +
+
83 public async ValueTask<IActionResult> Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)
+
84 {
+
85 ArgumentNullException.ThrowIfNull(registrationRequest);
+
86
+
87 var swarmProtocolVersion = Version.Parse(MasterVersionsAttribute.Instance.RawSwarmProtocolVersion);
+
88 if (registrationRequest.ServerVersion?.Major != swarmProtocolVersion.Major)
+
89 return StatusCode((int)HttpStatusCode.UpgradeRequired);
+
90
+
91 var registrationResult = await swarmOperations.RegisterNode(registrationRequest, RequestRegistrationId, cancellationToken);
+
92 if (registrationResult == null)
+
93 return Conflict();
94
-
95 if (registrationRequest.ServerVersion != assemblyInformationProvider.Version)
-
96 return StatusCode((int)HttpStatusCode.UpgradeRequired);
+
95 if (registrationRequest.ServerVersion != swarmProtocolVersion)
+
96 logger.LogWarning("Allowed node {identifier} to register despite having a slightly different swarm protocol version!", registrationRequest.Identifier);
97
-
98 var registrationResult = await swarmOperations.RegisterNode(registrationRequest, RequestRegistrationId, cancellationToken);
-
99 if (!registrationResult)
-
100 return Conflict();
-
101 return NoContent();
-
102 }
+
98 return Json(registrationResult);
+
99 }
-
103
-
109 [HttpDelete(SwarmConstants.RegisterRoute)]
-
-
110 public async ValueTask<IActionResult> UnregisterNode(CancellationToken cancellationToken)
-
111 {
- -
113 return Forbid();
-
114
-
115 await swarmOperations.UnregisterNode(RequestRegistrationId, cancellationToken);
-
116 return NoContent();
-
117 }
+
100
+
106 [HttpDelete(SwarmConstants.RegisterRoute)]
+
+
107 public async ValueTask<IActionResult> UnregisterNode(CancellationToken cancellationToken)
+
108 {
+ +
110 return Forbid();
+
111
+
112 await swarmOperations.UnregisterNode(RequestRegistrationId, cancellationToken);
+
113 return NoContent();
+
114 }
-
118
-
123 [HttpGet]
-
-
124 public IActionResult HealthCheck()
-
125 {
- -
127 return Forbid();
-
128
-
129 return NoContent();
-
130 }
+
115
+
120 [HttpGet]
+
+
121 public IActionResult HealthCheck()
+
122 {
+ +
124 return Forbid();
+
125
+
126 return NoContent();
+
127 }
-
131
-
138 [HttpGet(SwarmConstants.UpdateRoute)]
-
139 [Produces(MediaTypeNames.Application.Octet)]
-
140 public ValueTask<IActionResult> GetUpdatePackage([FromQuery] string ticket, CancellationToken cancellationToken)
-
141 => transferService.GenerateDownloadResponse(this, ticket, cancellationToken);
-
142
-
148 [HttpPost]
-
-
149 public IActionResult UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)
-
150 {
-
151 ArgumentNullException.ThrowIfNull(serversUpdateRequest);
+
128
+
135 [HttpGet(SwarmConstants.UpdateRoute)]
+
136 [Produces(MediaTypeNames.Application.Octet)]
+
137 public ValueTask<IActionResult> GetUpdatePackage([FromQuery] string ticket, CancellationToken cancellationToken)
+
138 => transferService.GenerateDownloadResponse(this, ticket, cancellationToken);
+
139
+
145 [HttpPost]
+
+
146 public IActionResult UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)
+
147 {
+
148 ArgumentNullException.ThrowIfNull(serversUpdateRequest);
+
149
+
150 if (serversUpdateRequest.SwarmServers == null)
+
151 return BadRequest();
152
-
153 if (serversUpdateRequest.SwarmServers == null)
-
154 return BadRequest();
+ +
154 return Forbid();
155
- -
157 return Forbid();
-
158
-
159 swarmOperations.UpdateSwarmServersList(serversUpdateRequest.SwarmServers);
-
160 return NoContent();
-
161 }
+
156 swarmOperations.UpdateSwarmServersList(serversUpdateRequest.SwarmServers);
+
157 return NoContent();
+
158 }
-
162
-
169 [HttpPut(SwarmConstants.UpdateRoute)]
-
-
170 public async ValueTask<IActionResult> PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
-
171 {
-
172 ArgumentNullException.ThrowIfNull(updateRequest);
+
159
+
166 [HttpPut(SwarmConstants.UpdateRoute)]
+
+
167 public async ValueTask<IActionResult> PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
+
168 {
+
169 ArgumentNullException.ThrowIfNull(updateRequest);
+
170
+ +
172 return Forbid();
173
- -
175 return Forbid();
-
176
-
177 var prepareResult = await swarmOperations.PrepareUpdateFromController(updateRequest, cancellationToken);
-
178 if (!prepareResult)
-
179 return Conflict();
-
180
-
181 return NoContent();
-
182 }
+
174 var prepareResult = await swarmOperations.PrepareUpdateFromController(updateRequest, cancellationToken);
+
175 if (!prepareResult)
+
176 return Conflict();
+
177
+
178 return NoContent();
+
179 }
-
183
-
189 [HttpPost(SwarmConstants.UpdateRoute)]
-
-
190 public async ValueTask<IActionResult> CommitUpdate(CancellationToken cancellationToken)
-
191 {
- -
193 return Forbid();
-
194
-
195 var result = await swarmOperations.RemoteCommitReceived(RequestRegistrationId, cancellationToken);
-
196 if (!result)
-
197 return Conflict();
-
198 return NoContent();
-
199 }
+
180
+
186 [HttpPost(SwarmConstants.UpdateRoute)]
+
+
187 public async ValueTask<IActionResult> CommitUpdate(CancellationToken cancellationToken)
+
188 {
+ +
190 return Forbid();
+
191
+
192 var result = await swarmOperations.RemoteCommitReceived(RequestRegistrationId, cancellationToken);
+
193 if (!result)
+
194 return Conflict();
+
195 return NoContent();
+
196 }
-
200
-
205 [HttpDelete(SwarmConstants.UpdateRoute)]
-
-
206 public async ValueTask<IActionResult> AbortUpdate()
-
207 {
- -
209 return Forbid();
-
210
- -
212 return NoContent();
-
213 }
+
197
+
202 [HttpDelete(SwarmConstants.UpdateRoute)]
+
+
203 public async ValueTask<IActionResult> AbortUpdate()
+
204 {
+ +
206 return Forbid();
+
207
+ +
209 return NoContent();
+
210 }
-
214
-
-
216 protected override async ValueTask<IActionResult?> HookExecuteAction(Func<Task> executeAction, CancellationToken cancellationToken)
-
217 {
-
218 using (LogContext.PushProperty(SerilogContextHelper.RequestPathContextProperty, $"{Request.Method} {Request.Path}"))
-
219 {
-
220 logger.LogTrace("Swarm request from {remoteIP}...", Request.HttpContext.Connection.RemoteIpAddress);
-
221 if (swarmConfiguration.PrivateKey == null)
-
222 {
-
223 logger.LogDebug("Attempted swarm request without private key configured!");
-
224 return Forbid();
-
225 }
-
226
-
227 if (!(Request.Headers.TryGetValue(SwarmConstants.ApiKeyHeader, out var apiKeyHeaderValues)
-
228 && apiKeyHeaderValues.Count == 1
-
229 && apiKeyHeaderValues.First() == swarmConfiguration.PrivateKey))
-
230 {
-
231 logger.LogDebug("Unauthorized swarm request!");
-
232 return Unauthorized();
-
233 }
-
234
-
235 if (!(Request.Headers.TryGetValue(SwarmConstants.RegistrationIdHeader, out var registrationHeaderValues)
-
236 && registrationHeaderValues.Count == 1
-
237 && Guid.TryParse(registrationHeaderValues.First(), out var registrationId)))
-
238 {
-
239 logger.LogDebug("Swarm request without registration ID!");
-
240 return BadRequest();
-
241 }
-
242
-
243 // we validate the registration itself on a case-by-case basis
-
244 if (ModelState?.IsValid == false)
-
245 {
-
246 var errorMessages = ModelState
-
247 .SelectMany(x => x.Value!.Errors)
-
248 .Select(x => x.ErrorMessage);
-
249
-
250 logger.LogDebug(
-
251 "Swarm request model validation failed!{newLine}{messages}",
-
252 Environment.NewLine,
-
253 String.Join(Environment.NewLine, errorMessages));
-
254 return BadRequest();
-
255 }
-
256
-
257 logger.LogTrace("Starting swarm request processing...");
-
258 await executeAction();
-
259 return null;
-
260 }
-
261 }
+
211
+
+
213 protected override async ValueTask<IActionResult?> HookExecuteAction(Func<Task> executeAction, CancellationToken cancellationToken)
+
214 {
+
215 using (LogContext.PushProperty(SerilogContextHelper.RequestPathContextProperty, $"{Request.Method} {Request.Path}"))
+
216 {
+
217 logger.LogTrace("Swarm request from {remoteIP}...", Request.HttpContext.Connection.RemoteIpAddress);
+
218 if (swarmConfiguration.PrivateKey == null)
+
219 {
+
220 logger.LogDebug("Attempted swarm request without private key configured!");
+
221 return Forbid();
+
222 }
+
223
+
224 if (!(Request.Headers.TryGetValue(SwarmConstants.ApiKeyHeader, out var apiKeyHeaderValues)
+
225 && apiKeyHeaderValues.Count == 1
+
226 && apiKeyHeaderValues.First() == swarmConfiguration.PrivateKey))
+
227 {
+
228 logger.LogDebug("Unauthorized swarm request!");
+
229 return Unauthorized();
+
230 }
+
231
+
232 if (!(Request.Headers.TryGetValue(SwarmConstants.RegistrationIdHeader, out var registrationHeaderValues)
+
233 && registrationHeaderValues.Count == 1
+
234 && Guid.TryParse(registrationHeaderValues.First(), out var registrationId)))
+
235 {
+
236 logger.LogDebug("Swarm request without registration ID!");
+
237 return BadRequest();
+
238 }
+
239
+
240 // we validate the registration itself on a case-by-case basis
+
241 if (ModelState?.IsValid == false)
+
242 {
+
243 var errorMessages = ModelState
+
244 .SelectMany(x => x.Value!.Errors)
+
245 .Select(x => x.ErrorMessage);
+
246
+
247 logger.LogDebug(
+
248 "Swarm request model validation failed!{newLine}{messages}",
+
249 Environment.NewLine,
+
250 String.Join(Environment.NewLine, errorMessages));
+
251 return BadRequest();
+
252 }
+
253
+
254 logger.LogTrace("Starting swarm request processing...");
+
255 await executeAction();
+
256 return null;
+
257 }
+
258 }
-
262
- -
268 }
+
259
+ +
265 }
-
269}
+
266}
Configuration for the server swarm system.
string? PrivateKey
The private key used for swarm communication.
Base class for all API style controllers.
@@ -303,19 +304,21 @@
readonly ISwarmOperations swarmOperations
The ISwarmOperations for the SwarmController.
bool ValidateRegistration()
Check that the RequestRegistrationId is valid.
ValueTask< IActionResult > GetUpdatePackage([FromQuery] string ticket, CancellationToken cancellationToken)
Endpoint to retrieve server update packages.
-
async ValueTask< IActionResult > Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)
Registration endpoint.
-
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SwarmController.
-
IActionResult HealthCheck()
Health check endpoint.
-
override async ValueTask< IActionResult?> HookExecuteAction(Func< Task > executeAction, CancellationToken cancellationToken)
Hook for executing a request.A ValueTask<TResult> resulting in an IActionResult that,...
-
IActionResult UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)
Node list update endpoint.
-
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the SwarmController.
-
readonly ILogger< SwarmController > logger
The ILogger for the SwarmController.
-
async ValueTask< IActionResult > PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Update initiation endpoint.
-
SwarmController(ISwarmOperations swarmOperations, IAssemblyInformationProvider assemblyInformationProvider, IFileTransferStreamHandler transferService, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmController > logger)
Initializes a new instance of the SwarmController class.
-
async ValueTask< IActionResult > AbortUpdate()
Update abort endpoint.
+
async ValueTask< IActionResult > Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)
Registration endpoint.
+
IActionResult HealthCheck()
Health check endpoint.
+
override async ValueTask< IActionResult?> HookExecuteAction(Func< Task > executeAction, CancellationToken cancellationToken)
Hook for executing a request.A ValueTask<TResult> resulting in an IActionResult that,...
+
IActionResult UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)
Node list update endpoint.
+
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the SwarmController.
+
SwarmController(ISwarmOperations swarmOperations, IFileTransferStreamHandler transferService, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmController > logger)
Initializes a new instance of the SwarmController class.
+
readonly ILogger< SwarmController > logger
The ILogger for the SwarmController.
+
async ValueTask< IActionResult > PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Update initiation endpoint.
+
async ValueTask< IActionResult > AbortUpdate()
Update abort endpoint.
readonly IFileTransferStreamHandler transferService
The IFileTransferStreamHandler for the SwarmController.
-
async ValueTask< IActionResult > CommitUpdate(CancellationToken cancellationToken)
Update commit endpoint.
-
async ValueTask< IActionResult > UnregisterNode(CancellationToken cancellationToken)
Deregistration endpoint.
+
async ValueTask< IActionResult > CommitUpdate(CancellationToken cancellationToken)
Update commit endpoint.
+
async ValueTask< IActionResult > UnregisterNode(CancellationToken cancellationToken)
Deregistration endpoint.
+
Attribute for bringing in the master versions list from MSBuild that aren't embedded into assemblies ...
+
string RawSwarmProtocolVersion
The Version string of the MariaDB server bundled with TGS installs.
+
static MasterVersionsAttribute Instance
Return the Assembly's instance of the MasterVersionsAttribute.
Constants used by the swarm system.
const string ApiKeyHeader
The header used to pass in the Configuration.SwarmConfiguration.PrivateKey.
const string UpdateRoute
The route used for swarm updates.
@@ -330,20 +333,18 @@
Swarm service operations for the Controllers.SwarmController.
ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
Attempt to unregister a node with a given registrationId with the controller.
ValueTask< bool > RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
Notify the controller that the node with the given registrationId is ready to commit or notify the n...
-
ValueTask< bool > RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.
ValueTask< bool > PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Notify the node of an update request from the controller.
bool ValidateRegistration(Guid registrationId)
Validate a given registrationId .
+
ValueTask< SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.
void UpdateSwarmServersList(IEnumerable< SwarmServerInformation > swarmServers)
Pass in an updated list of swarmServers to the node.
ValueTask AbortUpdate()
Attempt to abort an uncommitted update.
- -
Reads and writes to Streams associated with FileTicketResponses.
+
@ Unauthorized
The swarm private keys didn't match.
-
diff --git a/_swarm_registration_request_8cs_source.html b/_swarm_registration_request_8cs_source.html index 6ba514ae4e..f7eac89747 100644 --- a/_swarm_registration_request_8cs_source.html +++ b/_swarm_registration_request_8cs_source.html @@ -107,7 +107,7 @@
Information about a server in the swarm.
A request to register with a swarm controller.
SwarmRegistrationRequest(Version serverVersion)
Initializes a new instance of the SwarmRegistrationRequest class.
-
Version ServerVersion
The TGS Version of the sending server.
+
Version ServerVersion
The swarm protocol Version of the sending server. Named this way due to legacy reasons.
diff --git a/_swarm_registration_response_8cs.html b/_swarm_registration_response_8cs.html new file mode 100644 index 0000000000..eeed219fe5 --- /dev/null +++ b/_swarm_registration_response_8cs.html @@ -0,0 +1,109 @@ + + + + + + + +tgstation-server: src/Tgstation.Server.Host/Swarm/SwarmRegistrationResponse.cs File Reference + + + + + + + + + +
+
+ + + + + + +
+
tgstation-server 6.10.0 +
+
The /tg/station 13 server suite
+
+
+ + + + + + + + +
+
+ + +
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+ + +
+
+ +
SwarmRegistrationResponse.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Swarm.SwarmRegistrationResponse
 Response for a SwarmRegistrationRequest. More...
 
+ + + + + + + + + +

+Namespaces

namespace  Tgstation
 
namespace  Tgstation.Server
 
namespace  Tgstation.Server.Host
 
namespace  Tgstation.Server.Host.Swarm
 
+
+ + + + diff --git a/_swarm_registration_response_8cs_source.html b/_swarm_registration_response_8cs_source.html new file mode 100644 index 0000000000..f68f8de3f0 --- /dev/null +++ b/_swarm_registration_response_8cs_source.html @@ -0,0 +1,103 @@ + + + + + + + +tgstation-server: src/Tgstation.Server.Host/Swarm/SwarmRegistrationResponse.cs Source File + + + + + + + + + +
+
+ + + + + + +
+
tgstation-server 6.10.0 +
+
The /tg/station 13 server suite
+
+
+ + + + + + + + + +
+
+ + +
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+ + +
+
+
SwarmRegistrationResponse.cs
+
+
+Go to the documentation of this file.
+
2{
+
+
6 public sealed class SwarmRegistrationResponse
+
7 {
+
11 public required string TokenSigningKeyBase64 { get; init; }
+
12 }
+
+
13}
+ +
required string TokenSigningKeyBase64
The base64 encoded token signing key.
+ +
+ + + + diff --git a/_swarm_registration_result_8cs.html b/_swarm_registration_result_8cs.html index 5f02235f3f..27b7a25e0b 100644 --- a/_swarm_registration_result_8cs.html +++ b/_swarm_registration_result_8cs.html @@ -96,10 +96,14 @@ - diff --git a/_swarm_registration_result_8cs_source.html b/_swarm_registration_result_8cs_source.html index 950fc0b0ce..872749acb7 100644 --- a/_swarm_registration_result_8cs_source.html +++ b/_swarm_registration_result_8cs_source.html @@ -94,11 +94,14 @@
22
-
27 }
+
27
+ +
32 }
-
28}
+
33}
SwarmRegistrationResult
Result of attempting to register with a swarm controller.
+
@ PayloadFailure
Response could not be deserialized.
@ CommunicationFailure
A communication error occurred.
@ VersionMismatch
The swarm controller is running a different server version.
diff --git a/_swarm_service_8cs_source.html b/_swarm_service_8cs_source.html index bce02c14ed..08a22997e0 100644 --- a/_swarm_service_8cs_source.html +++ b/_swarm_service_8cs_source.html @@ -108,1415 +108,1458 @@ - - - -
30
- -
32{
-
- -
37 {
+ + + + + +
32
+ +
34{
+
38#pragma warning disable CA1506 // TODO: Decomplexify
- -
40 {
-
41 get
-
42 {
-
43 if (!SwarmMode)
-
44 return true;
-
45
-
46 lock (swarmServers)
-
47 return swarmServers.Count - 1 >= swarmConfiguration.UpdateRequiredNodeCount;
-
48 }
-
49 }
+ +
40#pragma warning restore CA1506
+
41 {
+
+ +
44 {
+
45 get
+
46 {
+
47 if (!SwarmMode)
+
48 return true;
+
49
+
50 lock (swarmServers)
+
51 return swarmServers.Count - 1 >= swarmConfiguration.UpdateRequiredNodeCount;
+
52 }
+
53 }
-
50
-
54 [MemberNotNullWhen(true, nameof(serverHealthCheckTask), nameof(forceHealthCheckTcs), nameof(serverHealthCheckCancellationTokenSource), nameof(swarmServers))]
-
55 bool SwarmMode => swarmConfiguration.PrivateKey != null;
-
56
- -
61
- -
66
- -
71
- -
76
- -
81
- -
86
- -
91
-
95 readonly ILogger<SwarmService> logger;
-
96
- -
101
-
105 readonly CancellationTokenSource? serverHealthCheckCancellationTokenSource;
-
106
-
110 readonly List<SwarmServerInformation>? swarmServers;
-
111
-
115 readonly Dictionary<string, (Guid RegistrationId, DateTimeOffset RegisteredAt)>? registrationIdsAndTimes;
-
116
-
120 readonly bool swarmController;
-
121
- -
126
-
130 volatile TaskCompletionSource? forceHealthCheckTcs;
-
131
- -
136
- -
141
- -
146
- -
151
-
- - - - - - - - -
172 IOptions<SwarmConfiguration> swarmConfigurationOptions,
-
173 ILogger<SwarmService> logger)
-
174 {
-
175 this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
-
176 this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
-
177 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
-
178 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
-
179 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
-
180 this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
-
181 this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
-
182 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
-
183 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
-
184
-
185 if (SwarmMode)
-
186 {
-
187 if (swarmConfiguration.Address == null)
-
188 throw new InvalidOperationException("Swarm configuration missing Address!");
-
189
-
190 if (String.IsNullOrWhiteSpace(swarmConfiguration.Identifier))
-
191 throw new InvalidOperationException("Swarm configuration missing Identifier!");
-
192
-
193 swarmController = swarmConfiguration.ControllerAddress == null;
-
194 if (swarmController)
- +
54
+
58 [MemberNotNullWhen(true, nameof(serverHealthCheckTask), nameof(forceHealthCheckTcs), nameof(serverHealthCheckCancellationTokenSource), nameof(swarmServers))]
+
59 bool SwarmMode => swarmConfiguration.PrivateKey != null;
+
60
+ +
65
+ +
70
+ +
75
+ +
80
+ +
85
+ +
90
+ +
95
+ +
100
+
104 readonly ILogger<SwarmService> logger;
+
105
+ +
110
+
114 readonly CancellationTokenSource? serverHealthCheckCancellationTokenSource;
+
115
+
119 readonly List<SwarmServerInformation>? swarmServers;
+
120
+
124 readonly Dictionary<string, (Guid RegistrationId, DateTimeOffset RegisteredAt)>? registrationIdsAndTimes;
+
125
+
129 readonly bool swarmController;
+
130
+ +
135
+
139 volatile TaskCompletionSource? forceHealthCheckTcs;
+
140
+ +
145
+ +
150
+ +
155
+ +
160
+
+ + + + + + + + + +
183 IOptions<SwarmConfiguration> swarmConfigurationOptions,
+
184 ILogger<SwarmService> logger)
+
185 {
+
186 this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
+
187 this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
+
188 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
+
189 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
+
190 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
+
191 this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
+
192 this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
+
193 this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
+
194 swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
+
195 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
196
-
197 serverHealthCheckCancellationTokenSource = new CancellationTokenSource();
-
198 forceHealthCheckTcs = new TaskCompletionSource();
-
199
-
200 swarmServers = new List<SwarmServerInformation>
-
201 {
- -
203 {
-
204 Address = swarmConfiguration.Address,
-
205 PublicAddress = swarmConfiguration.PublicAddress,
- -
207 Identifier = swarmConfiguration.Identifier,
-
208 },
-
209 };
-
210 }
-
211 else
-
212 swarmController = true;
-
213 }
+
197 if (SwarmMode)
+
198 {
+
199 if (swarmConfiguration.Address == null)
+
200 throw new InvalidOperationException("Swarm configuration missing Address!");
+
201
+
202 if (String.IsNullOrWhiteSpace(swarmConfiguration.Identifier))
+
203 throw new InvalidOperationException("Swarm configuration missing Identifier!");
+
204
+
205 swarmController = swarmConfiguration.ControllerAddress == null;
+
206 if (swarmController)
+ +
208
+
209 serverHealthCheckCancellationTokenSource = new CancellationTokenSource();
+
210 forceHealthCheckTcs = new TaskCompletionSource();
+
211
+
212 swarmServers = new List<SwarmServerInformation>
+
213 {
+ +
215 {
+
216 Address = swarmConfiguration.Address,
+
217 PublicAddress = swarmConfiguration.PublicAddress,
+ +
219 Identifier = swarmConfiguration.Identifier,
+
220 },
+
221 };
+
222 }
+
223 else
+
224 swarmController = true;
+
225 }
-
214
- -
217
-
-
219 public async ValueTask AbortUpdate()
-
220 {
-
221 if (!SwarmMode)
-
222 return;
-
223
-
224 var localUpdateOperation = Interlocked.Exchange(ref updateOperation, null);
-
225 var abortResult = localUpdateOperation?.Abort();
-
226 switch (abortResult)
-
227 {
-
228 case SwarmUpdateAbortResult.Aborted:
-
229 break;
-
230 case SwarmUpdateAbortResult.AlreadyAborted:
-
231 logger.LogDebug("Another context already aborted this update.");
-
232 return;
-
233 case SwarmUpdateAbortResult.CantAbortCommitted:
-
234 logger.LogDebug("Not aborting update because we have committed!");
-
235 return;
-
236 case null:
-
237 logger.LogTrace("Attempted update abort but no operation was found!");
-
238 return;
-
239 default:
-
240 throw new InvalidOperationException($"Invalid return value for SwarmUpdateOperation.Abort(): {abortResult}");
-
241 }
-
242
-
243 await RemoteAbortUpdate();
-
244 }
+
226
+ +
229
+
+
231 public async ValueTask AbortUpdate()
+
232 {
+
233 if (!SwarmMode)
+
234 return;
+
235
+
236 var localUpdateOperation = Interlocked.Exchange(ref updateOperation, null);
+
237 var abortResult = localUpdateOperation?.Abort();
+
238 switch (abortResult)
+
239 {
+
240 case SwarmUpdateAbortResult.Aborted:
+
241 break;
+
242 case SwarmUpdateAbortResult.AlreadyAborted:
+
243 logger.LogDebug("Another context already aborted this update.");
+
244 return;
+
245 case SwarmUpdateAbortResult.CantAbortCommitted:
+
246 logger.LogDebug("Not aborting update because we have committed!");
+
247 return;
+
248 case null:
+
249 logger.LogTrace("Attempted update abort but no operation was found!");
+
250 return;
+
251 default:
+
252 throw new InvalidOperationException($"Invalid return value for SwarmUpdateOperation.Abort(): {abortResult}");
+
253 }
+
254
+
255 await RemoteAbortUpdate();
+
256 }
-
245
-
-
247 public async ValueTask<SwarmCommitResult> CommitUpdate(CancellationToken cancellationToken)
-
248 {
-
249 if (!SwarmMode)
-
250 return SwarmCommitResult.ContinueUpdateNonCommitted;
-
251
-
252 // wait for the update commit TCS
-
253 var localUpdateOperation = updateOperation;
-
254 if (localUpdateOperation == null)
-
255 {
-
256 logger.LogDebug("Update commit failed, no pending operation!");
-
257 await AbortUpdate(); // unnecessary, but can never be too safe
-
258 return SwarmCommitResult.AbortUpdate;
-
259 }
-
260
-
261 logger.LogInformation("Waiting to commit update...");
-
262 using var httpClient = httpClientFactory.CreateClient();
-
263 if (!swarmController)
-
264 {
-
265 // let the controller know we're ready
-
266 logger.LogTrace("Sending ready-commit to swarm controller...");
-
267 using var commitReadyRequest = PrepareSwarmRequest(
-
268 null,
-
269 HttpMethod.Post,
- -
271 null);
+
257
+
+
259 public async ValueTask<SwarmCommitResult> CommitUpdate(CancellationToken cancellationToken)
+
260 {
+
261 if (!SwarmMode)
+
262 return SwarmCommitResult.ContinueUpdateNonCommitted;
+
263
+
264 // wait for the update commit TCS
+
265 var localUpdateOperation = updateOperation;
+
266 if (localUpdateOperation == null)
+
267 {
+
268 logger.LogDebug("Update commit failed, no pending operation!");
+
269 await AbortUpdate(); // unnecessary, but can never be too safe
+
270 return SwarmCommitResult.AbortUpdate;
+
271 }
272
-
273 try
-
274 {
-
275 using var commitReadyResponse = await httpClient.SendAsync(commitReadyRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
276 commitReadyResponse.EnsureSuccessStatusCode();
-
277 }
-
278 catch (Exception ex)
-
279 {
-
280 logger.LogWarning(ex, "Unable to send ready-commit to swarm controller!");
-
281 await AbortUpdate();
-
282 return SwarmCommitResult.AbortUpdate;
-
283 }
-
284 }
-
285
-
286 var timeoutTask = swarmController
- -
288 TimeSpan.FromMinutes(SwarmConstants.UpdateCommitTimeoutMinutes),
-
289 cancellationToken)
-
290 : Extensions.TaskExtensions.InfiniteTask.WaitAsync(cancellationToken);
-
291
-
292 var commitTask = Task.WhenAny(localUpdateOperation.CommitGate, timeoutTask);
-
293
-
294 await commitTask;
-
295
-
296 var commitGoAhead = localUpdateOperation.CommitGate.IsCompleted
-
297 && localUpdateOperation.CommitGate.Result
-
298 && localUpdateOperation == updateOperation;
-
299 if (!commitGoAhead)
-
300 {
-
301 logger.LogDebug(
-
302 "Update commit failed!{maybeTimeout}",
-
303 timeoutTask.IsCompleted
-
304 ? " Timed out!"
-
305 : String.Empty);
-
306 await AbortUpdate();
-
307 return SwarmCommitResult.AbortUpdate;
-
308 }
-
309
-
310 logger.LogTrace("Update commit task complete");
-
311
-
312 // on nodes, it means we can go straight ahead
-
313 if (!swarmController)
-
314 return SwarmCommitResult.MustCommitUpdate;
-
315
-
316 // on the controller, we first need to signal for nodes to go ahead
-
317 // if anything fails at this point, there's nothing we can do
-
318 logger.LogDebug("Sending remote commit message to nodes...");
-
319 async ValueTask SendRemoteCommitUpdate(SwarmServerInformation swarmServer)
-
320 {
-
321 using var request = PrepareSwarmRequest(
-
322 swarmServer,
-
323 HttpMethod.Post,
- -
325 null);
-
326
-
327 try
-
328 {
-
329 // I know using the cancellationToken after this point doesn't seem very sane
-
330 // It's the token for Ctrl+C on server's console though, so we must respect it
-
331 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
332 response.EnsureSuccessStatusCode();
-
333 }
-
334 catch (Exception ex)
-
335 {
-
336 logger.LogCritical(ex, "Failed to send update commit request to node {nodeId}!", swarmServer.Identifier);
-
337 }
-
338 }
-
339
-
340 ValueTask task;
-
341 lock (swarmServers)
- - -
344 .Where(x => !x.Controller)
-
345 .Select(SendRemoteCommitUpdate)
-
346 .ToList());
-
347
-
348 await task;
-
349 return SwarmCommitResult.MustCommitUpdate;
-
350 }
-
+
273 logger.LogInformation("Waiting to commit update...");
+
274 using var httpClient = httpClientFactory.CreateClient();
+
275 if (!swarmController)
+
276 {
+
277 // let the controller know we're ready
+
278 logger.LogTrace("Sending ready-commit to swarm controller...");
+
279 using var commitReadyRequest = PrepareSwarmRequest(
+
280 null,
+
281 HttpMethod.Post,
+ +
283 null);
+
284
+
285 try
+
286 {
+
287 using var commitReadyResponse = await httpClient.SendAsync(commitReadyRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
288 commitReadyResponse.EnsureSuccessStatusCode();
+
289 }
+
290 catch (Exception ex)
+
291 {
+
292 logger.LogWarning(ex, "Unable to send ready-commit to swarm controller!");
+
293 await AbortUpdate();
+
294 return SwarmCommitResult.AbortUpdate;
+
295 }
+
296 }
+
297
+
298 var timeoutTask = swarmController
+ +
300 TimeSpan.FromMinutes(SwarmConstants.UpdateCommitTimeoutMinutes),
+
301 cancellationToken)
+
302 : Extensions.TaskExtensions.InfiniteTask.WaitAsync(cancellationToken);
+
303
+
304 var commitTask = Task.WhenAny(localUpdateOperation.CommitGate, timeoutTask);
+
305
+
306 await commitTask;
+
307
+
308 var commitGoAhead = localUpdateOperation.CommitGate.IsCompleted
+
309 && localUpdateOperation.CommitGate.Result
+
310 && localUpdateOperation == updateOperation;
+
311 if (!commitGoAhead)
+
312 {
+
313 logger.LogDebug(
+
314 "Update commit failed!{maybeTimeout}",
+
315 timeoutTask.IsCompleted
+
316 ? " Timed out!"
+
317 : String.Empty);
+
318 await AbortUpdate();
+
319 return SwarmCommitResult.AbortUpdate;
+
320 }
+
321
+
322 logger.LogTrace("Update commit task complete");
+
323
+
324 // on nodes, it means we can go straight ahead
+
325 if (!swarmController)
+
326 return SwarmCommitResult.MustCommitUpdate;
+
327
+
328 // on the controller, we first need to signal for nodes to go ahead
+
329 // if anything fails at this point, there's nothing we can do
+
330 logger.LogDebug("Sending remote commit message to nodes...");
+
331 async ValueTask SendRemoteCommitUpdate(SwarmServerInformation swarmServer)
+
332 {
+
333 using var request = PrepareSwarmRequest(
+
334 swarmServer,
+
335 HttpMethod.Post,
+ +
337 null);
+
338
+
339 try
+
340 {
+
341 // I know using the cancellationToken after this point doesn't seem very sane
+
342 // It's the token for Ctrl+C on server's console though, so we must respect it
+
343 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
344 response.EnsureSuccessStatusCode();
+
345 }
+
346 catch (Exception ex)
+
347 {
+
348 logger.LogCritical(ex, "Failed to send update commit request to node {nodeId}!", swarmServer.Identifier);
+
349 }
+
350 }
351
-
-
353 public List<SwarmServerInformation>? GetSwarmServers()
-
354 {
-
355 if (!SwarmMode)
-
356 return null;
-
357
-
358 lock (swarmServers)
-
359 return swarmServers.ToList();
-
360 }
+
352 ValueTask task;
+
353 lock (swarmServers)
+ + +
356 .Where(x => !x.Controller)
+
357 .Select(SendRemoteCommitUpdate)
+
358 .ToList());
+
359
+
360 await task;
+
361 return SwarmCommitResult.MustCommitUpdate;
+
362 }
-
361
-
-
363 public ValueTask<SwarmPrepareResult> PrepareUpdate(ISeekableFileStreamProvider fileStreamProvider, Version version, CancellationToken cancellationToken)
-
364 {
-
365 ArgumentNullException.ThrowIfNull(fileStreamProvider);
-
366
-
367 ArgumentNullException.ThrowIfNull(version);
-
368
-
369 logger.LogTrace("Begin PrepareUpdate...");
-
370 return PrepareUpdateImpl(
-
371 fileStreamProvider,
- -
373 {
-
374 UpdateVersion = version,
-
375 },
-
376 cancellationToken);
-
377 }
+
363
+
+
365 public List<SwarmServerInformation>? GetSwarmServers()
+
366 {
+
367 if (!SwarmMode)
+
368 return null;
+
369
+
370 lock (swarmServers)
+
371 return swarmServers.ToList();
+
372 }
+
373
+
+
375 public ValueTask<SwarmPrepareResult> PrepareUpdate(ISeekableFileStreamProvider fileStreamProvider, Version version, CancellationToken cancellationToken)
+
376 {
+
377 ArgumentNullException.ThrowIfNull(fileStreamProvider);
378
-
-
380 public async ValueTask<bool> PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
-
381 {
-
382 ArgumentNullException.ThrowIfNull(updateRequest);
-
383
-
384 logger.LogInformation("Received remote update request from {nodeType}", !swarmController ? "controller" : "node");
-
385 var result = await PrepareUpdateImpl(
-
386 null,
-
387 updateRequest,
+
379 ArgumentNullException.ThrowIfNull(version);
+
380
+
381 logger.LogTrace("Begin PrepareUpdate...");
+
382 return PrepareUpdateImpl(
+
383 fileStreamProvider,
+ +
385 {
+
386 UpdateVersion = version,
+
387 },
388 cancellationToken);
-
389
-
390 return result != SwarmPrepareResult.Failure;
-
391 }
+
389 }
-
392
-
-
394 public async ValueTask<SwarmRegistrationResult> Initialize(CancellationToken cancellationToken)
-
395 {
-
396 if (SwarmMode)
-
397 logger.LogInformation(
-
398 "Swarm mode enabled: {nodeType} {nodeId}",
- -
400 ? "Controller"
-
401 : "Node",
- -
403 else
-
404 logger.LogTrace("Swarm mode disabled");
-
405
- -
407 if (swarmController)
-
408 {
- -
410 logger.LogInformation("Expecting connections from {expectedNodeCount} nodes", swarmConfiguration.UpdateRequiredNodeCount);
-
411
- -
413 databaseContext => databaseSeeder.Initialize(databaseContext, cancellationToken));
-
414
-
415 result = SwarmRegistrationResult.Success;
-
416 }
-
417 else
-
418 result = await RegisterWithController(cancellationToken);
-
419
-
420 if (SwarmMode && result == SwarmRegistrationResult.Success)
- -
422
-
423 return result;
-
424 }
+
390
+
+
392 public async ValueTask<bool> PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
+
393 {
+
394 ArgumentNullException.ThrowIfNull(updateRequest);
+
395
+
396 logger.LogInformation("Received remote update request from {nodeType}", !swarmController ? "controller" : "node");
+
397 var result = await PrepareUpdateImpl(
+
398 null,
+
399 updateRequest,
+
400 cancellationToken);
+
401
+
402 return result != SwarmPrepareResult.Failure;
+
403 }
-
425
-
-
427 public async ValueTask Shutdown(CancellationToken cancellationToken)
-
428 {
-
429 logger.LogTrace("Begin Shutdown");
-
430
-
431 async ValueTask SendUnregistrationRequest(SwarmServerInformation? swarmServer)
-
432 {
-
433 using var httpClient = httpClientFactory.CreateClient();
-
434 using var request = PrepareSwarmRequest(
-
435 swarmServer,
-
436 HttpMethod.Delete,
- -
438 null);
-
439
-
440 try
-
441 {
-
442 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
443 response.EnsureSuccessStatusCode();
-
444 }
-
445 catch (Exception ex)
-
446 {
-
447 logger.LogWarning(
-
448 ex,
-
449 "Error unregistering {nodeType}!",
-
450 swarmServer != null
-
451 ? $"node {swarmServer.Identifier}"
-
452 : "from controller");
-
453 }
-
454 }
-
455
-
456 if (SwarmMode && serverHealthCheckTask != null)
-
457 {
- - -
460 }
-
461
-
462 if (!swarmController)
-
463 {
-
464 if (controllerRegistration != null)
-
465 {
-
466 logger.LogInformation("Unregistering from swarm controller...");
-
467 await SendUnregistrationRequest(null);
-
468 }
-
469
-
470 return;
-
471 }
-
472
-
473 // We're in a single-threaded-like context now so touching updateOperation directly is fine
-
474
-
475 // downgrade the db if necessary
-
476 if (updateOperation != null
- - -
479 db => databaseSeeder.Downgrade(db, updateOperation.TargetVersion, cancellationToken));
-
480
-
481 if (SwarmMode)
-
482 {
-
483 // Put the nodes into a reconnecting state
-
484 if (updateOperation == null)
-
485 {
-
486 logger.LogInformation("Unregistering nodes...");
-
487 ValueTask task;
-
488 lock (swarmServers)
-
489 {
- - -
492 .Where(x => !x.Controller)
-
493 .Select(SendUnregistrationRequest)
-
494 .ToList());
-
495 swarmServers.RemoveRange(1, swarmServers.Count - 1);
- -
497 }
-
498
-
499 await task;
-
500 }
-
501
-
502 logger.LogTrace("Swarm controller shutdown");
-
503 }
-
504 }
+
404
+
+
406 public async ValueTask<SwarmRegistrationResult> Initialize(CancellationToken cancellationToken)
+
407 {
+
408 if (SwarmMode)
+
409 logger.LogInformation(
+
410 "Swarm mode enabled: {nodeType} {nodeId}",
+ +
412 ? "Controller"
+
413 : "Node",
+ +
415 else
+
416 logger.LogTrace("Swarm mode disabled");
+
417
+ +
419 if (swarmController)
+
420 {
+ +
422 logger.LogInformation("Expecting connections from {expectedNodeCount} nodes", swarmConfiguration.UpdateRequiredNodeCount);
+
423
+ +
425 databaseContext => databaseSeeder.Initialize(databaseContext, cancellationToken));
+
426
+
427 result = SwarmRegistrationResult.Success;
+
428 }
+
429 else
+
430 result = await RegisterWithController(cancellationToken);
+
431
+
432 if (SwarmMode && result == SwarmRegistrationResult.Success)
+ +
434
+
435 return result;
+
436 }
-
505
-
-
507 public void UpdateSwarmServersList(IEnumerable<SwarmServerInformation> swarmServers)
-
508 {
-
509 ArgumentNullException.ThrowIfNull(swarmServers);
+
437
+
+
439 public async ValueTask Shutdown(CancellationToken cancellationToken)
+
440 {
+
441 logger.LogTrace("Begin Shutdown");
+
442
+
443 async ValueTask SendUnregistrationRequest(SwarmServerInformation? swarmServer)
+
444 {
+
445 using var httpClient = httpClientFactory.CreateClient();
+
446 using var request = PrepareSwarmRequest(
+
447 swarmServer,
+
448 HttpMethod.Delete,
+ +
450 null);
+
451
+
452 try
+
453 {
+
454 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
455 response.EnsureSuccessStatusCode();
+
456 }
+
457 catch (Exception ex)
+
458 {
+
459 logger.LogWarning(
+
460 ex,
+
461 "Error unregistering {nodeType}!",
+
462 swarmServer != null
+
463 ? $"node {swarmServer.Identifier}"
+
464 : "from controller");
+
465 }
+
466 }
+
467
+
468 if (SwarmMode && serverHealthCheckTask != null)
+
469 {
+ + +
472 }
+
473
+
474 if (!swarmController)
+
475 {
+
476 if (controllerRegistration != null)
+
477 {
+
478 logger.LogInformation("Unregistering from swarm controller...");
+
479 await SendUnregistrationRequest(null);
+
480 }
+
481
+
482 return;
+
483 }
+
484
+
485 // We're in a single-threaded-like context now so touching updateOperation directly is fine
+
486
+
487 // downgrade the db if necessary
+
488 if (updateOperation != null
+ + +
491 db => databaseSeeder.Downgrade(db, updateOperation.TargetVersion, cancellationToken));
+
492
+
493 if (SwarmMode)
+
494 {
+
495 // Put the nodes into a reconnecting state
+
496 if (updateOperation == null)
+
497 {
+
498 logger.LogInformation("Unregistering nodes...");
+
499 ValueTask task;
+
500 lock (swarmServers)
+
501 {
+ + +
504 .Where(x => !x.Controller)
+
505 .Select(SendUnregistrationRequest)
+
506 .ToList());
+
507 swarmServers.RemoveRange(1, swarmServers.Count - 1);
+ +
509 }
510
-
511 if (!SwarmMode)
-
512 throw new InvalidOperationException("Swarm mode not enabled!");
+
511 await task;
+
512 }
513
-
514 if (swarmController)
-
515 throw new InvalidOperationException("Cannot UpdateSwarmServersList on swarm controller!");
-
516
-
517 lock (this.swarmServers)
-
518 {
-
519 this.swarmServers.Clear();
-
520 this.swarmServers.AddRange(swarmServers);
-
521 logger.LogDebug("Updated swarm server list with {nodeCount} total nodes", this.swarmServers.Count);
-
522 }
-
523 }
+
514 logger.LogTrace("Swarm controller shutdown");
+
515 }
+
516 }
-
524
-
-
526 public bool ValidateRegistration(Guid registrationId)
-
527 {
-
528 if (!SwarmMode)
-
529 throw new InvalidOperationException("Swarm mode not enabled!");
-
530
-
531 if (swarmController)
-
532 lock (swarmServers)
-
533 return registrationIdsAndTimes!.Values.Any(x => x.RegistrationId == registrationId);
-
534
-
535 if (registrationId != controllerRegistration)
-
536 return false;
-
537
-
538 lastControllerHealthCheck = DateTimeOffset.UtcNow;
-
539 return true;
-
540 }
+
517
+
+
519 public void UpdateSwarmServersList(IEnumerable<SwarmServerInformation> swarmServers)
+
520 {
+
521 ArgumentNullException.ThrowIfNull(swarmServers);
+
522
+
523 if (!SwarmMode)
+
524 throw new InvalidOperationException("Swarm mode not enabled!");
+
525
+
526 if (swarmController)
+
527 throw new InvalidOperationException("Cannot UpdateSwarmServersList on swarm controller!");
+
528
+
529 lock (this.swarmServers)
+
530 {
+
531 this.swarmServers.Clear();
+
532 this.swarmServers.AddRange(swarmServers);
+
533 logger.LogDebug("Updated swarm server list with {nodeCount} total nodes", this.swarmServers.Count);
+
534 }
+
535 }
-
541
-
-
543 public async ValueTask<bool> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
-
544 {
-
545 ArgumentNullException.ThrowIfNull(node);
+
536
+
+
538 public bool ValidateRegistration(Guid registrationId)
+
539 {
+
540 if (!SwarmMode)
+
541 throw new InvalidOperationException("Swarm mode not enabled!");
+
542
+
543 if (swarmController)
+
544 lock (swarmServers)
+
545 return registrationIdsAndTimes!.Values.Any(x => x.RegistrationId == registrationId);
546
-
547 if (node.Identifier == null)
-
548 throw new ArgumentException("Node missing Identifier!", nameof(node));
+
547 if (registrationId != controllerRegistration)
+
548 return false;
549
-
550 if (node.Address == null)
-
551 throw new ArgumentException("Node missing Address!", nameof(node));
-
552
-
553 if (!SwarmMode)
-
554 throw new InvalidOperationException("Swarm mode not enabled!");
-
555
-
556 if (!swarmController)
-
557 throw new InvalidOperationException("Cannot RegisterNode on swarm node!");
+
550 lastControllerHealthCheck = DateTimeOffset.UtcNow;
+
551 return true;
+
552 }
+
+
553
+
+
555 public async ValueTask<SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
+
556 {
+
557 ArgumentNullException.ThrowIfNull(node);
558
-
559 logger.LogTrace("RegisterNode");
-
560
-
561 await AbortUpdate();
-
562
-
563 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
-
564 lock (swarmServers)
-
565 {
-
566 if (registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId))
-
567 {
-
568 var preExistingRegistrationKvp = registrationIdsAndTimes.FirstOrDefault(x => x.Value.RegistrationId == registrationId);
-
569 if (preExistingRegistrationKvp.Key == node.Identifier)
-
570 {
-
571 logger.LogWarning("Node {nodeId} has already registered!", node.Identifier);
-
572 return true;
-
573 }
+
559 if (node.Identifier == null)
+
560 throw new ArgumentException("Node missing Identifier!", nameof(node));
+
561
+
562 if (node.Address == null)
+
563 throw new ArgumentException("Node missing Address!", nameof(node));
+
564
+
565 if (!SwarmMode)
+
566 throw new InvalidOperationException("Swarm mode not enabled!");
+
567
+
568 if (!swarmController)
+
569 throw new InvalidOperationException("Cannot RegisterNode on swarm node!");
+
570
+
571 logger.LogTrace("RegisterNode");
+
572
+
573 await AbortUpdate();
574
-
575 logger.LogWarning(
-
576 "Registration ID collision! Node {nodeId} tried to register with {otherNodeId}'s registration ID: {registrationId}",
-
577 node.Identifier,
-
578 preExistingRegistrationKvp.Key,
-
579 registrationId);
-
580 return false;
-
581 }
-
582
-
583 if (registrationIdsAndTimes.TryGetValue(node.Identifier, out var oldRegistration))
+
575 SwarmRegistrationResponse CreateResponse() => new()
+
576 {
+
577 TokenSigningKeyBase64 = Convert.ToBase64String(tokenFactory.SigningKeyBytes),
+
578 };
+
579
+
580 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
+
581 lock (swarmServers)
+
582 {
+
583 if (registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId))
584 {
-
585 logger.LogInformation("Node {nodeId} is re-registering without first unregistering. Indicative of restart.", node.Identifier);
-
586 swarmServers.RemoveAll(x => x.Identifier == node.Identifier);
- -
588 }
-
589
- -
591 {
-
592 PublicAddress = node.PublicAddress,
-
593 Address = node.Address,
-
594 Identifier = node.Identifier,
-
595 Controller = false,
-
596 });
-
597 registrationIdsAndTimes.Add(node.Identifier, (RegistrationId: registrationId, DateTimeOffset.UtcNow));
-
598 }
+
585 var preExistingRegistrationKvp = registrationIdsAndTimes.FirstOrDefault(x => x.Value.RegistrationId == registrationId);
+
586 if (preExistingRegistrationKvp.Key == node.Identifier)
+
587 {
+
588 logger.LogWarning("Node {nodeId} has already registered!", node.Identifier);
+
589 return CreateResponse();
+
590 }
+
591
+
592 logger.LogWarning(
+
593 "Registration ID collision! Node {nodeId} tried to register with {otherNodeId}'s registration ID: {registrationId}",
+
594 node.Identifier,
+
595 preExistingRegistrationKvp.Key,
+
596 registrationId);
+
597 return null;
+
598 }
599
-
600 logger.LogInformation("Registered node {nodeId} ({nodeIP}) with ID {registrationId}", node.Identifier, node.Address, registrationId);
- -
602 return true;
-
603 }
+
600 if (registrationIdsAndTimes.TryGetValue(node.Identifier, out var oldRegistration))
+
601 {
+
602 logger.LogInformation("Node {nodeId} is re-registering without first unregistering. Indicative of restart.", node.Identifier);
+
603 swarmServers.RemoveAll(x => x.Identifier == node.Identifier);
+ +
605 }
+
606
+ +
608 {
+
609 PublicAddress = node.PublicAddress,
+
610 Address = node.Address,
+
611 Identifier = node.Identifier,
+
612 Controller = false,
+
613 });
+
614 registrationIdsAndTimes.Add(node.Identifier, (RegistrationId: registrationId, DateTimeOffset.UtcNow));
+
615 }
+
616
+
617 logger.LogInformation("Registered node {nodeId} ({nodeIP}) with ID {registrationId}", node.Identifier, node.Address, registrationId);
+ +
619 return CreateResponse();
+
620 }
-
604
-
-
606 public async ValueTask<bool> RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
-
607 {
-
608 var localUpdateOperation = updateOperation;
-
609 if (!swarmController)
-
610 {
-
611 logger.LogDebug("Received remote commit go ahead");
-
612 return localUpdateOperation?.Commit() == true;
-
613 }
-
614
-
615 var nodeIdentifier = NodeIdentifierFromRegistration(registrationId);
-
616 if (nodeIdentifier == null)
-
617 {
-
618 // Something fucky is happening, take no chances.
-
619 logger.LogError("Aborting update due to unforseen circumstances!");
-
620 await AbortUpdate();
-
621 return false;
-
622 }
-
623
-
624 if (localUpdateOperation == null)
-
625 {
-
626 logger.LogDebug("Ignoring ready-commit from node {nodeId} as the update appears to have been aborted.", nodeIdentifier);
-
627 return false;
-
628 }
-
629
-
630 if (!localUpdateOperation.MarkNodeReady(nodeIdentifier))
-
631 {
-
632 logger.LogError(
-
633 "Attempting to mark {nodeId} as ready to commit resulted in the update being aborted!",
-
634 nodeIdentifier);
-
635
-
636 // bit racy here, localUpdateOperation has already been aborted.
-
637 // now if, FOR SOME GODFORSAKEN REASON, there's a new update operation, abort that too.
-
638 if (Interlocked.CompareExchange(ref updateOperation, null, localUpdateOperation) == localUpdateOperation)
-
639 await RemoteAbortUpdate();
-
640 else
-
641 {
-
642 // marking as an error because how the actual fuck
-
643 logger.LogError("Aborting new update due to unforseen consequences!");
-
644 await AbortUpdate();
-
645 }
+
621
+
+
623 public async ValueTask<bool> RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
+
624 {
+
625 var localUpdateOperation = updateOperation;
+
626 if (!swarmController)
+
627 {
+
628 logger.LogDebug("Received remote commit go ahead");
+
629 return localUpdateOperation?.Commit() == true;
+
630 }
+
631
+
632 var nodeIdentifier = NodeIdentifierFromRegistration(registrationId);
+
633 if (nodeIdentifier == null)
+
634 {
+
635 // Something fucky is happening, take no chances.
+
636 logger.LogError("Aborting update due to unforseen circumstances!");
+
637 await AbortUpdate();
+
638 return false;
+
639 }
+
640
+
641 if (localUpdateOperation == null)
+
642 {
+
643 logger.LogDebug("Ignoring ready-commit from node {nodeId} as the update appears to have been aborted.", nodeIdentifier);
+
644 return false;
+
645 }
646
-
647 return false;
-
648 }
-
649
-
650 logger.LogDebug("Node {nodeId} is ready to commit.", nodeIdentifier);
-
651 return true;
-
652 }
+
647 if (!localUpdateOperation.MarkNodeReady(nodeIdentifier))
+
648 {
+
649 logger.LogError(
+
650 "Attempting to mark {nodeId} as ready to commit resulted in the update being aborted!",
+
651 nodeIdentifier);
+
652
+
653 // bit racy here, localUpdateOperation has already been aborted.
+
654 // now if, FOR SOME GODFORSAKEN REASON, there's a new update operation, abort that too.
+
655 if (Interlocked.CompareExchange(ref updateOperation, null, localUpdateOperation) == localUpdateOperation)
+
656 await RemoteAbortUpdate();
+
657 else
+
658 {
+
659 // marking as an error because how the actual fuck
+
660 logger.LogError("Aborting new update due to unforseen consequences!");
+
661 await AbortUpdate();
+
662 }
+
663
+
664 return false;
+
665 }
+
666
+
667 logger.LogDebug("Node {nodeId} is ready to commit.", nodeIdentifier);
+
668 return true;
+
669 }
-
653
-
-
655 public async ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
-
656 {
-
657 if (!SwarmMode)
-
658 throw new InvalidOperationException("Swarm mode not enabled!");
-
659
-
660 logger.LogTrace("UnregisterNode {registrationId}", registrationId);
-
661 await AbortUpdate();
-
662
-
663 if (!swarmController)
-
664 {
-
665 // immediately trigger a health check
-
666 logger.LogInformation("Controller unregistering, will attempt re-registration...");
- - -
669 return;
-
670 }
-
671
-
672 var nodeIdentifier = NodeIdentifierFromRegistration(registrationId);
-
673 if (nodeIdentifier == null)
-
674 return;
-
675
-
676 logger.LogInformation("Unregistering node {nodeId}...", nodeIdentifier);
-
677
-
678 lock (swarmServers)
-
679 {
-
680 swarmServers.RemoveAll(x => x.Identifier == nodeIdentifier);
-
681 registrationIdsAndTimes!.Remove(nodeIdentifier);
-
682 }
-
683
- -
685 }
+
670
+
+
672 public async ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
+
673 {
+
674 if (!SwarmMode)
+
675 throw new InvalidOperationException("Swarm mode not enabled!");
+
676
+
677 logger.LogTrace("UnregisterNode {registrationId}", registrationId);
+
678 await AbortUpdate();
+
679
+
680 if (!swarmController)
+
681 {
+
682 // immediately trigger a health check
+
683 logger.LogInformation("Controller unregistering, will attempt re-registration...");
+ + +
686 return;
+
687 }
+
688
+
689 var nodeIdentifier = NodeIdentifierFromRegistration(registrationId);
+
690 if (nodeIdentifier == null)
+
691 return;
+
692
+
693 logger.LogInformation("Unregistering node {nodeId}...", nodeIdentifier);
+
694
+
695 lock (swarmServers)
+
696 {
+
697 swarmServers.RemoveAll(x => x.Identifier == nodeIdentifier);
+
698 registrationIdsAndTimes!.Remove(nodeIdentifier);
+
699 }
+
700
+ +
702 }
-
686
-
- -
693 {
-
694 logger.LogInformation("Aborting swarm update!");
-
695
-
696 using var httpClient = httpClientFactory.CreateClient();
-
697 async ValueTask SendRemoteAbort(SwarmServerInformation swarmServer)
-
698 {
-
699 using var request = PrepareSwarmRequest(
-
700 swarmServer,
-
701 HttpMethod.Delete,
- -
703 null);
-
704
-
705 try
-
706 {
-
707 // DCT: Intentionally should not be cancelled
-
708 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
-
709 response.EnsureSuccessStatusCode();
-
710 }
-
711 catch (Exception ex)
-
712 {
-
713 logger.LogWarning(
-
714 ex,
-
715 "Unable to send remote abort to {nodeOrController}!",
- -
717 ? $"node {swarmServer.Identifier}"
-
718 : "controller");
-
719 }
-
720 }
+
703
+
+ +
710 {
+
711 logger.LogInformation("Aborting swarm update!");
+
712
+
713 using var httpClient = httpClientFactory.CreateClient();
+
714 async ValueTask SendRemoteAbort(SwarmServerInformation swarmServer)
+
715 {
+
716 using var request = PrepareSwarmRequest(
+
717 swarmServer,
+
718 HttpMethod.Delete,
+ +
720 null);
721
-
722 if (!swarmController)
-
723 return SendRemoteAbort(new SwarmServerInformation
-
724 {
- -
726 });
-
727
-
728 lock (swarmServers!)
- - -
731 .Where(x => !x.Controller)
-
732 .Select(SendRemoteAbort)
-
733 .ToList());
-
734 }
+
722 try
+
723 {
+
724 // DCT: Intentionally should not be cancelled
+
725 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
+
726 response.EnsureSuccessStatusCode();
+
727 }
+
728 catch (Exception ex)
+
729 {
+
730 logger.LogWarning(
+
731 ex,
+
732 "Unable to send remote abort to {nodeOrController}!",
+ +
734 ? $"node {swarmServer.Identifier}"
+
735 : "controller");
+
736 }
+
737 }
+
738
+
739 if (!swarmController)
+
740 return SendRemoteAbort(new SwarmServerInformation
+
741 {
+ +
743 });
+
744
+
745 lock (swarmServers!)
+ + +
748 .Where(x => !x.Controller)
+
749 .Select(SendRemoteAbort)
+
750 .ToList());
+
751 }
-
735
-
- -
743 {
-
744 var httpClient = httpClientFactory.CreateClient();
-
745 try
-
746 {
-
747 var request = PrepareSwarmRequest(
-
748 sourceNode,
-
749 HttpMethod.Get,
-
750 $"{SwarmConstants.UpdateRoute}?ticket={HttpUtility.UrlEncode(ticket.FileTicket)}",
-
751 null);
752
-
753 try
-
754 {
-
755 request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Octet));
-
756 return new RequestFileStreamProvider(httpClient, request);
-
757 }
-
758 catch
-
759 {
-
760 request.Dispose();
-
761 throw;
-
762 }
-
763 }
-
764 catch
-
765 {
-
766 httpClient.Dispose();
-
767 throw;
-
768 }
-
769 }
-
-
770
-
-
778 async ValueTask<SwarmPrepareResult> PrepareUpdateImpl(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
-
779 {
-
780 var version = updateRequest.UpdateVersion!;
-
781 if (!SwarmMode)
+
+ +
760 {
+
761 var httpClient = httpClientFactory.CreateClient();
+
762 try
+
763 {
+
764 var request = PrepareSwarmRequest(
+
765 sourceNode,
+
766 HttpMethod.Get,
+
767 $"{SwarmConstants.UpdateRoute}?ticket={HttpUtility.UrlEncode(ticket.FileTicket)}",
+
768 null);
+
769
+
770 try
+
771 {
+
772 request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Octet));
+
773 return new RequestFileStreamProvider(httpClient, request);
+
774 }
+
775 catch
+
776 {
+
777 request.Dispose();
+
778 throw;
+
779 }
+
780 }
+
781 catch
782 {
-
783 // we still need an active update operation for the TargetVersion
- -
785 return SwarmPrepareResult.SuccessProviderNotRequired;
-
786 }
+
783 httpClient.Dispose();
+
784 throw;
+
785 }
+
786 }
+
787
-
788 var initiator = initiatorProvider != null;
-
789 logger.LogTrace("PrepareUpdateImpl {version}...", version);
-
790
-
791 var shouldAbort = false;
-
792 SwarmUpdateOperation localUpdateOperation;
-
793 try
-
794 {
-
795 SwarmServerInformation? sourceNode = null;
-
796 List<SwarmServerInformation> currentNodes;
-
797 lock (swarmServers)
-
798 {
-
799 currentNodes = swarmServers
-
800 .Select(node =>
-
801 {
-
802 if (node.Identifier == updateRequest.SourceNode)
-
803 sourceNode = node;
+
+
795 async ValueTask<SwarmPrepareResult> PrepareUpdateImpl(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
+
796 {
+
797 var version = updateRequest.UpdateVersion!;
+
798 if (!SwarmMode)
+
799 {
+
800 // we still need an active update operation for the TargetVersion
+ +
802 return SwarmPrepareResult.SuccessProviderNotRequired;
+
803 }
804
-
805 return node;
-
806 })
-
807 .ToList();
-
808 if (swarmController)
-
809 localUpdateOperation = new SwarmUpdateOperation(
-
810 version,
-
811 currentNodes);
-
812 else
-
813 localUpdateOperation = new SwarmUpdateOperation(version);
-
814 }
-
815
-
816 var existingUpdateOperation = Interlocked.CompareExchange(ref updateOperation, localUpdateOperation, null);
-
817 if (existingUpdateOperation != null && existingUpdateOperation.TargetVersion != version)
-
818 {
-
819 logger.LogWarning("Aborting update preparation, version {targetUpdateVersion} already prepared!", existingUpdateOperation.TargetVersion);
-
820 shouldAbort = true;
-
821 return SwarmPrepareResult.Failure;
-
822 }
-
823
-
824 if (existingUpdateOperation?.TargetVersion == version)
-
825 {
-
826 logger.LogTrace("PrepareUpdateImpl early out, already prepared!");
-
827 return SwarmPrepareResult.SuccessProviderNotRequired;
-
828 }
-
829
-
830 if (!swarmController && initiator)
-
831 {
-
832 var downloadTickets = await CreateDownloadTickets(initiatorProvider!, currentNodes, cancellationToken); // condition of initiator
-
833
-
834 logger.LogInformation("Forwarding update request to swarm controller...");
-
835 using var httpClient = httpClientFactory.CreateClient();
-
836 using var request = PrepareSwarmRequest(
-
837 null,
-
838 HttpMethod.Put,
- - -
841 {
-
842 UpdateVersion = version,
-
843 SourceNode = swarmConfiguration.Identifier,
-
844 DownloadTickets = downloadTickets,
-
845 });
+
805 var initiator = initiatorProvider != null;
+
806 logger.LogTrace("PrepareUpdateImpl {version}...", version);
+
807
+
808 var shouldAbort = false;
+
809 SwarmUpdateOperation localUpdateOperation;
+
810 try
+
811 {
+
812 SwarmServerInformation? sourceNode = null;
+
813 List<SwarmServerInformation> currentNodes;
+
814 lock (swarmServers)
+
815 {
+
816 currentNodes = swarmServers
+
817 .Select(node =>
+
818 {
+
819 if (node.Identifier == updateRequest.SourceNode)
+
820 sourceNode = node;
+
821
+
822 return node;
+
823 })
+
824 .ToList();
+
825 if (swarmController)
+
826 localUpdateOperation = new SwarmUpdateOperation(
+
827 version,
+
828 currentNodes);
+
829 else
+
830 localUpdateOperation = new SwarmUpdateOperation(version);
+
831 }
+
832
+
833 var existingUpdateOperation = Interlocked.CompareExchange(ref updateOperation, localUpdateOperation, null);
+
834 if (existingUpdateOperation != null && existingUpdateOperation.TargetVersion != version)
+
835 {
+
836 logger.LogWarning("Aborting update preparation, version {targetUpdateVersion} already prepared!", existingUpdateOperation.TargetVersion);
+
837 shouldAbort = true;
+
838 return SwarmPrepareResult.Failure;
+
839 }
+
840
+
841 if (existingUpdateOperation?.TargetVersion == version)
+
842 {
+
843 logger.LogTrace("PrepareUpdateImpl early out, already prepared!");
+
844 return SwarmPrepareResult.SuccessProviderNotRequired;
+
845 }
846
-
847 // File transfer service will hold the necessary streams
-
848 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
849 if (response.IsSuccessStatusCode)
-
850 return SwarmPrepareResult.SuccessHoldProviderUntilCommit;
-
851
-
852 shouldAbort = true;
-
853 return SwarmPrepareResult.Failure;
-
854 }
-
855
-
856 if (!initiator)
-
857 {
-
858 logger.LogTrace("Beginning local update process...");
-
859
-
860 if (sourceNode == null)
-
861 {
-
862 logger.Log(
- -
864 ? LogLevel.Error
-
865 : LogLevel.Warning,
-
866 "Missing local node entry for update source node: {sourceNode}",
-
867 updateRequest.SourceNode);
-
868 shouldAbort = true;
-
869 return SwarmPrepareResult.Failure;
-
870 }
-
871
-
872 if (updateRequest.DownloadTickets == null)
-
873 {
-
874 logger.LogError("Missing download tickets in update request!");
-
875 return SwarmPrepareResult.Failure;
-
876 }
-
877
-
878 if (!updateRequest.DownloadTickets.TryGetValue(swarmConfiguration.Identifier!, out var ticket))
-
879 {
-
880 logger.Log(
- -
882 ? LogLevel.Error
-
883 : LogLevel.Warning,
-
884 "Missing node entry for download ticket in update request!");
+
847 if (!swarmController && initiator)
+
848 {
+
849 var downloadTickets = await CreateDownloadTickets(initiatorProvider!, currentNodes, cancellationToken); // condition of initiator
+
850
+
851 logger.LogInformation("Forwarding update request to swarm controller...");
+
852 using var httpClient = httpClientFactory.CreateClient();
+
853 using var request = PrepareSwarmRequest(
+
854 null,
+
855 HttpMethod.Put,
+ + +
858 {
+
859 UpdateVersion = version,
+
860 SourceNode = swarmConfiguration.Identifier,
+
861 DownloadTickets = downloadTickets,
+
862 });
+
863
+
864 // File transfer service will hold the necessary streams
+
865 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
866 if (response.IsSuccessStatusCode)
+
867 return SwarmPrepareResult.SuccessHoldProviderUntilCommit;
+
868
+
869 shouldAbort = true;
+
870 return SwarmPrepareResult.Failure;
+
871 }
+
872
+
873 if (!initiator)
+
874 {
+
875 logger.LogTrace("Beginning local update process...");
+
876
+
877 if (sourceNode == null)
+
878 {
+
879 logger.Log(
+ +
881 ? LogLevel.Error
+
882 : LogLevel.Warning,
+
883 "Missing local node entry for update source node: {sourceNode}",
+
884 updateRequest.SourceNode);
885 shouldAbort = true;
886 return SwarmPrepareResult.Failure;
887 }
888
-
889 ServerUpdateResult updateApplyResult;
-
890 var downloaderStream = CreateUpdateStreamProvider(sourceNode, ticket);
-
891 try
-
892 {
-
893 updateApplyResult = await serverUpdater.BeginUpdate(
-
894 this,
-
895 downloaderStream,
-
896 version,
-
897 cancellationToken);
-
898 }
-
899 catch
-
900 {
-
901 await downloaderStream.DisposeAsync();
-
902 throw;
-
903 }
-
904
-
905 if (updateApplyResult != ServerUpdateResult.Started)
-
906 {
-
907 logger.LogWarning("Failed to prepare update! Result: {serverUpdateResult}", updateApplyResult);
-
908 shouldAbort = true;
-
909 return SwarmPrepareResult.Failure;
-
910 }
-
911 }
-
912 else
-
913 logger.LogTrace("No need to re-initiate update as it originated here on the swarm controller");
-
914
-
915 logger.LogDebug("Local node prepared for update to version {version}", version);
-
916 }
-
917 catch (Exception ex)
-
918 {
-
919 logger.LogWarning(ex, "Failed to prepare update!");
-
920 shouldAbort = true;
-
921 return SwarmPrepareResult.Failure;
-
922 }
-
923 finally
-
924 {
-
925 if (shouldAbort)
-
926 await AbortUpdate();
-
927 }
-
928
-
929 if (!swarmController)
-
930 return SwarmPrepareResult.SuccessProviderNotRequired;
+
889 if (updateRequest.DownloadTickets == null)
+
890 {
+
891 logger.LogError("Missing download tickets in update request!");
+
892 return SwarmPrepareResult.Failure;
+
893 }
+
894
+
895 if (!updateRequest.DownloadTickets.TryGetValue(swarmConfiguration.Identifier!, out var ticket))
+
896 {
+
897 logger.Log(
+ +
899 ? LogLevel.Error
+
900 : LogLevel.Warning,
+
901 "Missing node entry for download ticket in update request!");
+
902 shouldAbort = true;
+
903 return SwarmPrepareResult.Failure;
+
904 }
+
905
+
906 ServerUpdateResult updateApplyResult;
+
907 var downloaderStream = CreateUpdateStreamProvider(sourceNode, ticket);
+
908 try
+
909 {
+
910 updateApplyResult = await serverUpdater.BeginUpdate(
+
911 this,
+
912 downloaderStream,
+
913 version,
+
914 cancellationToken);
+
915 }
+
916 catch
+
917 {
+
918 await downloaderStream.DisposeAsync();
+
919 throw;
+
920 }
+
921
+
922 if (updateApplyResult != ServerUpdateResult.Started)
+
923 {
+
924 logger.LogWarning("Failed to prepare update! Result: {serverUpdateResult}", updateApplyResult);
+
925 shouldAbort = true;
+
926 return SwarmPrepareResult.Failure;
+
927 }
+
928 }
+
929 else
+
930 logger.LogTrace("No need to re-initiate update as it originated here on the swarm controller");
931
- -
933 initiatorProvider,
-
934 updateRequest,
-
935 localUpdateOperation,
-
936 cancellationToken);
-
937 }
+
932 logger.LogDebug("Local node prepared for update to version {version}", version);
+
933 }
+
934 catch (Exception ex)
+
935 {
+
936 logger.LogWarning(ex, "Failed to prepare update!");
+
937 shouldAbort = true;
+
938 return SwarmPrepareResult.Failure;
+
939 }
+
940 finally
+
941 {
+
942 if (shouldAbort)
+
943 await AbortUpdate();
+
944 }
+
945
+
946 if (!swarmController)
+
947 return SwarmPrepareResult.SuccessProviderNotRequired;
+
948
+ +
950 initiatorProvider,
+
951 updateRequest,
+
952 localUpdateOperation,
+
953 cancellationToken);
+
954 }
-
938
-
-
947 async ValueTask<SwarmPrepareResult> ControllerDistributedPrepareUpdate(
-
948 ISeekableFileStreamProvider? initiatorProvider,
-
949 SwarmUpdateRequest updateRequest,
-
950 SwarmUpdateOperation currentUpdateOperation,
-
951 CancellationToken cancellationToken)
-
952 {
-
953 bool abortUpdate = false;
-
954 try
-
955 {
-
956 logger.LogInformation("Sending remote prepare to nodes...");
-
957
-
958 if (currentUpdateOperation.InvolvedServers.Count - 1 < swarmConfiguration.UpdateRequiredNodeCount)
-
959 {
-
960 logger.LogWarning(
-
961 "Aborting update, controller expects to be in sync with {requiredNodeCount} nodes but currently only has {currentNodeCount}!",
- -
963 currentUpdateOperation.InvolvedServers.Count - 1);
-
964 abortUpdate = true;
-
965 return SwarmPrepareResult.Failure;
-
966 }
-
967
-
968 var weAreInitiator = initiatorProvider != null;
-
969 if (currentUpdateOperation.InvolvedServers.Count == 1)
-
970 {
-
971 logger.LogDebug("Controller has no nodes, setting commit-ready.");
-
972 if (updateOperation?.Commit() == true)
-
973 return SwarmPrepareResult.SuccessProviderNotRequired;
+
955
+
+
964 async ValueTask<SwarmPrepareResult> ControllerDistributedPrepareUpdate(
+
965 ISeekableFileStreamProvider? initiatorProvider,
+
966 SwarmUpdateRequest updateRequest,
+
967 SwarmUpdateOperation currentUpdateOperation,
+
968 CancellationToken cancellationToken)
+
969 {
+
970 bool abortUpdate = false;
+
971 try
+
972 {
+
973 logger.LogInformation("Sending remote prepare to nodes...");
974
-
975 logger.LogDebug("Update appears to have been aborted");
-
976 return SwarmPrepareResult.Failure;
-
977 }
-
978
-
979 // The initiator node obviously doesn't create a ticket for itself
-
980 else if (!weAreInitiator && updateRequest.DownloadTickets!.Count != currentUpdateOperation.InvolvedServers.Count - 1)
-
981 {
-
982 logger.LogWarning(
-
983 "Aborting update, {receivedTickets} download tickets were provided but there are {nodesToUpdate} nodes in the swarm that require the package!",
-
984 updateRequest.DownloadTickets.Count,
-
985 currentUpdateOperation.InvolvedServers.Count);
-
986 abortUpdate = true;
-
987 return SwarmPrepareResult.Failure;
-
988 }
-
989
-
990 var downloadTicketDictionary = weAreInitiator
-
991 ? await CreateDownloadTickets(initiatorProvider!, currentUpdateOperation.InvolvedServers, cancellationToken)
-
992 : updateRequest.DownloadTickets!;
-
993
-
994 var sourceNode = weAreInitiator
-
995 ? swarmConfiguration.Identifier
-
996 : updateRequest.SourceNode;
-
997
-
998 using var httpClient = httpClientFactory.CreateClient();
-
999 using var transferSemaphore = new SemaphoreSlim(1);
-
1000
-
1001 bool anyFailed = false;
-
1002 var updateRequests = currentUpdateOperation
- -
1004 .Where(node => !node.Controller)
-
1005 .Select(node =>
-
1006 {
-
1007 // only send the necessary ticket to each node from the controller
-
1008 Dictionary<string, FileTicketResponse>? localTicketDictionary;
-
1009 var nodeId = node.Identifier!;
-
1010 if (nodeId == sourceNode)
-
1011 localTicketDictionary = null;
-
1012 else if (!downloadTicketDictionary.TryGetValue(nodeId, out var ticket))
-
1013 {
-
1014 logger.LogError("Missing download ticket for node {missingNodeId}!", nodeId);
-
1015 anyFailed = true;
-
1016 return null;
-
1017 }
-
1018 else
-
1019 localTicketDictionary = new Dictionary<string, FileTicketResponse>
-
1020 {
-
1021 { nodeId, ticket },
-
1022 };
-
1023
-
1024 var request = new SwarmUpdateRequest
-
1025 {
-
1026 UpdateVersion = updateRequest.UpdateVersion,
-
1027 SourceNode = sourceNode,
-
1028 DownloadTickets = localTicketDictionary,
-
1029 };
-
1030
-
1031 return Tuple.Create(node, request);
-
1032 })
-
1033 .ToList();
-
1034
-
1035 if (anyFailed)
-
1036 return SwarmPrepareResult.Failure;
-
1037
-
1038 var tasks = updateRequests
-
1039 .Select(async tuple =>
-
1040 {
-
1041 var node = tuple!.Item1;
-
1042 var body = tuple.Item2;
-
1043
-
1044 using var request = PrepareSwarmRequest(
-
1045 node,
-
1046 HttpMethod.Put,
- -
1048 body);
-
1049
-
1050 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
1051 return response.IsSuccessStatusCode;
-
1052 })
-
1053 .ToList();
+
975 if (currentUpdateOperation.InvolvedServers.Count - 1 < swarmConfiguration.UpdateRequiredNodeCount)
+
976 {
+
977 logger.LogWarning(
+
978 "Aborting update, controller expects to be in sync with {requiredNodeCount} nodes but currently only has {currentNodeCount}!",
+ +
980 currentUpdateOperation.InvolvedServers.Count - 1);
+
981 abortUpdate = true;
+
982 return SwarmPrepareResult.Failure;
+
983 }
+
984
+
985 var weAreInitiator = initiatorProvider != null;
+
986 if (currentUpdateOperation.InvolvedServers.Count == 1)
+
987 {
+
988 logger.LogDebug("Controller has no nodes, setting commit-ready.");
+
989 if (updateOperation?.Commit() == true)
+
990 return SwarmPrepareResult.SuccessProviderNotRequired;
+
991
+
992 logger.LogDebug("Update appears to have been aborted");
+
993 return SwarmPrepareResult.Failure;
+
994 }
+
995
+
996 // The initiator node obviously doesn't create a ticket for itself
+
997 else if (!weAreInitiator && updateRequest.DownloadTickets!.Count != currentUpdateOperation.InvolvedServers.Count - 1)
+
998 {
+
999 logger.LogWarning(
+
1000 "Aborting update, {receivedTickets} download tickets were provided but there are {nodesToUpdate} nodes in the swarm that require the package!",
+
1001 updateRequest.DownloadTickets.Count,
+
1002 currentUpdateOperation.InvolvedServers.Count);
+
1003 abortUpdate = true;
+
1004 return SwarmPrepareResult.Failure;
+
1005 }
+
1006
+
1007 var downloadTicketDictionary = weAreInitiator
+
1008 ? await CreateDownloadTickets(initiatorProvider!, currentUpdateOperation.InvolvedServers, cancellationToken)
+
1009 : updateRequest.DownloadTickets!;
+
1010
+
1011 var sourceNode = weAreInitiator
+
1012 ? swarmConfiguration.Identifier
+
1013 : updateRequest.SourceNode;
+
1014
+
1015 using var httpClient = httpClientFactory.CreateClient();
+
1016 using var transferSemaphore = new SemaphoreSlim(1);
+
1017
+
1018 bool anyFailed = false;
+
1019 var updateRequests = currentUpdateOperation
+ +
1021 .Where(node => !node.Controller)
+
1022 .Select(node =>
+
1023 {
+
1024 // only send the necessary ticket to each node from the controller
+
1025 Dictionary<string, FileTicketResponse>? localTicketDictionary;
+
1026 var nodeId = node.Identifier!;
+
1027 if (nodeId == sourceNode)
+
1028 localTicketDictionary = null;
+
1029 else if (!downloadTicketDictionary.TryGetValue(nodeId, out var ticket))
+
1030 {
+
1031 logger.LogError("Missing download ticket for node {missingNodeId}!", nodeId);
+
1032 anyFailed = true;
+
1033 return null;
+
1034 }
+
1035 else
+
1036 localTicketDictionary = new Dictionary<string, FileTicketResponse>
+
1037 {
+
1038 { nodeId, ticket },
+
1039 };
+
1040
+
1041 var request = new SwarmUpdateRequest
+
1042 {
+
1043 UpdateVersion = updateRequest.UpdateVersion,
+
1044 SourceNode = sourceNode,
+
1045 DownloadTickets = localTicketDictionary,
+
1046 };
+
1047
+
1048 return Tuple.Create(node, request);
+
1049 })
+
1050 .ToList();
+
1051
+
1052 if (anyFailed)
+
1053 return SwarmPrepareResult.Failure;
1054
-
1055 await Task.WhenAll(tasks);
-
1056
-
1057 // if all succeeds...
-
1058 if (tasks.All(x => x.Result))
-
1059 {
-
1060 logger.LogInformation("Distributed prepare for update to version {version} complete.", updateRequest.UpdateVersion);
-
1061 return weAreInitiator
-
1062 ? SwarmPrepareResult.SuccessHoldProviderUntilCommit
-
1063 : SwarmPrepareResult.SuccessProviderNotRequired;
-
1064 }
-
1065
-
1066 abortUpdate = true;
-
1067 logger.LogDebug("Distrubuted prepare failed!");
-
1068 }
-
1069 catch (Exception ex)
-
1070 {
-
1071 logger.LogWarning(ex, "Error remotely preparing updates!");
-
1072 }
-
1073 finally
-
1074 {
-
1075 if (abortUpdate)
-
1076 await AbortUpdate();
-
1077 }
-
1078
-
1079 return SwarmPrepareResult.Failure;
-
1080 }
-
-
1081
-
-
1089 async ValueTask<Dictionary<string, FileTicketResponse>> CreateDownloadTickets(
-
1090 ISeekableFileStreamProvider initiatorProvider,
-
1091 IReadOnlyCollection<SwarmServerInformation> involvedServers,
-
1092 CancellationToken cancellationToken)
-
1093 {
-
1094 // we need to ensure this thing is loaded before we start providing downloads or it'll create unnecessary delays
-
1095 var streamRetrievalTask = initiatorProvider.GetResult(cancellationToken);
-
1096
-
1097 var downloadProvider = new FileDownloadProvider(
-
1098 () => initiatorProvider.Disposed
-
1099 ? Api.Models.ErrorCode.ResourceNotPresent
-
1100 : null,
-
1101 async downloadToken => await initiatorProvider.GetOwnedResult(downloadToken), // let it throw if disposed, shouldn't happen regardless
-
1102 "<Swarm Update Package Provider>",
-
1103 false);
-
1104
-
1105 var serversRequiringTickets = involvedServers
-
1106 .Where(node => node.Identifier != swarmConfiguration.Identifier)
-
1107 .ToList();
-
1108
-
1109 logger.LogTrace("Creating {n} download tickets for other nodes...", serversRequiringTickets.Count);
-
1110
-
1111 var downloadTickets = new Dictionary<string, FileTicketResponse>(serversRequiringTickets.Count);
-
1112 foreach (var node in serversRequiringTickets)
-
1113 downloadTickets.Add(
-
1114 node.Identifier!,
-
1115 transferService.CreateDownload(downloadProvider));
-
1116
-
1117 await streamRetrievalTask;
-
1118 return downloadTickets;
-
1119 }
+
1055 var tasks = updateRequests
+
1056 .Select(async tuple =>
+
1057 {
+
1058 var node = tuple!.Item1;
+
1059 var body = tuple.Item2;
+
1060
+
1061 using var request = PrepareSwarmRequest(
+
1062 node,
+
1063 HttpMethod.Put,
+ +
1065 body);
+
1066
+
1067 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
1068 return response.IsSuccessStatusCode;
+
1069 })
+
1070 .ToList();
+
1071
+
1072 await Task.WhenAll(tasks);
+
1073
+
1074 // if all succeeds...
+
1075 if (tasks.All(x => x.Result))
+
1076 {
+
1077 logger.LogInformation("Distributed prepare for update to version {version} complete.", updateRequest.UpdateVersion);
+
1078 return weAreInitiator
+
1079 ? SwarmPrepareResult.SuccessHoldProviderUntilCommit
+
1080 : SwarmPrepareResult.SuccessProviderNotRequired;
+
1081 }
+
1082
+
1083 abortUpdate = true;
+
1084 logger.LogDebug("Distrubuted prepare failed!");
+
1085 }
+
1086 catch (Exception ex)
+
1087 {
+
1088 logger.LogWarning(ex, "Error remotely preparing updates!");
+
1089 }
+
1090 finally
+
1091 {
+
1092 if (abortUpdate)
+
1093 await AbortUpdate();
+
1094 }
+
1095
+
1096 return SwarmPrepareResult.Failure;
+
1097 }
-
1120
-
-
1126 async ValueTask HealthCheckNodes(CancellationToken cancellationToken)
-
1127 {
-
1128 using var httpClient = httpClientFactory.CreateClient();
-
1129
-
1130 List<SwarmServerInformation> currentSwarmServers;
-
1131 lock (swarmServers!)
-
1132 currentSwarmServers = swarmServers.ToList();
+
1098
+
+
1106 async ValueTask<Dictionary<string, FileTicketResponse>> CreateDownloadTickets(
+
1107 ISeekableFileStreamProvider initiatorProvider,
+
1108 IReadOnlyCollection<SwarmServerInformation> involvedServers,
+
1109 CancellationToken cancellationToken)
+
1110 {
+
1111 // we need to ensure this thing is loaded before we start providing downloads or it'll create unnecessary delays
+
1112 var streamRetrievalTask = initiatorProvider.GetResult(cancellationToken);
+
1113
+
1114 var downloadProvider = new FileDownloadProvider(
+
1115 () => initiatorProvider.Disposed
+
1116 ? Api.Models.ErrorCode.ResourceNotPresent
+
1117 : null,
+
1118 async downloadToken => await initiatorProvider.GetOwnedResult(downloadToken), // let it throw if disposed, shouldn't happen regardless
+
1119 "<Swarm Update Package Provider>",
+
1120 false);
+
1121
+
1122 var serversRequiringTickets = involvedServers
+
1123 .Where(node => node.Identifier != swarmConfiguration.Identifier)
+
1124 .ToList();
+
1125
+
1126 logger.LogTrace("Creating {n} download tickets for other nodes...", serversRequiringTickets.Count);
+
1127
+
1128 var downloadTickets = new Dictionary<string, FileTicketResponse>(serversRequiringTickets.Count);
+
1129 foreach (var node in serversRequiringTickets)
+
1130 downloadTickets.Add(
+
1131 node.Identifier!,
+
1132 transferService.CreateDownload(downloadProvider));
1133
-
1134 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
-
1135 async ValueTask HealthRequestForServer(SwarmServerInformation swarmServer)
-
1136 {
-
1137 using var request = PrepareSwarmRequest(
-
1138 swarmServer,
-
1139 HttpMethod.Get,
-
1140 String.Empty,
-
1141 null);
-
1142
-
1143 try
-
1144 {
-
1145 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
1146 response.EnsureSuccessStatusCode();
-
1147 return;
-
1148 }
-
1149 catch (Exception ex) when (ex is not OperationCanceledException)
-
1150 {
-
1151 logger.LogWarning(
-
1152 ex,
-
1153 "Error during swarm server health check on node '{nodeId}'! Unregistering...",
-
1154 swarmServer.Identifier);
-
1155 }
-
1156
-
1157 lock (swarmServers)
-
1158 {
-
1159 swarmServers.Remove(swarmServer);
-
1160 registrationIdsAndTimes.Remove(swarmServer.Identifier!);
-
1161 }
-
1162 }
-
1163
- -
1165 currentSwarmServers
-
1166 .Where(node => !node.Controller
-
1167 && registrationIdsAndTimes.TryGetValue(node.Identifier!, out var registrationAndTime)
-
1168 && registrationAndTime.RegisteredAt.AddMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes) < DateTimeOffset.UtcNow)
-
1169 .Select(HealthRequestForServer));
-
1170
-
1171 lock (swarmServers)
-
1172 if (swarmServers.Count != currentSwarmServers.Count)
-
1173 MarkServersDirty();
-
1174
-
1175 if (serversDirty)
-
1176 await SendUpdatedServerListToNodes(cancellationToken);
-
1177 }
+
1134 await streamRetrievalTask;
+
1135 return downloadTickets;
+
1136 }
-
1178
-
- -
1183 {
-
1184 serversDirty = true;
-
1185 if (TriggerHealthCheck())
-
1186 logger.LogTrace("Server list is dirty!");
-
1187 }
+
1137
+
+
1143 async ValueTask HealthCheckNodes(CancellationToken cancellationToken)
+
1144 {
+
1145 using var httpClient = httpClientFactory.CreateClient();
+
1146
+
1147 List<SwarmServerInformation> currentSwarmServers;
+
1148 lock (swarmServers!)
+
1149 currentSwarmServers = swarmServers.ToList();
+
1150
+
1151 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
+
1152 async ValueTask HealthRequestForServer(SwarmServerInformation swarmServer)
+
1153 {
+
1154 using var request = PrepareSwarmRequest(
+
1155 swarmServer,
+
1156 HttpMethod.Get,
+
1157 String.Empty,
+
1158 null);
+
1159
+
1160 try
+
1161 {
+
1162 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
1163 response.EnsureSuccessStatusCode();
+
1164 return;
+
1165 }
+
1166 catch (Exception ex) when (ex is not OperationCanceledException)
+
1167 {
+
1168 logger.LogWarning(
+
1169 ex,
+
1170 "Error during swarm server health check on node '{nodeId}'! Unregistering...",
+
1171 swarmServer.Identifier);
+
1172 }
+
1173
+
1174 lock (swarmServers)
+
1175 {
+
1176 swarmServers.Remove(swarmServer);
+
1177 registrationIdsAndTimes.Remove(swarmServer.Identifier!);
+
1178 }
+
1179 }
+
1180
+ +
1182 currentSwarmServers
+
1183 .Where(node => !node.Controller
+
1184 && registrationIdsAndTimes.TryGetValue(node.Identifier!, out var registrationAndTime)
+
1185 && registrationAndTime.RegisteredAt.AddMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes) < DateTimeOffset.UtcNow)
+
1186 .Select(HealthRequestForServer));
+
1187
+
1188 lock (swarmServers)
+
1189 if (swarmServers.Count != currentSwarmServers.Count)
+
1190 MarkServersDirty();
+
1191
+
1192 if (serversDirty)
+
1193 await SendUpdatedServerListToNodes(cancellationToken);
+
1194 }
-
1188
-
- -
1194 {
-
1195 var currentTcs = Interlocked.Exchange(ref forceHealthCheckTcs, new TaskCompletionSource());
-
1196 return currentTcs!.TrySetResult();
-
1197 }
+
1195
+
+ +
1200 {
+
1201 serversDirty = true;
+
1202 if (TriggerHealthCheck())
+
1203 logger.LogTrace("Server list is dirty!");
+
1204 }
-
1198
-
-
1204 async ValueTask HealthCheckController(CancellationToken cancellationToken)
-
1205 {
-
1206 using var httpClient = httpClientFactory.CreateClient();
-
1207
-
1208 if (controllerRegistration.HasValue)
-
1209 try
-
1210 {
-
1211 using var request = PrepareSwarmRequest(
-
1212 null,
-
1213 HttpMethod.Get,
-
1214 String.Empty,
-
1215 null);
-
1216 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
1217 response.EnsureSuccessStatusCode();
-
1218 logger.LogTrace("Controller health check successful");
-
1219 return;
-
1220 }
-
1221 catch (Exception ex)
-
1222 {
-
1223 logger.LogWarning(ex, "Error during swarm controller health check! Attempting to re-register...");
-
1224 controllerRegistration = null;
-
1225 await AbortUpdate();
-
1226 }
-
1227
-
1228 SwarmRegistrationResult registrationResult;
-
1229 for (var registrationAttempt = 1UL; ; ++registrationAttempt)
-
1230 {
-
1231 logger.LogInformation("Swarm re-registration attempt {attemptNumber}...", registrationAttempt);
-
1232 registrationResult = await RegisterWithController(cancellationToken);
-
1233
-
1234 if (registrationResult == SwarmRegistrationResult.Success)
-
1235 return;
-
1236
-
1237 if (registrationResult == SwarmRegistrationResult.Unauthorized)
-
1238 {
-
1239 logger.LogError("Swarm re-registration failed, controller's private key has changed!");
-
1240 break;
-
1241 }
-
1242
-
1243 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
-
1244 {
-
1245 logger.LogError("Swarm re-registration failed, controller's TGS version has changed!");
-
1246 break;
-
1247 }
-
1248
-
1249 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
-
1250 }
-
1251
-
1252 // we could do something here... but what?
-
1253 // best to just let the health check loop keep retrying... we won't be able to update at least
-
1254 }
+
1205
+
+ +
1211 {
+
1212 var currentTcs = Interlocked.Exchange(ref forceHealthCheckTcs, new TaskCompletionSource());
+
1213 return currentTcs!.TrySetResult();
+
1214 }
-
1255
-
-
1261 async ValueTask<SwarmRegistrationResult> RegisterWithController(CancellationToken cancellationToken)
-
1262 {
-
1263 logger.LogInformation("Attempting to register with swarm controller at {controllerAddress}...", swarmConfiguration.ControllerAddress);
-
1264 var requestedRegistrationId = Guid.NewGuid();
+
1215
+
+
1221 async ValueTask HealthCheckController(CancellationToken cancellationToken)
+
1222 {
+
1223 using var httpClient = httpClientFactory.CreateClient();
+
1224
+
1225 if (controllerRegistration.HasValue)
+
1226 try
+
1227 {
+
1228 using var request = PrepareSwarmRequest(
+
1229 null,
+
1230 HttpMethod.Get,
+
1231 String.Empty,
+
1232 null);
+
1233 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
1234 response.EnsureSuccessStatusCode();
+
1235 logger.LogTrace("Controller health check successful");
+
1236 return;
+
1237 }
+
1238 catch (Exception ex)
+
1239 {
+
1240 logger.LogWarning(ex, "Error during swarm controller health check! Attempting to re-register...");
+
1241 controllerRegistration = null;
+
1242 await AbortUpdate();
+
1243 }
+
1244
+
1245 SwarmRegistrationResult registrationResult;
+
1246 for (var registrationAttempt = 1UL; ; ++registrationAttempt)
+
1247 {
+
1248 logger.LogInformation("Swarm re-registration attempt {attemptNumber}...", registrationAttempt);
+
1249 registrationResult = await RegisterWithController(cancellationToken);
+
1250
+
1251 if (registrationResult == SwarmRegistrationResult.Success)
+
1252 return;
+
1253
+
1254 if (registrationResult == SwarmRegistrationResult.Unauthorized)
+
1255 {
+
1256 logger.LogError("Swarm re-registration failed, controller's private key has changed!");
+
1257 break;
+
1258 }
+
1259
+
1260 if (registrationResult == SwarmRegistrationResult.VersionMismatch)
+
1261 {
+
1262 logger.LogError("Swarm re-registration failed, controller's TGS version has changed!");
+
1263 break;
+
1264 }
1265
-
1266 using var httpClient = httpClientFactory.CreateClient();
-
1267 using var registrationRequest = PrepareSwarmRequest(
-
1268 null,
-
1269 HttpMethod.Post,
- -
1271 new SwarmRegistrationRequest(assemblyInformationProvider.Version)
-
1272 {
-
1273 Identifier = swarmConfiguration.Identifier,
-
1274 Address = swarmConfiguration.Address,
-
1275 PublicAddress = swarmConfiguration.PublicAddress,
-
1276 },
-
1277 requestedRegistrationId);
-
1278
-
1279 try
-
1280 {
-
1281 using var response = await httpClient.SendAsync(registrationRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
1282 if (response.IsSuccessStatusCode)
-
1283 {
-
1284 logger.LogInformation("Sucessfully registered with ID {registrationId}", requestedRegistrationId);
-
1285 controllerRegistration = requestedRegistrationId;
-
1286 lastControllerHealthCheck = DateTimeOffset.UtcNow;
-
1287 return SwarmRegistrationResult.Success;
-
1288 }
-
1289
-
1290 logger.LogWarning("Unable to register with swarm: HTTP {statusCode}!", response.StatusCode);
-
1291
-
1292 if (response.StatusCode == HttpStatusCode.Unauthorized)
-
1293 return SwarmRegistrationResult.Unauthorized;
-
1294
-
1295 if (response.StatusCode == HttpStatusCode.UpgradeRequired)
-
1296 return SwarmRegistrationResult.VersionMismatch;
-
1297
-
1298 try
-
1299 {
-
1300 var responseData = await response.Content.ReadAsStringAsync(cancellationToken);
-
1301 if (!String.IsNullOrWhiteSpace(responseData))
-
1302 logger.LogDebug("Response:{newLine}{responseData}", Environment.NewLine, responseData);
-
1303 }
-
1304 catch (Exception ex)
-
1305 {
-
1306 logger.LogDebug(ex, "Error reading registration response content stream!");
-
1307 }
-
1308 }
-
1309 catch (Exception ex)
-
1310 {
-
1311 logger.LogWarning(ex, "Error sending registration request!");
-
1312 }
-
1313
-
1314 return SwarmRegistrationResult.CommunicationFailure;
-
1315 }
+
1266 await asyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken);
+
1267 }
+
1268
+
1269 // we could do something here... but what?
+
1270 // best to just let the health check loop keep retrying... we won't be able to update at least
+
1271 }
+
1272
+
+
1278 async ValueTask<SwarmRegistrationResult> RegisterWithController(CancellationToken cancellationToken)
+
1279 {
+
1280 logger.LogInformation("Attempting to register with swarm controller at {controllerAddress}...", swarmConfiguration.ControllerAddress);
+
1281 var requestedRegistrationId = Guid.NewGuid();
+
1282
+
1283 using var httpClient = httpClientFactory.CreateClient();
+
1284 using var registrationRequest = PrepareSwarmRequest(
+
1285 null,
+
1286 HttpMethod.Post,
+ + +
1289 {
+
1290 Identifier = swarmConfiguration.Identifier,
+
1291 Address = swarmConfiguration.Address,
+
1292 PublicAddress = swarmConfiguration.PublicAddress,
+
1293 },
+
1294 requestedRegistrationId);
+
1295
+
1296 try
+
1297 {
+
1298 using var response = await httpClient.SendAsync(registrationRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
1299 if (response.IsSuccessStatusCode)
+
1300 {
+
1301 try
+
1302 {
+
1303 var json = await response.Content.ReadAsStringAsync(cancellationToken);
+
1304 if (json == null)
+
1305 {
+
1306 logger.LogDebug("Error reading registration response content stream! Text was null!");
+
1307 return SwarmRegistrationResult.PayloadFailure;
+
1308 }
+
1309
+
1310 var registrationResponse = JsonConvert.DeserializeObject<SwarmRegistrationResponse>(json);
+
1311 if (registrationResponse == null)
+
1312 {
+
1313 logger.LogDebug("Error reading registration response content stream! Payload was null!");
+
1314 return SwarmRegistrationResult.PayloadFailure;
+
1315 }
1316
-
-
1322 async ValueTask SendUpdatedServerListToNodes(CancellationToken cancellationToken)
-
1323 {
-
1324 List<SwarmServerInformation> currentSwarmServers;
-
1325 lock (swarmServers!)
-
1326 {
-
1327 serversDirty = false;
-
1328 currentSwarmServers = swarmServers.ToList();
-
1329 }
+
1317 if (registrationResponse.TokenSigningKeyBase64 == null)
+
1318 {
+
1319 logger.LogDebug("Error reading registration response content stream! SigningKey was null!");
+
1320 return SwarmRegistrationResult.PayloadFailure;
+
1321 }
+
1322
+
1323 tokenFactory.SigningKeyBytes = Convert.FromBase64String(registrationResponse.TokenSigningKeyBase64);
+
1324 }
+
1325 catch (Exception ex)
+
1326 {
+
1327 logger.LogDebug(ex, "Error reading registration response content stream!");
+
1328 return SwarmRegistrationResult.PayloadFailure;
+
1329 }
1330
-
1331 if (currentSwarmServers.Count == 1)
-
1332 {
-
1333 logger.LogTrace("Skipping server list broadcast as no nodes are connected!");
-
1334 return;
-
1335 }
+
1331 logger.LogInformation("Sucessfully registered with ID {registrationId}", requestedRegistrationId);
+
1332 controllerRegistration = requestedRegistrationId;
+
1333 lastControllerHealthCheck = DateTimeOffset.UtcNow;
+
1334 return SwarmRegistrationResult.Success;
+
1335 }
1336
-
1337 logger.LogDebug("Sending updated server list to all {nodeCount} nodes...", currentSwarmServers.Count - 1);
+
1337 logger.LogWarning("Unable to register with swarm: HTTP {statusCode}!", response.StatusCode);
1338
-
1339 using var httpClient = httpClientFactory.CreateClient();
-
1340 async ValueTask UpdateRequestForServer(SwarmServerInformation swarmServer)
-
1341 {
-
1342 using var request = PrepareSwarmRequest(
-
1343 swarmServer,
-
1344 HttpMethod.Post,
-
1345 String.Empty,
- -
1347 {
-
1348 SwarmServers = currentSwarmServers,
-
1349 });
-
1350
-
1351 try
+
1339 if (response.StatusCode == HttpStatusCode.Unauthorized)
+
1340 return SwarmRegistrationResult.Unauthorized;
+
1341
+
1342 if (response.StatusCode == HttpStatusCode.UpgradeRequired)
+
1343 return SwarmRegistrationResult.VersionMismatch;
+
1344
+
1345 try
+
1346 {
+
1347 var responseData = await response.Content.ReadAsStringAsync(cancellationToken);
+
1348 if (!String.IsNullOrWhiteSpace(responseData))
+
1349 logger.LogDebug("Response:{newLine}{responseData}", Environment.NewLine, responseData);
+
1350 }
+
1351 catch (Exception ex)
1352 {
-
1353 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
-
1354 response.EnsureSuccessStatusCode();
-
1355 }
-
1356 catch (Exception ex) when (ex is not OperationCanceledException)
-
1357 {
-
1358 logger.LogWarning(ex, "Error during swarm server list update for node '{nodeId}'! Unregistering...", swarmServer.Identifier);
-
1359
-
1360 lock (swarmServers)
-
1361 {
-
1362 swarmServers.Remove(swarmServer);
-
1363 registrationIdsAndTimes!.Remove(swarmServer.Identifier!);
-
1364 }
-
1365 }
-
1366 }
-
1367
- -
1369 currentSwarmServers
-
1370 .Where(x => !x.Controller)
-
1371 .Select(UpdateRequestForServer)
-
1372 .ToList());
-
1373 }
+
1353 logger.LogDebug(ex, "Error reading registration response content stream!");
+
1354 }
+
1355 }
+
1356 catch (Exception ex)
+
1357 {
+
1358 logger.LogWarning(ex, "Error sending registration request!");
+
1359 }
+
1360
+
1361 return SwarmRegistrationResult.CommunicationFailure;
+
1362 }
-
1374
-
-
1384 HttpRequestMessage PrepareSwarmRequest(
-
1385 SwarmServerInformation? swarmServer,
-
1386 HttpMethod httpMethod,
-
1387 string route,
-
1388 object? body,
-
1389 Guid? registrationIdOverride = null)
-
1390 {
-
1391 swarmServer ??= new SwarmServerInformation
-
1392 {
-
1393 Address = swarmConfiguration.ControllerAddress,
-
1394 };
-
1395
-
1396 var fullRoute = $"{SwarmConstants.ControllerRoute}/{route}";
-
1397 logger.LogTrace(
-
1398 "{method} {route} to swarm server {nodeIdOrAddress}",
-
1399 httpMethod,
-
1400 fullRoute,
-
1401 swarmServer.Identifier ?? swarmServer.Address!.ToString());
-
1402
-
1403 var request = new HttpRequestMessage(
-
1404 httpMethod,
-
1405 swarmServer.Address + fullRoute[1..]);
-
1406 try
-
1407 {
-
1408 request.Headers.Accept.Clear();
-
1409 request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
-
1410
-
1411 request.Headers.Add(SwarmConstants.ApiKeyHeader, swarmConfiguration.PrivateKey);
-
1412 if (registrationIdOverride.HasValue)
-
1413 request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdOverride.Value.ToString());
-
1414 else if (swarmController)
-
1415 {
-
1416 lock (swarmServers!)
-
1417 if (registrationIdsAndTimes!.TryGetValue(swarmServer.Identifier!, out var registrationIdAndTime))
-
1418 request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdAndTime.RegistrationId.ToString());
-
1419 }
-
1420 else if (controllerRegistration.HasValue)
-
1421 request.Headers.Add(SwarmConstants.RegistrationIdHeader, controllerRegistration.Value.ToString());
-
1422
-
1423 if (body != null)
-
1424 request.Content = new StringContent(
-
1425 JsonConvert.SerializeObject(body, SwarmConstants.SerializerSettings),
-
1426 Encoding.UTF8,
-
1427 MediaTypeNames.Application.Json);
-
1428
-
1429 return request;
-
1430 }
-
1431 catch
-
1432 {
-
1433 request.Dispose();
-
1434 throw;
-
1435 }
-
1436 }
+
1363
+
+
1369 async ValueTask SendUpdatedServerListToNodes(CancellationToken cancellationToken)
+
1370 {
+
1371 List<SwarmServerInformation> currentSwarmServers;
+
1372 lock (swarmServers!)
+
1373 {
+
1374 serversDirty = false;
+
1375 currentSwarmServers = swarmServers.ToList();
+
1376 }
+
1377
+
1378 if (currentSwarmServers.Count == 1)
+
1379 {
+
1380 logger.LogTrace("Skipping server list broadcast as no nodes are connected!");
+
1381 return;
+
1382 }
+
1383
+
1384 logger.LogDebug("Sending updated server list to all {nodeCount} nodes...", currentSwarmServers.Count - 1);
+
1385
+
1386 using var httpClient = httpClientFactory.CreateClient();
+
1387 async ValueTask UpdateRequestForServer(SwarmServerInformation swarmServer)
+
1388 {
+
1389 using var request = PrepareSwarmRequest(
+
1390 swarmServer,
+
1391 HttpMethod.Post,
+
1392 String.Empty,
+ +
1394 {
+
1395 SwarmServers = currentSwarmServers,
+
1396 });
+
1397
+
1398 try
+
1399 {
+
1400 using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
+
1401 response.EnsureSuccessStatusCode();
+
1402 }
+
1403 catch (Exception ex) when (ex is not OperationCanceledException)
+
1404 {
+
1405 logger.LogWarning(ex, "Error during swarm server list update for node '{nodeId}'! Unregistering...", swarmServer.Identifier);
+
1406
+
1407 lock (swarmServers)
+
1408 {
+
1409 swarmServers.Remove(swarmServer);
+
1410 registrationIdsAndTimes!.Remove(swarmServer.Identifier!);
+
1411 }
+
1412 }
+
1413 }
+
1414
+ +
1416 currentSwarmServers
+
1417 .Where(x => !x.Controller)
+
1418 .Select(UpdateRequestForServer)
+
1419 .ToList());
+
1420 }
-
1437
-
-
1443 async Task HealthCheckLoop(CancellationToken cancellationToken)
-
1444 {
-
1445 logger.LogTrace("Starting HealthCheckLoop...");
-
1446 try
-
1447 {
-
1448 var nextForceHealthCheckTask = forceHealthCheckTcs!.Task;
-
1449 while (!cancellationToken.IsCancellationRequested)
-
1450 {
-
1451 TimeSpan delay;
-
1452 if (swarmController)
-
1453 delay = TimeSpan.FromMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes);
-
1454 else
-
1455 {
-
1456 delay = TimeSpan.FromMinutes(SwarmConstants.NodeHealthCheckIntervalMinutes);
-
1457 if (lastControllerHealthCheck.HasValue)
-
1458 {
-
1459 var recommendedTimeOfNextCheck = lastControllerHealthCheck.Value + delay;
-
1460
-
1461 if (recommendedTimeOfNextCheck > DateTimeOffset.UtcNow)
-
1462 delay = recommendedTimeOfNextCheck - DateTimeOffset.UtcNow;
-
1463 }
-
1464 }
-
1465
-
1466 var delayTask = asyncDelayer.Delay(
-
1467 delay,
-
1468 cancellationToken);
+
1421
+
+
1431 HttpRequestMessage PrepareSwarmRequest(
+
1432 SwarmServerInformation? swarmServer,
+
1433 HttpMethod httpMethod,
+
1434 string route,
+
1435 object? body,
+
1436 Guid? registrationIdOverride = null)
+
1437 {
+
1438 swarmServer ??= new SwarmServerInformation
+
1439 {
+
1440 Address = swarmConfiguration.ControllerAddress,
+
1441 };
+
1442
+
1443 var fullRoute = $"{SwarmConstants.ControllerRoute}/{route}";
+
1444 logger.LogTrace(
+
1445 "{method} {route} to swarm server {nodeIdOrAddress}",
+
1446 httpMethod,
+
1447 fullRoute,
+
1448 swarmServer.Identifier ?? swarmServer.Address!.ToString());
+
1449
+
1450 var request = new HttpRequestMessage(
+
1451 httpMethod,
+
1452 swarmServer.Address + fullRoute[1..]);
+
1453 try
+
1454 {
+
1455 request.Headers.Accept.Clear();
+
1456 request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+
1457
+
1458 request.Headers.Add(SwarmConstants.ApiKeyHeader, swarmConfiguration.PrivateKey);
+
1459 if (registrationIdOverride.HasValue)
+
1460 request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdOverride.Value.ToString());
+
1461 else if (swarmController)
+
1462 {
+
1463 lock (swarmServers!)
+
1464 if (registrationIdsAndTimes!.TryGetValue(swarmServer.Identifier!, out var registrationIdAndTime))
+
1465 request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdAndTime.RegistrationId.ToString());
+
1466 }
+
1467 else if (controllerRegistration.HasValue)
+
1468 request.Headers.Add(SwarmConstants.RegistrationIdHeader, controllerRegistration.Value.ToString());
1469
-
1470 var awakeningTask = Task.WhenAny(
-
1471 delayTask,
-
1472 nextForceHealthCheckTask);
-
1473
-
1474 await awakeningTask;
+
1470 if (body != null)
+
1471 request.Content = new StringContent(
+
1472 JsonConvert.SerializeObject(body, SwarmConstants.SerializerSettings),
+
1473 Encoding.UTF8,
+
1474 MediaTypeNames.Application.Json);
1475
-
1476 if (nextForceHealthCheckTask.IsCompleted && swarmController)
-
1477 {
-
1478 // Intentionally wait a few seconds for the other server to start up before interogating it
-
1479 logger.LogTrace("Next health check triggering in {delaySeconds}s...", SwarmConstants.SecondsToDelayForcedHealthChecks);
-
1480 await asyncDelayer.Delay(TimeSpan.FromSeconds(SwarmConstants.SecondsToDelayForcedHealthChecks), cancellationToken);
-
1481 }
-
1482 else if (!swarmController && !nextForceHealthCheckTask.IsCompleted)
-
1483 {
-
1484 if (!lastControllerHealthCheck.HasValue)
-
1485 {
-
1486 logger.LogTrace("Not initially registered with controller, skipping health check.");
-
1487 continue; // unregistered
-
1488 }
-
1489
-
1490 if ((DateTimeOffset.UtcNow - lastControllerHealthCheck.Value).TotalMinutes < SwarmConstants.NodeHealthCheckIntervalMinutes)
-
1491 {
-
1492 logger.LogTrace("Controller seems to be active, skipping health check.");
-
1493 continue;
-
1494 }
-
1495 }
-
1496
-
1497 nextForceHealthCheckTask = forceHealthCheckTcs.Task;
-
1498
-
1499 logger.LogTrace("Performing swarm health check...");
-
1500 try
-
1501 {
-
1502 if (swarmController)
-
1503 await HealthCheckNodes(cancellationToken);
-
1504 else
-
1505 await HealthCheckController(cancellationToken);
-
1506 }
-
1507 catch (Exception ex) when (ex is not OperationCanceledException)
-
1508 {
-
1509 logger.LogError(ex, "Health check error!");
-
1510 }
-
1511 }
-
1512 }
-
1513 catch (OperationCanceledException ex)
-
1514 {
-
1515 logger.LogTrace(ex, "Health check loop cancelled!");
-
1516 }
-
1517
-
1518 logger.LogTrace("Stopped HealthCheckLoop");
-
1519 }
+
1476 return request;
+
1477 }
+
1478 catch
+
1479 {
+
1480 request.Dispose();
+
1481 throw;
+
1482 }
+
1483 }
+
1484
+
+
1490 async Task HealthCheckLoop(CancellationToken cancellationToken)
+
1491 {
+
1492 logger.LogTrace("Starting HealthCheckLoop...");
+
1493 try
+
1494 {
+
1495 var nextForceHealthCheckTask = forceHealthCheckTcs!.Task;
+
1496 while (!cancellationToken.IsCancellationRequested)
+
1497 {
+
1498 TimeSpan delay;
+
1499 if (swarmController)
+
1500 delay = TimeSpan.FromMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes);
+
1501 else
+
1502 {
+
1503 delay = TimeSpan.FromMinutes(SwarmConstants.NodeHealthCheckIntervalMinutes);
+
1504 if (lastControllerHealthCheck.HasValue)
+
1505 {
+
1506 var recommendedTimeOfNextCheck = lastControllerHealthCheck.Value + delay;
+
1507
+
1508 if (recommendedTimeOfNextCheck > DateTimeOffset.UtcNow)
+
1509 delay = recommendedTimeOfNextCheck - DateTimeOffset.UtcNow;
+
1510 }
+
1511 }
+
1512
+
1513 var delayTask = asyncDelayer.Delay(
+
1514 delay,
+
1515 cancellationToken);
+
1516
+
1517 var awakeningTask = Task.WhenAny(
+
1518 delayTask,
+
1519 nextForceHealthCheckTask);
1520
-
-
1526 string? NodeIdentifierFromRegistration(Guid registrationId)
-
1527 {
-
1528 if (!swarmController)
-
1529 throw new InvalidOperationException("NodeIdentifierFromRegistration on node!");
-
1530
-
1531 lock (swarmServers!)
-
1532 {
-
1533 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
-
1534 var exists = registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId);
-
1535 if (!exists)
-
1536 {
-
1537 logger.LogWarning("A node that was to be looked up ({registrationId}) disappeared from our records!", registrationId);
-
1538 return null;
-
1539 }
-
1540
-
1541 return registrationIdsAndTimes.First(x => x.Value.RegistrationId == registrationId).Key;
-
1542 }
-
1543 }
+
1521 await awakeningTask;
+
1522
+
1523 if (nextForceHealthCheckTask.IsCompleted && swarmController)
+
1524 {
+
1525 // Intentionally wait a few seconds for the other server to start up before interogating it
+
1526 logger.LogTrace("Next health check triggering in {delaySeconds}s...", SwarmConstants.SecondsToDelayForcedHealthChecks);
+
1527 await asyncDelayer.Delay(TimeSpan.FromSeconds(SwarmConstants.SecondsToDelayForcedHealthChecks), cancellationToken);
+
1528 }
+
1529 else if (!swarmController && !nextForceHealthCheckTask.IsCompleted)
+
1530 {
+
1531 if (!lastControllerHealthCheck.HasValue)
+
1532 {
+
1533 logger.LogTrace("Not initially registered with controller, skipping health check.");
+
1534 continue; // unregistered
+
1535 }
+
1536
+
1537 if ((DateTimeOffset.UtcNow - lastControllerHealthCheck.Value).TotalMinutes < SwarmConstants.NodeHealthCheckIntervalMinutes)
+
1538 {
+
1539 logger.LogTrace("Controller seems to be active, skipping health check.");
+
1540 continue;
+
1541 }
+
1542 }
+
1543
+
1544 nextForceHealthCheckTask = forceHealthCheckTcs.Task;
+
1545
+
1546 logger.LogTrace("Performing swarm health check...");
+
1547 try
+
1548 {
+
1549 if (swarmController)
+
1550 await HealthCheckNodes(cancellationToken);
+
1551 else
+
1552 await HealthCheckController(cancellationToken);
+
1553 }
+
1554 catch (Exception ex) when (ex is not OperationCanceledException)
+
1555 {
+
1556 logger.LogError(ex, "Health check error!");
+
1557 }
+
1558 }
+
1559 }
+
1560 catch (OperationCanceledException ex)
+
1561 {
+
1562 logger.LogTrace(ex, "Health check loop cancelled!");
+
1563 }
+
1564
+
1565 logger.LogTrace("Stopped HealthCheckLoop");
+
1566 }
+
+
1567
+
+
1573 string? NodeIdentifierFromRegistration(Guid registrationId)
+
1574 {
+
1575 if (!swarmController)
+
1576 throw new InvalidOperationException("NodeIdentifierFromRegistration on node!");
+
1577
+
1578 lock (swarmServers!)
+
1579 {
+
1580 var registrationIdsAndTimes = this.registrationIdsAndTimes!;
+
1581 var exists = registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId);
+
1582 if (!exists)
+
1583 {
+
1584 logger.LogWarning("A node that was to be looked up ({registrationId}) disappeared from our records!", registrationId);
+
1585 return null;
+
1586 }
+
1587
+
1588 return registrationIdsAndTimes.First(x => x.Value.RegistrationId == registrationId).Key;
+
1589 }
+
1590 }
-
1544 }
+
1591 }
-
1545}
+
1592}
@@ -1534,6 +1577,9 @@
Uri? ControllerAddress
The SwarmServer.Address of the swarm controller. If null, the current server is considered the contro...
A IFileStreamProvider that represents the response of HttpRequestMessages.
+
Attribute for bringing in the master versions list from MSBuild that aren't embedded into assemblies ...
+
string RawSwarmProtocolVersion
The Version string of the MariaDB server bundled with TGS installs.
+
static MasterVersionsAttribute Instance
Return the Assembly's instance of the MasterVersionsAttribute.
Constants used by the swarm system.
const int ControllerHealthCheckIntervalMinutes
Interval at which the swarm controller makes health checks on nodes.
static JsonSerializerSettings SerializerSettings
See JsonSerializerSettings for the swarm system.
@@ -1545,57 +1591,59 @@
const int UpdateCommitTimeoutMinutes
Number of minutes the controller waits to receive a ready-commit from all nodes before aborting an up...
const int SecondsToDelayForcedHealthChecks
Number of seconds between a health check global::System.Threading.Tasks.TaskCompletionSource triggeri...
A request to register with a swarm controller.
+
A request to update a nodes list of SwarmServers.
-
Helps keep servers connected to the same database in sync by coordinating updates.
-
volatile? TaskCompletionSource forceHealthCheckTcs
A TaskCompletionSource that is used to force a health check.
-
HttpRequestMessage PrepareSwarmRequest(SwarmServerInformation? swarmServer, HttpMethod httpMethod, string route, object? body, Guid? registrationIdOverride=null)
Prepares a HttpRequestMessage for swarm communication.
-
async ValueTask< SwarmPrepareResult > ControllerDistributedPrepareUpdate(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, SwarmUpdateOperation currentUpdateOperation, CancellationToken cancellationToken)
Send a given updateRequest out to nodes from the swarm controller.
-
void UpdateSwarmServersList(IEnumerable< SwarmServerInformation > swarmServers)
Pass in an updated list of swarmServers to the node.
-
string? NodeIdentifierFromRegistration(Guid registrationId)
Gets the SwarmServer.Identifier from a given registrationId .
-
readonly bool swarmController
If the current server is the swarm controller.
-
async ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
Attempt to unregister a node with a given registrationId with the controller.A ValueTask representin...
-
bool ValidateRegistration(Guid registrationId)
Validate a given registrationId .true if the registration is valid, false otherwise.
-
readonly IFileTransferTicketProvider transferService
The IFileTransferTicketProvider for the SwarmService.
-
readonly? List< SwarmServerInformation > swarmServers
List<T> of connected SwarmServerInformations.
-
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the SwarmService.
-
ValueTask RemoteAbortUpdate()
Sends out remote abort update requests.
-
async ValueTask< SwarmCommitResult > CommitUpdate(CancellationToken cancellationToken)
Signal to the swarm that an update is ready to be applied.A ValueTask<TResult> resulting in the Swarm...
-
bool SwarmMode
If the swarm system is enabled.
-
async ValueTask< bool > RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
Notify the controller that the node with the given registrationId is ready to commit or notify the n...
-
bool ExpectedNumberOfNodesConnected
Gets a value indicating if the expected amount of nodes are connected to the swarm.
-
async Task HealthCheckLoop(CancellationToken cancellationToken)
Timed loop for calling HealthCheckNodes(CancellationToken).
-
async ValueTask< SwarmPrepareResult > PrepareUpdateImpl(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Implementation of PrepareUpdate(ISeekableFileStreamProvider, Version, CancellationToken),...
-
async ValueTask< SwarmRegistrationResult > RegisterWithController(CancellationToken cancellationToken)
Attempt to register the node with the controller.
-
async ValueTask Shutdown(CancellationToken cancellationToken)
Deregister with the swarm controller or put clients into querying state.A ValueTask representing the ...
-
async ValueTask< bool > RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.A ValueTask<TResult> resulting in true if the r...
-
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the SwarmService.
-
async ValueTask HealthCheckController(CancellationToken cancellationToken)
Ping the swarm controller to see that it is still running. If need be, reregister.
-
SwarmService(IDatabaseContextFactory databaseContextFactory, IDatabaseSeeder databaseSeeder, IAssemblyInformationProvider assemblyInformationProvider, IAbstractHttpClientFactory httpClientFactory, IAsyncDelayer asyncDelayer, IServerUpdater serverUpdater, IFileTransferTicketProvider transferService, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmService > logger)
Initializes a new instance of the SwarmService class.
-
async ValueTask< bool > PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Notify the node of an update request from the controller.A ValueTask<TResult> resulting in true if th...
-
readonly? CancellationTokenSource serverHealthCheckCancellationTokenSource
The CancellationTokenSource for serverHealthCheckTask.
-
async ValueTask< SwarmRegistrationResult > Initialize(CancellationToken cancellationToken)
Attempt to register with the swarm controller if not one, sets up the database otherwise....
-
RequestFileStreamProvider CreateUpdateStreamProvider(SwarmServerInformation sourceNode, FileTicketResponse ticket)
Create the RequestFileStreamProvider for an update package retrieval from a given sourceNode .
-
readonly IAbstractHttpClientFactory httpClientFactory
The IAbstractHttpClientFactory for the SwarmService.
-
async ValueTask SendUpdatedServerListToNodes(CancellationToken cancellationToken)
Sends the controllers list of nodes to all nodes.
-
readonly? Dictionary< string,(Guid RegistrationId, DateTimeOffset RegisteredAt)> registrationIdsAndTimes
Dictionary<TKey, TValue> of SwarmServer.Identifiers to registration Guids and when they were created.
-
ValueTask< SwarmPrepareResult > PrepareUpdate(ISeekableFileStreamProvider fileStreamProvider, Version version, CancellationToken cancellationToken)
Signal to the swarm that an update is requested.A ValueTask<TResult> resulting in true if the update ...
-
async ValueTask< Dictionary< string, FileTicketResponse > > CreateDownloadTickets(ISeekableFileStreamProvider initiatorProvider, IReadOnlyCollection< SwarmServerInformation > involvedServers, CancellationToken cancellationToken)
Create a FileTicketResponse for downloading the content of a given initiatorProvider for the rest of...
-
void MarkServersDirty()
Set serversDirty and complete the current forceHealthCheckTcs.
-
bool serversDirty
If the swarmServers list has been updated and needs to be resent to clients.
-
readonly ILogger< SwarmService > logger
The ILogger for the SwarmService.
-
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the SwarmService.
-
List< SwarmServerInformation >? GetSwarmServers()
Gets the list of SwarmServerInformations in the swarm, including the current one.A List<T> of SwarmSe...
-
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SwarmService.
-
Task? serverHealthCheckTask
The Task for the HealthCheckLoop(CancellationToken).
-
async ValueTask AbortUpdate()
Attempt to abort an uncommitted update.A ValueTask representing the running operation....
-
volatile? SwarmUpdateOperation updateOperation
A SwarmUpdateOperation that is currently in progress.
-
Guid? controllerRegistration
The registration Guid provided by the swarm controller.
-
DateTimeOffset? lastControllerHealthCheck
The last DateTimeOffset when the controller checked on this node.
-
readonly IServerUpdater serverUpdater
The IServerUpdater for the SwarmService.
-
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the SwarmService.
+
Helps keep servers connected to the same database in sync by coordinating updates.
+
volatile? TaskCompletionSource forceHealthCheckTcs
A TaskCompletionSource that is used to force a health check.
+
HttpRequestMessage PrepareSwarmRequest(SwarmServerInformation? swarmServer, HttpMethod httpMethod, string route, object? body, Guid? registrationIdOverride=null)
Prepares a HttpRequestMessage for swarm communication.
+
async ValueTask< SwarmPrepareResult > ControllerDistributedPrepareUpdate(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, SwarmUpdateOperation currentUpdateOperation, CancellationToken cancellationToken)
Send a given updateRequest out to nodes from the swarm controller.
+
void UpdateSwarmServersList(IEnumerable< SwarmServerInformation > swarmServers)
Pass in an updated list of swarmServers to the node.
+
string? NodeIdentifierFromRegistration(Guid registrationId)
Gets the SwarmServer.Identifier from a given registrationId .
+
readonly bool swarmController
If the current server is the swarm controller.
+
async ValueTask UnregisterNode(Guid registrationId, CancellationToken cancellationToken)
Attempt to unregister a node with a given registrationId with the controller.A ValueTask representin...
+
bool ValidateRegistration(Guid registrationId)
Validate a given registrationId .true if the registration is valid, false otherwise.
+
readonly IFileTransferTicketProvider transferService
The IFileTransferTicketProvider for the SwarmService.
+
readonly? List< SwarmServerInformation > swarmServers
List<T> of connected SwarmServerInformations.
+
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the SwarmService.
+
ValueTask RemoteAbortUpdate()
Sends out remote abort update requests.
+
async ValueTask< SwarmCommitResult > CommitUpdate(CancellationToken cancellationToken)
Signal to the swarm that an update is ready to be applied.A ValueTask<TResult> resulting in the Swarm...
+
bool SwarmMode
If the swarm system is enabled.
+
async ValueTask< bool > RemoteCommitReceived(Guid registrationId, CancellationToken cancellationToken)
Notify the controller that the node with the given registrationId is ready to commit or notify the n...
+
SwarmService(IDatabaseContextFactory databaseContextFactory, IDatabaseSeeder databaseSeeder, IAssemblyInformationProvider assemblyInformationProvider, IAbstractHttpClientFactory httpClientFactory, IAsyncDelayer asyncDelayer, IServerUpdater serverUpdater, IFileTransferTicketProvider transferService, ITokenFactory tokenFactory, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmService > logger)
Initializes a new instance of the SwarmService class.
+
async ValueTask< SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
Attempt to register a given node with the controller.A ValueTask<TResult> resulting in a SwarmRegist...
+
bool ExpectedNumberOfNodesConnected
Gets a value indicating if the expected amount of nodes are connected to the swarm.
+
async Task HealthCheckLoop(CancellationToken cancellationToken)
Timed loop for calling HealthCheckNodes(CancellationToken).
+
async ValueTask< SwarmPrepareResult > PrepareUpdateImpl(ISeekableFileStreamProvider? initiatorProvider, SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Implementation of PrepareUpdate(ISeekableFileStreamProvider, Version, CancellationToken),...
+
async ValueTask< SwarmRegistrationResult > RegisterWithController(CancellationToken cancellationToken)
Attempt to register the node with the controller.
+
async ValueTask Shutdown(CancellationToken cancellationToken)
Deregister with the swarm controller or put clients into querying state.A ValueTask representing the ...
+
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the SwarmService.
+
async ValueTask HealthCheckController(CancellationToken cancellationToken)
Ping the swarm controller to see that it is still running. If need be, reregister.
+
readonly ITokenFactory tokenFactory
The ITokenFactory for the SwarmService.
+
async ValueTask< bool > PrepareUpdateFromController(SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)
Notify the node of an update request from the controller.A ValueTask<TResult> resulting in true if th...
+
readonly? CancellationTokenSource serverHealthCheckCancellationTokenSource
The CancellationTokenSource for serverHealthCheckTask.
+
async ValueTask< SwarmRegistrationResult > Initialize(CancellationToken cancellationToken)
Attempt to register with the swarm controller if not one, sets up the database otherwise....
+
RequestFileStreamProvider CreateUpdateStreamProvider(SwarmServerInformation sourceNode, FileTicketResponse ticket)
Create the RequestFileStreamProvider for an update package retrieval from a given sourceNode .
+
readonly IAbstractHttpClientFactory httpClientFactory
The IAbstractHttpClientFactory for the SwarmService.
+
async ValueTask SendUpdatedServerListToNodes(CancellationToken cancellationToken)
Sends the controllers list of nodes to all nodes.
+
readonly? Dictionary< string,(Guid RegistrationId, DateTimeOffset RegisteredAt)> registrationIdsAndTimes
Dictionary<TKey, TValue> of SwarmServer.Identifiers to registration Guids and when they were created.
+
ValueTask< SwarmPrepareResult > PrepareUpdate(ISeekableFileStreamProvider fileStreamProvider, Version version, CancellationToken cancellationToken)
Signal to the swarm that an update is requested.A ValueTask<TResult> resulting in true if the update ...
+
async ValueTask< Dictionary< string, FileTicketResponse > > CreateDownloadTickets(ISeekableFileStreamProvider initiatorProvider, IReadOnlyCollection< SwarmServerInformation > involvedServers, CancellationToken cancellationToken)
Create a FileTicketResponse for downloading the content of a given initiatorProvider for the rest of...
+
void MarkServersDirty()
Set serversDirty and complete the current forceHealthCheckTcs.
+
bool serversDirty
If the swarmServers list has been updated and needs to be resent to clients.
+
readonly ILogger< SwarmService > logger
The ILogger for the SwarmService.
+
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the SwarmService.
+
List< SwarmServerInformation >? GetSwarmServers()
Gets the list of SwarmServerInformations in the swarm, including the current one.A List<T> of SwarmSe...
+
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SwarmService.
+
Task? serverHealthCheckTask
The Task for the HealthCheckLoop(CancellationToken).
+
async ValueTask AbortUpdate()
Attempt to abort an uncommitted update.A ValueTask representing the running operation....
+
volatile? SwarmUpdateOperation updateOperation
A SwarmUpdateOperation that is currently in progress.
+
Guid? controllerRegistration
The registration Guid provided by the swarm controller.
+
DateTimeOffset? lastControllerHealthCheck
The last DateTimeOffset when the controller checked on this node.
+
readonly IServerUpdater serverUpdater
The IServerUpdater for the SwarmService.
+
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the SwarmService.
-
bool TriggerHealthCheck()
Complete the current forceHealthCheckTcs.
-
async ValueTask HealthCheckNodes(CancellationToken cancellationToken)
Ping each node to see that they are still running.
+
bool TriggerHealthCheck()
Complete the current forceHealthCheckTcs.
+
async ValueTask HealthCheckNodes(CancellationToken cancellationToken)
Ping each node to see that they are still running.
Represents the state of a distributed swarm update.
Version TargetVersion
The Version being updated to.
IReadOnlyList< SwarmServerInformation > InvolvedServers
All of the SwarmServers that are involved in the updates.
@@ -1618,6 +1666,8 @@
IFileStreamProvider that provides MemoryStreams.
bool Disposed
If the ISeekableFileStreamProvider has had global::System.IAsyncDisposable.DisposeAsync called on it.
ValueTask< MemoryStream > GetOwnedResult(CancellationToken cancellationToken)
Gets the provided MemoryStream. May be called multiple times, though cancelling any may cause all cal...
+ +
ReadOnlySpan< byte > SigningKeyBytes
Gets or sets the ITokenFactory's signing key bytes.
Swarm service operations for the Controllers.SwarmController.
Start and stop controllers for a swarm service.
Used for swarm operations. Functions may be no-op based on configuration.
@@ -1635,6 +1685,8 @@
ServerUpdateResult
The result of a call to start a server update.
+ +
SwarmUpdateAbortResult
Result of attempting to abort a SwarmUpdateOperation.
SwarmCommitResult
How to proceed on the commit step of an update.
diff --git a/_token_factory_8cs_source.html b/_token_factory_8cs_source.html index 3b1d36a201..c5366010a3 100644 --- a/_token_factory_8cs_source.html +++ b/_token_factory_8cs_source.html @@ -84,124 +84,140 @@
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
-
3using System.Globalization;
-
4using System.IdentityModel.Tokens.Jwt;
-
5using System.Linq;
-
6using System.Security.Claims;
-
7
-
8using Microsoft.Extensions.Options;
-
9using Microsoft.IdentityModel.Tokens;
-
10
- - - - -
15
- -
17{
-
- -
20 {
-
22 public TokenValidationParameters ValidationParameters { get; }
-
23
- -
28
-
32 readonly JwtHeader tokenHeader;
-
33
-
37 readonly JwtSecurityTokenHandler tokenHandler;
-
38
-
- -
46 ICryptographySuite cryptographySuite,
-
47 IAssemblyInformationProvider assemblyInformationProvider,
-
48 IOptions<SecurityConfiguration> securityConfigurationOptions)
-
49 {
-
50 ArgumentNullException.ThrowIfNull(cryptographySuite);
-
51 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
-
52
-
53 securityConfiguration = securityConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
-
54
-
55 var signingKeyBytes = String.IsNullOrWhiteSpace(securityConfiguration.CustomTokenSigningKeyBase64)
- -
57 : Convert.FromBase64String(securityConfiguration.CustomTokenSigningKeyBase64);
-
58
-
59 ValidationParameters = new TokenValidationParameters
-
60 {
-
61 ValidateIssuerSigningKey = true,
-
62 IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes),
-
63
-
64 ValidateIssuer = true,
-
65 ValidIssuer = assemblyInformationProvider.AssemblyName.Name,
-
66
-
67 ValidateLifetime = true,
-
68 ValidateAudience = true,
-
69 ValidAudience = typeof(TokenResponse).Assembly.GetName().Name,
-
70
-
71 ClockSkew = TimeSpan.FromMinutes(securityConfiguration.TokenClockSkewMinutes),
-
72
-
73 RequireSignedTokens = true,
+
3using System.Diagnostics.CodeAnalysis;
+
4using System.Globalization;
+
5using System.IdentityModel.Tokens.Jwt;
+
6using System.Linq;
+
7using System.Security.Claims;
+
8
+
9using Microsoft.Extensions.Options;
+
10using Microsoft.IdentityModel.Tokens;
+
11
+ + + + +
16
+ +
18{
+
+ +
21 {
+
23 public TokenValidationParameters ValidationParameters { get; }
+
24
+
+
26 public ReadOnlySpan<byte> SigningKeyBytes
+
27 {
+
28 get => signingKey.Key;
+
29 [MemberNotNull(nameof(signingKey))]
+
30 [MemberNotNull(nameof(tokenHeader))]
+
31 set
+
32 {
+
33 signingKey = new SymmetricSecurityKey(value.ToArray());
+
34 tokenHeader = new JwtHeader(
+
35 new SigningCredentials(
+ +
37 SecurityAlgorithms.HmacSha256));
+
38 }
+
39 }
+
+
40
+ +
45
+
49 readonly JwtSecurityTokenHandler tokenHandler;
+
50
+
54 SymmetricSecurityKey signingKey;
+
55
+
59 JwtHeader tokenHeader;
+
60
+
+ +
68 ICryptographySuite cryptographySuite,
+
69 IAssemblyInformationProvider assemblyInformationProvider,
+
70 IOptions<SecurityConfiguration> securityConfigurationOptions)
+
71 {
+
72 ArgumentNullException.ThrowIfNull(cryptographySuite);
+
73 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
74
-
75 RequireExpirationTime = true,
-
76 };
-
77
-
78 tokenHeader = new JwtHeader(
-
79 new SigningCredentials(
-
80 ValidationParameters.IssuerSigningKey,
-
81 SecurityAlgorithms.HmacSha256));
-
82 tokenHandler = new JwtSecurityTokenHandler();
-
83 }
+
75 securityConfiguration = securityConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
+
76
+ + +
79 : Convert.FromBase64String(securityConfiguration.CustomTokenSigningKeyBase64);
+
80
+
81 ValidationParameters = new TokenValidationParameters
+
82 {
+
83 ValidateIssuerSigningKey = true,
+
84 IssuerSigningKeyResolver = (_, _, _, _) => Enumerable.Repeat(signingKey, 1),
+
85
+
86 ValidateIssuer = true,
+
87 ValidIssuer = assemblyInformationProvider.AssemblyName.Name,
+
88
+
89 ValidateLifetime = true,
+
90 ValidateAudience = true,
+
91 ValidAudience = typeof(TokenResponse).Assembly.GetName().Name,
+
92
+
93 ClockSkew = TimeSpan.FromMinutes(securityConfiguration.TokenClockSkewMinutes),
+
94
+
95 RequireSignedTokens = true,
+
96
+
97 RequireExpirationTime = true,
+
98 };
+
99
+
100 tokenHandler = new JwtSecurityTokenHandler();
+
101 }
-
84
-
-
86 public TokenResponse CreateToken(User user, bool oAuth)
-
87 {
-
88 ArgumentNullException.ThrowIfNull(user);
-
89
-
90 var uid = user.Require(x => x.Id);
-
91 var now = DateTimeOffset.UtcNow;
-
92 var nowUnix = now.ToUnixTimeSeconds();
-
93
-
94 // this prevents validation conflicts down the line
-
95 // tldr we can (theoretically) receive a token the same second after we generate it
-
96 // since unix time rounds down, it looks like it came from before the user changed their password
-
97 // this happens occasionally in unit tests
-
98 // just delay a second so we can force a round up
-
99 var userLastPassworUpdateUnix = user.LastPasswordUpdate?.ToUnixTimeSeconds();
-
100 DateTimeOffset notBefore;
-
101 if (nowUnix == userLastPassworUpdateUnix)
-
102 notBefore = now.AddSeconds(1);
-
103 else
-
104 notBefore = now;
-
105
-
106 var expiry = now.AddMinutes(oAuth
- - -
109
-
110 var securityToken = new JwtSecurityToken(
- -
112 new JwtPayload(
-
113 ValidationParameters.ValidIssuer,
-
114 ValidationParameters.ValidAudience,
-
115 Enumerable.Empty<Claim>(),
-
116 new Dictionary<string, object>
-
117 {
-
118 { JwtRegisteredClaimNames.Sub, uid.ToString(CultureInfo.InvariantCulture) },
-
119 },
-
120 notBefore.UtcDateTime,
-
121 expiry.UtcDateTime,
-
122 now.UtcDateTime));
+
102
+
+
104 public TokenResponse CreateToken(User user, bool oAuth)
+
105 {
+
106 ArgumentNullException.ThrowIfNull(user);
+
107
+
108 var uid = user.Require(x => x.Id);
+
109 var now = DateTimeOffset.UtcNow;
+
110 var nowUnix = now.ToUnixTimeSeconds();
+
111
+
112 // this prevents validation conflicts down the line
+
113 // tldr we can (theoretically) receive a token the same second after we generate it
+
114 // since unix time rounds down, it looks like it came from before the user changed their password
+
115 // this happens occasionally in unit tests
+
116 // just delay a second so we can force a round up
+
117 var userLastPassworUpdateUnix = user.LastPasswordUpdate?.ToUnixTimeSeconds();
+
118 DateTimeOffset notBefore;
+
119 if (nowUnix == userLastPassworUpdateUnix)
+
120 notBefore = now.AddSeconds(1);
+
121 else
+
122 notBefore = now;
123
-
124 var tokenResponse = new TokenResponse
-
125 {
-
126 Bearer = tokenHandler.WriteToken(securityToken),
-
127 };
-
128
-
129 return tokenResponse;
-
130 }
+
124 var expiry = now.AddMinutes(oAuth
+ + +
127
+
128 var securityToken = new JwtSecurityToken(
+ +
130 new JwtPayload(
+
131 ValidationParameters.ValidIssuer,
+
132 ValidationParameters.ValidAudience,
+
133 Enumerable.Empty<Claim>(),
+
134 new Dictionary<string, object>
+
135 {
+
136 { JwtRegisteredClaimNames.Sub, uid.ToString(CultureInfo.InvariantCulture) },
+
137 },
+
138 notBefore.UtcDateTime,
+
139 expiry.UtcDateTime,
+
140 now.UtcDateTime));
+
141
+
142 var tokenResponse = new TokenResponse
+
143 {
+
144 Bearer = tokenHandler.WriteToken(securityToken),
+
145 };
+
146
+
147 return tokenResponse;
+
148 }
-
131 }
+
149 }
-
132}
+
150}
Represents a JWT returned by the API.
Configuration options pertaining to user security.
uint TokenSigningKeyByteCount
Amount of bytes to use in the Microsoft.IdentityModel.Tokens.TokenValidationParameters....
@@ -211,16 +227,18 @@
uint TokenExpiryMinutes
Amount of minutes until Api.Models.Response.TokenResponses generated from passwords expire.
DateTimeOffset? LastPasswordUpdate
When PasswordHash was last changed.
Definition User.cs:54
- -
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
-
readonly JwtHeader tokenHeader
The JwtHeader for generating tokens.
-
readonly JwtSecurityTokenHandler tokenHandler
The JwtSecurityTokenHandler used to generate TokenResponse.Bearer strings.
-
readonly SecurityConfiguration securityConfiguration
The SecurityConfiguration for the TokenFactory.
-
TokenFactory(ICryptographySuite cryptographySuite, IAssemblyInformationProvider assemblyInformationProvider, IOptions< SecurityConfiguration > securityConfigurationOptions)
Initializes a new instance of the TokenFactory class.
-
TokenResponse CreateToken(User user, bool oAuth)
Create a TokenResponse for a given user .A new TokenResponse.
+ +
SymmetricSecurityKey signingKey
Backing field for SigningKeyBytes.
+
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
+
ReadOnlySpan< byte > SigningKeyBytes
Gets or sets the ITokenFactory's signing key bytes.
+
readonly JwtSecurityTokenHandler tokenHandler
The JwtSecurityTokenHandler used to generate TokenResponse.Bearer strings.
+
readonly SecurityConfiguration securityConfiguration
The SecurityConfiguration for the TokenFactory.
+
TokenFactory(ICryptographySuite cryptographySuite, IAssemblyInformationProvider assemblyInformationProvider, IOptions< SecurityConfiguration > securityConfigurationOptions)
Initializes a new instance of the TokenFactory class.
+
TokenResponse CreateToken(User user, bool oAuth)
Create a TokenResponse for a given user .A new TokenResponse.
+
JwtHeader tokenHeader
The JwtHeader for generating tokens.
Contains various cryptographic functions.
byte[] GetSecureBytes(uint amount)
Generates a secure set of bytes.
- +
AssemblyName AssemblyName
Gets the global::System.Reflection.AssemblyName.
diff --git a/_web_host_builder_extensions_8cs_source.html b/_web_host_builder_extensions_8cs_source.html index 8320c888aa..efdb173dbd 100644 --- a/_web_host_builder_extensions_8cs_source.html +++ b/_web_host_builder_extensions_8cs_source.html @@ -150,7 +150,7 @@
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
Provides access to the server's HttpApiPort.
Interface for using filesystems.
Definition IIOManager.cs:13
- +
Set of objects needed to configure an Core.Application.
diff --git a/annotated.html b/annotated.html index 5fcea83066..9e201ccbcd 100644 --- a/annotated.html +++ b/annotated.html @@ -818,10 +818,11 @@
- - - - + + + + + diff --git a/changelog.yml b/changelog.yml index fb8bb4904d..6d1e2d3970 100644 --- a/changelog.yml +++ b/changelog.yml @@ -158,8 +158,22 @@ Components: PullRequest: 1070 Core: - Version: 6.11.0 - ComponentVersions: {} - Changes: [] + ComponentVersions: + HttpApi: 10.9.0 + DreamMakerApi: 7.3.0 + Configuration: 5.2.0 + InteropApi: 5.10.0 + HostWatchdog: 1.5.0 + NugetCommon: 7.0.0 + NugetApi: 15.0.0 + NugetClient: 18.0.0 + WebControlPanel: 6.2.0 + Changes: + - Descriptions: + - Swarm server nodes now inherit the token signing key of the controller, meaning that an authentication token generated on one server will work for all servers in the swarm. This behavior occurs for all swarm nodes regardless of `Security` configuration settings. + - The swarm protocol is now versioned separately from the TGS core version. This allows for slightly differing servers to group together in a swarm. Updates will still synchronize all servers to the same version. **It is still recommended that all servers in the swarm run the same TGS version.** + Author: Cyberboss + PullRequest: 1929 Unreleased: true - Version: 6.10.1 ComponentVersions: {} @@ -4631,27 +4645,6 @@ Components: Author: Cyberboss PullRequest: 1636 NugetApi: - - Version: 15.0.0 - Changes: - - Descriptions: - - '**BREAKING:** Moved `RepositorySettings` out of `Internal` namespace and made abstract.' - - Added TGS encoded app private key prefix to `RepositorySettings`. - - Added `DreamDaemonApiBase.LaunchTime`. - - Added `DreamDaemonApiBase.ClientCount`. - Author: Cyberboss - PullRequest: 1920 - - Descriptions: - - Added `Tgstation.Server.Api.Models.DMApiValidationMode`. - - Added `DreamMakerSettings.DMApiValidationMode`. - - '**Deprecation:** Make `DreamMakerSettings.RequireDMApiValidation` obsolete. Use `DreamMakerSettings.DMApiValidationMode` instead.' - Author: Cyberboss - PullRequest: 1923 - - Descriptions: - - '**BREAKING**: Reorganized the internal structure of several classes. Sum of parts remains unchanged.' - - '**BREAKING**: `SwarmServerResponse` can no longer be constructed directly. Instantiate one of its child classes instead.' - Author: Cyberboss - PullRequest: 1924 - Unreleased: true - Version: 13.5.0 Changes: - Descriptions: @@ -4735,20 +4728,4 @@ Components: - Made `DiscordConnectionStringBuilder.BasedMeme` obsolete. Author: Cyberboss PullRequest: 1629 - NugetClient: - - Version: 18.0.0 - Changes: - - Descriptions: - - '**BREAKING:** Updated API definitions library to 14.0.0.' - Author: Cyberboss - PullRequest: 1920 - - Descriptions: - - Updated `Tgstation.Server.Api` dependency to 14.1.0 for TGS API version 10.9.0. - Author: Cyberboss - PullRequest: 1923 - - Descriptions: - - '**BREAKING**: Renamed types similar to "ServerClient" to "RestServerClient"' - - Added `ITransferClient` and `IRestServerClient.Transfer` to access the underlying raw file transfer API. - Author: Cyberboss - PullRequest: 1924 - Unreleased: true + NugetClient: [] diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_swarm_server.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_swarm_server.html index 8a87cc3b0a..ff2d253240 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_swarm_server.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_swarm_server.html @@ -274,7 +274,7 @@

Definition at line 15 of file SwarmServer.cs.

15{ get; set; }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.PrepareSwarmRequest(), Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), and Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.PrepareSwarmRequest(), Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), and Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer().

@@ -303,7 +303,7 @@

Definition at line 26 of file SwarmServer.cs.

26{ get; set; }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Controllers.InstanceController.Create(), Tgstation.Server.Host.Security.AuthenticationContextFactory.CreateAuthenticationContext(), Tgstation.Server.Host.Controllers.InstanceController.CreateDefaultInstance(), Tgstation.Server.Host.Controllers.InstanceController.Delete(), Tgstation.Server.Host.Utils.PortAllocator.GetAvailablePort(), Tgstation.Server.Host.Controllers.InstanceController.GetId(), Tgstation.Server.Host.Controllers.InstanceController.GrantPermissions(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.InstanceManager.Initialize(), Tgstation.Server.Host.Swarm.SwarmService.Initialize(), Tgstation.Server.Host.Controllers.InstanceController.List(), Tgstation.Server.Host.Swarm.SwarmService.PrepareSwarmRequest(), Tgstation.Server.Host.Swarm.SwarmService.PrepareUpdateImpl(), Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), Tgstation.Server.Host.Database.DatabaseSeeder.SanitizeDatabase(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer(), Tgstation.Server.Host.Swarm.SwarmService.SwarmService(), Tgstation.Server.Host.Controllers.InstanceController.Update(), and Tgstation.Server.Host.Extensions.ApplicationBuilderExtensions.UseAdditionalRequestLoggingContext().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Controllers.InstanceController.Create(), Tgstation.Server.Host.Security.AuthenticationContextFactory.CreateAuthenticationContext(), Tgstation.Server.Host.Controllers.InstanceController.CreateDefaultInstance(), Tgstation.Server.Host.Controllers.InstanceController.Delete(), Tgstation.Server.Host.Utils.PortAllocator.GetAvailablePort(), Tgstation.Server.Host.Controllers.InstanceController.GetId(), Tgstation.Server.Host.Controllers.InstanceController.GrantPermissions(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.InstanceManager.Initialize(), Tgstation.Server.Host.Swarm.SwarmService.Initialize(), Tgstation.Server.Host.Controllers.InstanceController.List(), Tgstation.Server.Host.Swarm.SwarmService.PrepareSwarmRequest(), Tgstation.Server.Host.Swarm.SwarmService.PrepareUpdateImpl(), Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), Tgstation.Server.Host.Database.DatabaseSeeder.SanitizeDatabase(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer(), Tgstation.Server.Host.Swarm.SwarmService.SwarmService(), Tgstation.Server.Host.Controllers.InstanceController.Update(), and Tgstation.Server.Host.Extensions.ApplicationBuilderExtensions.UseAdditionalRequestLoggingContext().

@@ -332,7 +332,7 @@

Definition at line 20 of file SwarmServer.cs.

20{ get; set; }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), and Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.RegisterNode(), and Tgstation.Server.Api.Models.Internal.SwarmServer.SwarmServer().

diff --git a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html index b02be95352..9bde79d822 100644 --- a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html +++ b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html @@ -165,7 +165,7 @@

118 } -

Referenced by Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanRegisteredCompileJob(), Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanUnusedCompileJobs(), Tgstation.Server.Host.Components.Deployment.DreamMaker.CleanupFailedCompile(), Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Components.Deployment.DreamMaker.DeploymentProcess(), Tgstation.Server.Client.ApiClient.DisposeAsync(), Tgstation.Server.Host.Security.IdentityCache.DisposeAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.EnsureDirectories(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.HandleEventImpl(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.Engine.OpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsOpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.PosixByondInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsByondInstaller.Install(), Tgstation.Server.Host.Components.InstanceManager.OfflineInstance(), Tgstation.Server.Host.Components.Deployment.Remote.BaseRemoteDeploymentManager.PostDeploymentComments(), Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate(), Tgstation.Server.Host.Components.Chat.ChatManager.RemoveProviderChannels(), Tgstation.Server.Host.Server.RestartImpl(), Tgstation.Server.Client.ApiClient.RunRequest< TResult >(), Tgstation.Server.Host.Components.Chat.ChatManager.SendMessage(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Host.Swarm.SwarmService.Shutdown(), Tgstation.Server.Host.Components.Chat.ChatManager.StartAsync(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), Tgstation.Server.Host.Components.InstanceManager.StopAsync(), Tgstation.Server.Host.Jobs.JobService.StopAsync(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.SymlinkStaticFilesTo().

+

Referenced by Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanRegisteredCompileJob(), Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanUnusedCompileJobs(), Tgstation.Server.Host.Components.Deployment.DreamMaker.CleanupFailedCompile(), Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Components.Deployment.DreamMaker.DeploymentProcess(), Tgstation.Server.Client.ApiClient.DisposeAsync(), Tgstation.Server.Host.Security.IdentityCache.DisposeAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.EnsureDirectories(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.HandleEventImpl(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.Engine.OpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsOpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.PosixByondInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsByondInstaller.Install(), Tgstation.Server.Host.Components.InstanceManager.OfflineInstance(), Tgstation.Server.Host.Components.Deployment.Remote.BaseRemoteDeploymentManager.PostDeploymentComments(), Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate(), Tgstation.Server.Host.Components.Chat.ChatManager.RemoveProviderChannels(), Tgstation.Server.Host.Server.RestartImpl(), Tgstation.Server.Client.ApiClient.RunRequest< TResult >(), Tgstation.Server.Host.Components.Chat.ChatManager.SendMessage(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Host.Swarm.SwarmService.Shutdown(), Tgstation.Server.Host.Components.Chat.ChatManager.StartAsync(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), Tgstation.Server.Host.Components.InstanceManager.StopAsync(), Tgstation.Server.Host.Jobs.JobService.StopAsync(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.SymlinkStaticFilesTo().

Here is the caller graph for this function:
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_security_configuration.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_security_configuration.html index 67f8234836..34109d1253 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_security_configuration.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_security_configuration.html @@ -336,7 +336,7 @@

Definition at line 63 of file SecurityConfiguration.cs.

63{ get; set; }
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

@@ -409,7 +409,7 @@

const uint DefaultOAuthTokenExpiryMinutes
Default value of OAuthTokenExpiryMinutes.
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

@@ -439,7 +439,7 @@

const uint DefaultTokenClockSkewMinutes
Default value of TokenClockSkewMinutes.
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

@@ -469,7 +469,7 @@

const uint DefaultTokenExpiryMinutes
Default value of TokenExpiryMinutes.
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

@@ -499,7 +499,7 @@

const uint DefaultTokenSigningKeyByteAmount
Default value of TokenSigningKeyByteCount.
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.TokenFactory().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_swarm_configuration.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_swarm_configuration.html index f5559c4003..3dcaa4fb9d 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_swarm_configuration.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_swarm_configuration.html @@ -218,7 +218,7 @@

24 set => base.Address = value;
25 }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.SwarmService().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.SwarmService().

@@ -247,7 +247,7 @@

Definition at line 39 of file SwarmConfiguration.cs.

39{ get; set; }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate().

@@ -276,7 +276,7 @@

Definition at line 44 of file SwarmConfiguration.cs.

44{ get; set; }
-

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction().

+

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction().

@@ -308,7 +308,7 @@

32 set => base.PublicAddress = value;
33 }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.SwarmService().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.SwarmService().

@@ -337,7 +337,7 @@

Definition at line 49 of file SwarmConfiguration.cs.

49{ get; set; }
-

Referenced by Tgstation.Server.Host.Swarm.SwarmService.ControllerDistributedPrepareUpdate(), and Tgstation.Server.Host.Swarm.SwarmService.Initialize().

+

Referenced by Tgstation.Server.Host.Swarm.SwarmService.ControllerDistributedPrepareUpdate(), and Tgstation.Server.Host.Swarm.SwarmService.Initialize().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_api_root_controller.html b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_api_root_controller.html index 883c40db86..239d4a92f5 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_api_root_controller.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_api_root_controller.html @@ -638,11 +638,11 @@

string Uid
A unique identifier for the user.
TokenResponse CreateToken(Models.User user, bool oAuth)
Create a TokenResponse for a given user .
-
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
+
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
IOAuthValidator? GetValidator(OAuthProvider oAuthProvider)
Gets the IOAuthValidator for a given oAuthProvider .
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
-

References Tgstation.Server.Host.Security.IIdentityCache.CacheSystemIdentity(), Tgstation.Server.Host.Security.ICryptographySuite.CheckUserPassword(), Tgstation.Server.Host.Security.ISystemIdentityFactory.CreateSystemIdentity(), Tgstation.Server.Host.Security.ITokenFactory.CreateToken(), Tgstation.Server.Host.Controllers.ApiRootController.cryptographySuite, Tgstation.Server.Host.Security.OAuth.IOAuthProviders.GetValidator(), Tgstation.Server.Host.Utils.ApiHeadersProvider.HeadersException, Tgstation.Server.Host.Controllers.ApiController.HeadersIssue(), Tgstation.Server.Api.Models.EntityId.Id, Tgstation.Server.Host.Controllers.ApiRootController.identityCache, Tgstation.Server.Api.ApiHeaders.IsTokenAuthentication, Tgstation.Server.Host.Controllers.ApiController.Logger, Tgstation.Server.Api.ApiHeaders.OAuthCode, Tgstation.Server.Api.ApiHeaders.OAuthProvider, Tgstation.Server.Host.Controllers.ApiRootController.oAuthProviders, Tgstation.Server.Api.Models.Response.TokenResponse.ParseJwt(), Tgstation.Server.Api.ApiHeaders.Password, Tgstation.Server.Host.Controllers.ApiController.RateLimit(), Tgstation.Server.Host.Database.DatabaseContext.Save(), Tgstation.Server.Host.Controllers.ApiRootController.systemIdentityFactory, Tgstation.Server.Host.Controllers.ApiRootController.tokenFactory, Tgstation.Server.Host.Security.ISystemIdentity.Uid, Tgstation.Server.Host.Controllers.ApiController.Unauthorized(), Tgstation.Server.Api.ApiHeaders.Username, Tgstation.Server.Host.Security.ISystemIdentity.Username, Tgstation.Server.Host.Database.DatabaseContext.Users, and Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters.

+

References Tgstation.Server.Host.Security.IIdentityCache.CacheSystemIdentity(), Tgstation.Server.Host.Security.ICryptographySuite.CheckUserPassword(), Tgstation.Server.Host.Security.ISystemIdentityFactory.CreateSystemIdentity(), Tgstation.Server.Host.Security.ITokenFactory.CreateToken(), Tgstation.Server.Host.Controllers.ApiRootController.cryptographySuite, Tgstation.Server.Host.Security.OAuth.IOAuthProviders.GetValidator(), Tgstation.Server.Host.Utils.ApiHeadersProvider.HeadersException, Tgstation.Server.Host.Controllers.ApiController.HeadersIssue(), Tgstation.Server.Api.Models.EntityId.Id, Tgstation.Server.Host.Controllers.ApiRootController.identityCache, Tgstation.Server.Api.ApiHeaders.IsTokenAuthentication, Tgstation.Server.Host.Controllers.ApiController.Logger, Tgstation.Server.Api.ApiHeaders.OAuthCode, Tgstation.Server.Api.ApiHeaders.OAuthProvider, Tgstation.Server.Host.Controllers.ApiRootController.oAuthProviders, Tgstation.Server.Api.Models.Response.TokenResponse.ParseJwt(), Tgstation.Server.Api.ApiHeaders.Password, Tgstation.Server.Host.Controllers.ApiController.RateLimit(), Tgstation.Server.Host.Database.DatabaseContext.Save(), Tgstation.Server.Host.Controllers.ApiRootController.systemIdentityFactory, Tgstation.Server.Host.Controllers.ApiRootController.tokenFactory, Tgstation.Server.Host.Security.ISystemIdentity.Uid, Tgstation.Server.Host.Controllers.ApiController.Unauthorized(), Tgstation.Server.Api.ApiHeaders.Username, Tgstation.Server.Host.Security.ISystemIdentity.Username, Tgstation.Server.Host.Database.DatabaseContext.Users, and Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters.

Referenced by Tgstation.Server.Host.Utils.SwaggerConfiguration.Apply().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller-members.html b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller-members.html index eb57154cdd..622d485bb9 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller-members.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller-members.html @@ -81,22 +81,21 @@

This is the complete list of members for Tgstation.Server.Host.Controllers.SwarmController, including all inherited members.

Enumerations

enum  Tgstation.Server.Host.Swarm.SwarmRegistrationResult { Tgstation.Server.Host.Swarm.Success +
enum  Tgstation.Server.Host.Swarm.SwarmRegistrationResult {
+  Tgstation.Server.Host.Swarm.Success , Tgstation.Server.Host.Swarm.Unauthorized , Tgstation.Server.Host.Swarm.VersionMismatch , Tgstation.Server.Host.Swarm.CommunicationFailure +,
+  Tgstation.Server.Host.Swarm.PayloadFailure +
}
 Result of attempting to register with a swarm controller. More...
 
- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +
AbortUpdate()Tgstation.Server.Host.Controllers.SwarmController
assemblyInformationProviderTgstation.Server.Host.Controllers.SwarmControllerprivate
CommitUpdate(CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
GetUpdatePackage([FromQuery] string ticket, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
HealthCheck()Tgstation.Server.Host.Controllers.SwarmController
HookExecuteAction(Func< Task > executeAction, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmControllerprotectedvirtual
loggerTgstation.Server.Host.Controllers.SwarmControllerprivate
OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)Tgstation.Server.Host.Controllers.ApiControllerBase
PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
swarmConfigurationTgstation.Server.Host.Controllers.SwarmControllerprivate
SwarmController(ISwarmOperations swarmOperations, IAssemblyInformationProvider assemblyInformationProvider, IFileTransferStreamHandler transferService, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmController > logger)Tgstation.Server.Host.Controllers.SwarmController
swarmOperationsTgstation.Server.Host.Controllers.SwarmControllerprivate
transferServiceTgstation.Server.Host.Controllers.SwarmControllerprivate
UnregisterNode(CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)Tgstation.Server.Host.Controllers.SwarmController
ValidateRegistration()Tgstation.Server.Host.Controllers.SwarmControllerprivate
CommitUpdate(CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
GetUpdatePackage([FromQuery] string ticket, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
HealthCheck()Tgstation.Server.Host.Controllers.SwarmController
HookExecuteAction(Func< Task > executeAction, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmControllerprotectedvirtual
loggerTgstation.Server.Host.Controllers.SwarmControllerprivate
OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)Tgstation.Server.Host.Controllers.ApiControllerBase
PrepareUpdate([FromBody] SwarmUpdateRequest updateRequest, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
Register([FromBody] SwarmRegistrationRequest registrationRequest, CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
swarmConfigurationTgstation.Server.Host.Controllers.SwarmControllerprivate
SwarmController(ISwarmOperations swarmOperations, IFileTransferStreamHandler transferService, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< SwarmController > logger)Tgstation.Server.Host.Controllers.SwarmController
swarmOperationsTgstation.Server.Host.Controllers.SwarmControllerprivate
transferServiceTgstation.Server.Host.Controllers.SwarmControllerprivate
UnregisterNode(CancellationToken cancellationToken)Tgstation.Server.Host.Controllers.SwarmController
UpdateNodeList([FromBody] SwarmServersUpdateRequest serversUpdateRequest)Tgstation.Server.Host.Controllers.SwarmController
ValidateRegistration()Tgstation.Server.Host.Controllers.SwarmControllerprivate
- -

The IAssemblyInformationProvider for the SwarmController.

- -

Definition at line 50 of file SwarmController.cs.

- -

Referenced by Tgstation.Server.Host.Controllers.SwarmController.Register(), and Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

- -
-

◆ logger

@@ -902,9 +868,9 @@

Definition at line 55 of file SwarmController.cs.

+

Definition at line 50 of file SwarmController.cs.

-

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction(), and Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

+

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction(), Tgstation.Server.Host.Controllers.SwarmController.Register(), and Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

@@ -930,9 +896,9 @@

Definition at line 60 of file SwarmController.cs.

+

Definition at line 55 of file SwarmController.cs.

-

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction(), and Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

+

Referenced by Tgstation.Server.Host.Controllers.SwarmController.HookExecuteAction(), and Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

@@ -960,7 +926,7 @@

Definition at line 40 of file SwarmController.cs.

-

Referenced by Tgstation.Server.Host.Controllers.SwarmController.AbortUpdate(), Tgstation.Server.Host.Controllers.SwarmController.CommitUpdate(), Tgstation.Server.Host.Controllers.SwarmController.PrepareUpdate(), Tgstation.Server.Host.Controllers.SwarmController.Register(), Tgstation.Server.Host.Controllers.SwarmController.SwarmController(), Tgstation.Server.Host.Controllers.SwarmController.UnregisterNode(), and Tgstation.Server.Host.Controllers.SwarmController.UpdateNodeList().

+

Referenced by Tgstation.Server.Host.Controllers.SwarmController.AbortUpdate(), Tgstation.Server.Host.Controllers.SwarmController.CommitUpdate(), Tgstation.Server.Host.Controllers.SwarmController.PrepareUpdate(), Tgstation.Server.Host.Controllers.SwarmController.Register(), Tgstation.Server.Host.Controllers.SwarmController.SwarmController(), Tgstation.Server.Host.Controllers.SwarmController.UnregisterNode(), and Tgstation.Server.Host.Controllers.SwarmController.UpdateNodeList().

@@ -988,7 +954,7 @@

Definition at line 45 of file SwarmController.cs.

-

Referenced by Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

+

Referenced by Tgstation.Server.Host.Controllers.SwarmController.SwarmController().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.map b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.map index 52e3be71aa..99720be140 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.map +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.map @@ -1,25 +1,23 @@ - + - + - + - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.md5 b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.md5 index f8450ac690..f41580d0b4 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.md5 +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.md5 @@ -1 +1 @@ -0857f6577ec12301fc0de774dd4eb895 \ No newline at end of file +7971e977b900d4005af2f25cfd722056 \ No newline at end of file diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.png b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.png index 85c81b361b..b2aa3c8c8c 100644 Binary files a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.png and b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller__coll__graph.png differ diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.map b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.map index c34e1b1429..14bbb0cba1 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.map +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.map @@ -1,5 +1,5 @@ - + diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.md5 b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.md5 index 48a2225fcb..de7b8a7233 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.md5 +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_swarm_controller_a39ad629bd3c687676c39af000e7af99f_cgraph.md5 @@ -1 +1 @@ -894e3cbefeec757c7c90222487653c28 \ No newline at end of file +05342734d5bffabcac7860e1d8293f5d \ No newline at end of file diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html index eb3add210d..f115a4971f 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html @@ -654,10 +654,10 @@

-
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
+
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
-

References Tgstation.Server.Api.Routes.HubsRoot, Tgstation.Server.Host.Core.Application.tokenFactory, and Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters.

+

References Tgstation.Server.Api.Routes.HubsRoot, Tgstation.Server.Host.Core.Application.tokenFactory, and Tgstation.Server.Host.Security.ITokenFactory.ValidationParameters.

Referenced by Tgstation.Server.Host.Core.Application.ConfigureServices().

- +
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
IConfiguration Configuration
The IConfiguration for the SetupApplication.
-
Helps keep servers connected to the same database in sync by coordinating updates.
+
Helps keep servers connected to the same database in sync by coordinating updates.
@@ -1181,7 +1181,7 @@

For caching ISystemIdentitys.
Receives notifications about permissions updates.
- +
ElasticsearchConfiguration ElasticsearchConfiguration
The Configuration.ElasticsearchConfiguration.
FileLoggingConfiguration FileLoggingConfiguration
The Configuration.FileLoggingConfiguration.
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_server_update_operation.html b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_server_update_operation.html index c15a52f3d6..f58bea28be 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_server_update_operation.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_server_update_operation.html @@ -158,7 +158,7 @@

39 }
IFileStreamProvider FileStreamProvider
The IFileStreamProvider that contains the update zip file.
Version TargetVersion
The Version being updated to.
-
Helps keep servers connected to the same database in sync by coordinating updates.
+
Helps keep servers connected to the same database in sync by coordinating updates.

References Tgstation.Server.Host.Core.ServerUpdateOperation.FileStreamProvider, and Tgstation.Server.Host.Core.ServerUpdateOperation.TargetVersion.

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context.html b/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context.html index 9168f77008..6593f1f9dc 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context.html @@ -625,185 +625,186 @@

Returns
The name of the migration to run on success, null otherwise.
-

Definition at line 476 of file DatabaseContext.cs.

-
477 {
-
478 // Update this with new migrations as they are made
-
479 string? targetMigration = null;
-
480
-
481 string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
-
482
-
483 if (targetVersion < new Version(6, 7, 0))
-
484 targetMigration = currentDatabaseType switch
-
485 {
-
486 DatabaseType.MySql => nameof(MSAddCronAutoUpdates),
-
487 DatabaseType.PostgresSql => nameof(PGAddCronAutoUpdates),
-
488 DatabaseType.SqlServer => nameof(MSAddCronAutoUpdates),
-
489 DatabaseType.Sqlite => nameof(SLAddCronAutoUpdates),
-
490 _ => BadDatabaseType(),
-
491 };
-
492
-
493 if (targetVersion < new Version(6, 6, 0))
-
494 targetMigration = currentDatabaseType switch
-
495 {
-
496 DatabaseType.MySql => nameof(MYAddCompilerAdditionalArguments),
-
497 DatabaseType.PostgresSql => nameof(PGAddCompilerAdditionalArguments),
-
498 DatabaseType.SqlServer => nameof(MSAddCompilerAdditionalArguments),
-
499 DatabaseType.Sqlite => nameof(SLAddCompilerAdditionalArguments),
-
500 _ => BadDatabaseType(),
-
501 };
-
502
-
503 if (targetVersion < new Version(6, 5, 0))
-
504 targetMigration = currentDatabaseType switch
-
505 {
-
506 DatabaseType.MySql => nameof(MYAddMinidumpsOption),
-
507 DatabaseType.PostgresSql => nameof(PGAddMinidumpsOption),
-
508 DatabaseType.SqlServer => nameof(MSAddMinidumpsOption),
-
509 DatabaseType.Sqlite => nameof(SLAddMinidumpsOption),
-
510 _ => BadDatabaseType(),
-
511 };
-
512
-
513 if (targetVersion < new Version(6, 2, 0))
-
514 targetMigration = currentDatabaseType switch
-
515 {
-
516 DatabaseType.MySql => nameof(MYAddTopicPort),
-
517 DatabaseType.PostgresSql => nameof(PGAddTopicPort),
-
518 DatabaseType.SqlServer => nameof(MSAddTopicPort),
-
519 DatabaseType.Sqlite => nameof(SLAddTopicPort),
-
520 _ => BadDatabaseType(),
-
521 };
-
522
-
523 if (targetVersion < new Version(6, 0, 0))
-
524 targetMigration = currentDatabaseType switch
-
525 {
-
526 DatabaseType.MySql => nameof(MYAddJobCodes),
-
527 DatabaseType.PostgresSql => nameof(PGAddJobCodes),
-
528 DatabaseType.SqlServer => nameof(MSAddJobCodes),
-
529 DatabaseType.Sqlite => nameof(SLAddJobCodes),
-
530 _ => BadDatabaseType(),
-
531 };
-
532 if (targetVersion < new Version(5, 17, 0))
-
533 targetMigration = currentDatabaseType switch
-
534 {
-
535 DatabaseType.MySql => nameof(MYAddMapThreads),
-
536 DatabaseType.PostgresSql => nameof(PGAddMapThreads),
-
537 DatabaseType.SqlServer => nameof(MSAddMapThreads),
-
538 DatabaseType.Sqlite => nameof(SLAddMapThreads),
-
539 _ => BadDatabaseType(),
-
540 };
-
541 if (targetVersion < new Version(5, 13, 0))
-
542 targetMigration = currentDatabaseType switch
-
543 {
-
544 DatabaseType.MySql => nameof(MYAddReattachInfoInitialCompileJob),
-
545 DatabaseType.PostgresSql => nameof(PGAddReattachInfoInitialCompileJob),
-
546 DatabaseType.SqlServer => nameof(MSAddReattachInfoInitialCompileJob),
-
547 DatabaseType.Sqlite => nameof(SLAddReattachInfoInitialCompileJob),
-
548 _ => BadDatabaseType(),
-
549 };
-
550 if (targetVersion < new Version(5, 7, 3))
-
551 targetMigration = currentDatabaseType switch
-
552 {
-
553 DatabaseType.MySql => nameof(MYAddDreamDaemonLogOutput),
-
554 DatabaseType.PostgresSql => nameof(PGAddDreamDaemonLogOutput),
-
555 DatabaseType.SqlServer => nameof(MSAddDreamDaemonLogOutput),
-
556 DatabaseType.Sqlite => nameof(SLAddDreamDaemonLogOutput),
-
557 _ => BadDatabaseType(),
-
558 };
-
559 if (targetVersion < new Version(5, 7, 0))
-
560 targetMigration = currentDatabaseType switch
-
561 {
-
562 DatabaseType.MySql => nameof(MYAddProfiler),
-
563 DatabaseType.PostgresSql => nameof(PGAddProfiler),
-
564 DatabaseType.SqlServer => nameof(MSAddProfiler),
-
565 DatabaseType.Sqlite => nameof(SLAddProfiler),
-
566 _ => BadDatabaseType(),
-
567 };
-
568 if (targetVersion < new Version(4, 19, 0))
-
569 targetMigration = currentDatabaseType switch
-
570 {
-
571 DatabaseType.MySql => nameof(MYAddDumpOnHeartbeatRestart),
-
572 DatabaseType.PostgresSql => nameof(PGAddDumpOnHeartbeatRestart),
-
573 DatabaseType.SqlServer => nameof(MSAddDumpOnHeartbeatRestart),
-
574 DatabaseType.Sqlite => nameof(SLAddDumpOnHeartbeatRestart),
-
575 _ => BadDatabaseType(),
-
576 };
-
577 if (targetVersion < new Version(4, 18, 0))
-
578 targetMigration = currentDatabaseType switch
-
579 {
-
580 DatabaseType.MySql => nameof(MYAddUpdateSubmodules),
-
581 DatabaseType.PostgresSql => nameof(PGAddUpdateSubmodules),
-
582 DatabaseType.SqlServer => nameof(MSAddUpdateSubmodules),
-
583 DatabaseType.Sqlite => nameof(SLAddUpdateSubmodules),
-
584 _ => BadDatabaseType(),
-
585 };
-
586 if (targetVersion < new Version(4, 14, 0))
-
587 targetMigration = currentDatabaseType switch
-
588 {
-
589 DatabaseType.MySql => nameof(MYTruncateInstanceNames),
-
590 DatabaseType.PostgresSql => nameof(PGTruncateInstanceNames),
-
591 DatabaseType.SqlServer => nameof(MSTruncateInstanceNames),
-
592 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
-
593 _ => BadDatabaseType(),
-
594 };
-
595 if (targetVersion < new Version(4, 10, 0))
-
596 targetMigration = currentDatabaseType switch
-
597 {
-
598 DatabaseType.MySql => nameof(MSAddRevInfoTimestamp),
-
599 DatabaseType.PostgresSql => nameof(PGAddRevInfoTimestamp),
-
600 DatabaseType.SqlServer => nameof(MSAddRevInfoTimestamp),
-
601 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
-
602 _ => BadDatabaseType(),
-
603 };
-
604 if (targetVersion < new Version(4, 8, 0))
-
605 targetMigration = currentDatabaseType switch
-
606 {
-
607 DatabaseType.MySql => nameof(MYAddSwarmIdentifer),
-
608 DatabaseType.PostgresSql => nameof(PGAddSwarmIdentifer),
-
609 DatabaseType.SqlServer => nameof(MSAddSwarmIdentifer),
-
610 DatabaseType.Sqlite => nameof(SLAddSwarmIdentifer),
-
611 _ => BadDatabaseType(),
-
612 };
-
613 if (targetVersion < new Version(4, 7, 0))
-
614 targetMigration = currentDatabaseType switch
-
615 {
-
616 DatabaseType.MySql => nameof(MYAddAdditionalDDParameters),
-
617 DatabaseType.PostgresSql => nameof(PGAddAdditionalDDParameters),
-
618 DatabaseType.SqlServer => nameof(MSAddAdditionalDDParameters),
-
619 DatabaseType.Sqlite => nameof(SLAddAdditionalDDParameters),
-
620 _ => BadDatabaseType(),
-
621 };
-
622 if (targetVersion < new Version(4, 6, 0))
-
623 targetMigration = currentDatabaseType switch
-
624 {
-
625 DatabaseType.MySql => nameof(MYAddDeploymentColumns),
-
626 DatabaseType.PostgresSql => nameof(PGAddDeploymentColumns),
-
627 DatabaseType.SqlServer => nameof(MSAddDeploymentColumns),
-
628 DatabaseType.Sqlite => nameof(SLAddDeploymentColumns),
-
629 _ => BadDatabaseType(),
-
630 };
-
631 if (targetVersion < new Version(4, 5, 0))
-
632 targetMigration = currentDatabaseType switch
-
633 {
-
634 DatabaseType.MySql => nameof(MYAllowNullDMApi),
-
635 DatabaseType.PostgresSql => nameof(PGAllowNullDMApi),
-
636 DatabaseType.SqlServer => nameof(MSAllowNullDMApi),
-
637 DatabaseType.Sqlite => nameof(SLAllowNullDMApi),
-
638 _ => BadDatabaseType(),
-
639 };
-
640 if (targetVersion < new Version(4, 4, 0))
-
641 targetMigration = currentDatabaseType switch
-
642 {
-
643 DatabaseType.MySql => nameof(MYFixForeignKey),
-
644 DatabaseType.PostgresSql => nameof(PGCreate),
-
645 DatabaseType.SqlServer => nameof(MSRemoveSoftColumns),
-
646 DatabaseType.Sqlite => nameof(SLRemoveSoftColumns),
-
647 _ => BadDatabaseType(),
-
648 };
-
649
-
650 if (targetVersion < new Version(4, 2, 0))
-
651 targetMigration = currentDatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
-
652
-
653 return targetMigration;
-
654 }
+

Definition at line 477 of file DatabaseContext.cs.

+
478 {
+
479 // Update this with new migrations as they are made
+
480 string? targetMigration = null;
+
481
+
482 string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
+
483
+
484 // !!! DON'T FORGET TO UPDATE THE SWARM PROTOCOL MAJOR VERSION !!!
+
485 if (targetVersion < new Version(6, 7, 0))
+
486 targetMigration = currentDatabaseType switch
+
487 {
+
488 DatabaseType.MySql => nameof(MSAddCronAutoUpdates),
+
489 DatabaseType.PostgresSql => nameof(PGAddCronAutoUpdates),
+
490 DatabaseType.SqlServer => nameof(MSAddCronAutoUpdates),
+
491 DatabaseType.Sqlite => nameof(SLAddCronAutoUpdates),
+
492 _ => BadDatabaseType(),
+
493 };
+
494
+
495 if (targetVersion < new Version(6, 6, 0))
+
496 targetMigration = currentDatabaseType switch
+
497 {
+
498 DatabaseType.MySql => nameof(MYAddCompilerAdditionalArguments),
+
499 DatabaseType.PostgresSql => nameof(PGAddCompilerAdditionalArguments),
+
500 DatabaseType.SqlServer => nameof(MSAddCompilerAdditionalArguments),
+
501 DatabaseType.Sqlite => nameof(SLAddCompilerAdditionalArguments),
+
502 _ => BadDatabaseType(),
+
503 };
+
504
+
505 if (targetVersion < new Version(6, 5, 0))
+
506 targetMigration = currentDatabaseType switch
+
507 {
+
508 DatabaseType.MySql => nameof(MYAddMinidumpsOption),
+
509 DatabaseType.PostgresSql => nameof(PGAddMinidumpsOption),
+
510 DatabaseType.SqlServer => nameof(MSAddMinidumpsOption),
+
511 DatabaseType.Sqlite => nameof(SLAddMinidumpsOption),
+
512 _ => BadDatabaseType(),
+
513 };
+
514
+
515 if (targetVersion < new Version(6, 2, 0))
+
516 targetMigration = currentDatabaseType switch
+
517 {
+
518 DatabaseType.MySql => nameof(MYAddTopicPort),
+
519 DatabaseType.PostgresSql => nameof(PGAddTopicPort),
+
520 DatabaseType.SqlServer => nameof(MSAddTopicPort),
+
521 DatabaseType.Sqlite => nameof(SLAddTopicPort),
+
522 _ => BadDatabaseType(),
+
523 };
+
524
+
525 if (targetVersion < new Version(6, 0, 0))
+
526 targetMigration = currentDatabaseType switch
+
527 {
+
528 DatabaseType.MySql => nameof(MYAddJobCodes),
+
529 DatabaseType.PostgresSql => nameof(PGAddJobCodes),
+
530 DatabaseType.SqlServer => nameof(MSAddJobCodes),
+
531 DatabaseType.Sqlite => nameof(SLAddJobCodes),
+
532 _ => BadDatabaseType(),
+
533 };
+
534 if (targetVersion < new Version(5, 17, 0))
+
535 targetMigration = currentDatabaseType switch
+
536 {
+
537 DatabaseType.MySql => nameof(MYAddMapThreads),
+
538 DatabaseType.PostgresSql => nameof(PGAddMapThreads),
+
539 DatabaseType.SqlServer => nameof(MSAddMapThreads),
+
540 DatabaseType.Sqlite => nameof(SLAddMapThreads),
+
541 _ => BadDatabaseType(),
+
542 };
+
543 if (targetVersion < new Version(5, 13, 0))
+
544 targetMigration = currentDatabaseType switch
+
545 {
+
546 DatabaseType.MySql => nameof(MYAddReattachInfoInitialCompileJob),
+
547 DatabaseType.PostgresSql => nameof(PGAddReattachInfoInitialCompileJob),
+
548 DatabaseType.SqlServer => nameof(MSAddReattachInfoInitialCompileJob),
+
549 DatabaseType.Sqlite => nameof(SLAddReattachInfoInitialCompileJob),
+
550 _ => BadDatabaseType(),
+
551 };
+
552 if (targetVersion < new Version(5, 7, 3))
+
553 targetMigration = currentDatabaseType switch
+
554 {
+
555 DatabaseType.MySql => nameof(MYAddDreamDaemonLogOutput),
+
556 DatabaseType.PostgresSql => nameof(PGAddDreamDaemonLogOutput),
+
557 DatabaseType.SqlServer => nameof(MSAddDreamDaemonLogOutput),
+
558 DatabaseType.Sqlite => nameof(SLAddDreamDaemonLogOutput),
+
559 _ => BadDatabaseType(),
+
560 };
+
561 if (targetVersion < new Version(5, 7, 0))
+
562 targetMigration = currentDatabaseType switch
+
563 {
+
564 DatabaseType.MySql => nameof(MYAddProfiler),
+
565 DatabaseType.PostgresSql => nameof(PGAddProfiler),
+
566 DatabaseType.SqlServer => nameof(MSAddProfiler),
+
567 DatabaseType.Sqlite => nameof(SLAddProfiler),
+
568 _ => BadDatabaseType(),
+
569 };
+
570 if (targetVersion < new Version(4, 19, 0))
+
571 targetMigration = currentDatabaseType switch
+
572 {
+
573 DatabaseType.MySql => nameof(MYAddDumpOnHeartbeatRestart),
+
574 DatabaseType.PostgresSql => nameof(PGAddDumpOnHeartbeatRestart),
+
575 DatabaseType.SqlServer => nameof(MSAddDumpOnHeartbeatRestart),
+
576 DatabaseType.Sqlite => nameof(SLAddDumpOnHeartbeatRestart),
+
577 _ => BadDatabaseType(),
+
578 };
+
579 if (targetVersion < new Version(4, 18, 0))
+
580 targetMigration = currentDatabaseType switch
+
581 {
+
582 DatabaseType.MySql => nameof(MYAddUpdateSubmodules),
+
583 DatabaseType.PostgresSql => nameof(PGAddUpdateSubmodules),
+
584 DatabaseType.SqlServer => nameof(MSAddUpdateSubmodules),
+
585 DatabaseType.Sqlite => nameof(SLAddUpdateSubmodules),
+
586 _ => BadDatabaseType(),
+
587 };
+
588 if (targetVersion < new Version(4, 14, 0))
+
589 targetMigration = currentDatabaseType switch
+
590 {
+
591 DatabaseType.MySql => nameof(MYTruncateInstanceNames),
+
592 DatabaseType.PostgresSql => nameof(PGTruncateInstanceNames),
+
593 DatabaseType.SqlServer => nameof(MSTruncateInstanceNames),
+
594 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
+
595 _ => BadDatabaseType(),
+
596 };
+
597 if (targetVersion < new Version(4, 10, 0))
+
598 targetMigration = currentDatabaseType switch
+
599 {
+
600 DatabaseType.MySql => nameof(MSAddRevInfoTimestamp),
+
601 DatabaseType.PostgresSql => nameof(PGAddRevInfoTimestamp),
+
602 DatabaseType.SqlServer => nameof(MSAddRevInfoTimestamp),
+
603 DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp),
+
604 _ => BadDatabaseType(),
+
605 };
+
606 if (targetVersion < new Version(4, 8, 0))
+
607 targetMigration = currentDatabaseType switch
+
608 {
+
609 DatabaseType.MySql => nameof(MYAddSwarmIdentifer),
+
610 DatabaseType.PostgresSql => nameof(PGAddSwarmIdentifer),
+
611 DatabaseType.SqlServer => nameof(MSAddSwarmIdentifer),
+
612 DatabaseType.Sqlite => nameof(SLAddSwarmIdentifer),
+
613 _ => BadDatabaseType(),
+
614 };
+
615 if (targetVersion < new Version(4, 7, 0))
+
616 targetMigration = currentDatabaseType switch
+
617 {
+
618 DatabaseType.MySql => nameof(MYAddAdditionalDDParameters),
+
619 DatabaseType.PostgresSql => nameof(PGAddAdditionalDDParameters),
+
620 DatabaseType.SqlServer => nameof(MSAddAdditionalDDParameters),
+
621 DatabaseType.Sqlite => nameof(SLAddAdditionalDDParameters),
+
622 _ => BadDatabaseType(),
+
623 };
+
624 if (targetVersion < new Version(4, 6, 0))
+
625 targetMigration = currentDatabaseType switch
+
626 {
+
627 DatabaseType.MySql => nameof(MYAddDeploymentColumns),
+
628 DatabaseType.PostgresSql => nameof(PGAddDeploymentColumns),
+
629 DatabaseType.SqlServer => nameof(MSAddDeploymentColumns),
+
630 DatabaseType.Sqlite => nameof(SLAddDeploymentColumns),
+
631 _ => BadDatabaseType(),
+
632 };
+
633 if (targetVersion < new Version(4, 5, 0))
+
634 targetMigration = currentDatabaseType switch
+
635 {
+
636 DatabaseType.MySql => nameof(MYAllowNullDMApi),
+
637 DatabaseType.PostgresSql => nameof(PGAllowNullDMApi),
+
638 DatabaseType.SqlServer => nameof(MSAllowNullDMApi),
+
639 DatabaseType.Sqlite => nameof(SLAllowNullDMApi),
+
640 _ => BadDatabaseType(),
+
641 };
+
642 if (targetVersion < new Version(4, 4, 0))
+
643 targetMigration = currentDatabaseType switch
+
644 {
+
645 DatabaseType.MySql => nameof(MYFixForeignKey),
+
646 DatabaseType.PostgresSql => nameof(PGCreate),
+
647 DatabaseType.SqlServer => nameof(MSRemoveSoftColumns),
+
648 DatabaseType.Sqlite => nameof(SLRemoveSoftColumns),
+
649 _ => BadDatabaseType(),
+
650 };
+
651
+
652 if (targetVersion < new Version(4, 2, 0))
+
653 targetMigration = currentDatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
+
654
+
655 return targetMigration;
+
656 }
@@ -1249,10 +1250,10 @@

375 }

376 }
-
string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
Gets the name of the migration to run for migrating down to a given targetVersion for the currentDat...
+
string? GetTargetMigration(Version targetVersion, DatabaseType currentDatabaseType)
Gets the name of the migration to run for migrating down to a given targetVersion for the currentDat...
DatabaseType
Type of database to user.
-

References Tgstation.Server.Host.Database.DatabaseContext.GetTargetMigration().

+

References Tgstation.Server.Host.Database.DatabaseContext.GetTargetMigration().

Here is the call graph for this function:
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_user.html b/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_user.html index 5f8d8326c5..a8e67177b8 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_user.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_user.html @@ -582,7 +582,7 @@

Definition at line 54 of file User.cs.

54{ get; set; }
-

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

+

Referenced by Tgstation.Server.Host.Security.TokenFactory.CreateToken().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_properties_1_1_master_versions_attribute-members.html b/class_tgstation_1_1_server_1_1_host_1_1_properties_1_1_master_versions_attribute-members.html index 1d0da1fe2a..a5659c586f 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_properties_1_1_master_versions_attribute-members.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_properties_1_1_master_versions_attribute-members.html @@ -81,12 +81,13 @@

This is the complete list of members for Tgstation.Server.Host.Properties.MasterVersionsAttribute, including all inherited members.

- + - + +
InstanceTgstation.Server.Host.Properties.MasterVersionsAttributestatic
MasterVersionsAttribute(string rawConfigurationVersion, string rawInteropVersion, string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion)Tgstation.Server.Host.Properties.MasterVersionsAttribute
MasterVersionsAttribute(string rawConfigurationVersion, string rawInteropVersion, string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion, string rawSwarmProtocolVersion)Tgstation.Server.Host.Properties.MasterVersionsAttribute
RawConfigurationVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawHostWatchdogVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawInteropVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawMariaDBRedistVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawWebpanelVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawSwarmProtocolVersionTgstation.Server.Host.Properties.MasterVersionsAttribute
RawWebpanelVersionTgstation.Server.Host.Properties.MasterVersionsAttribute