Exemplo n.º 1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="BasicWatchdog"/> <see langword="class"/>.
 /// </summary>
 /// <param name="chat">The <see cref="IChatManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionPersistor">The <see cref="ISessionPersistor"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="diagnosticsIOManager">The <see cref="IIOManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="remoteDeploymentManagerFactory">The <see cref="IRemoteDeploymentManagerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
 public BasicWatchdog(
     IChatManager chat,
     ISessionControllerFactory sessionControllerFactory,
     IDmbFactory dmbFactory,
     ISessionPersistor sessionPersistor,
     IJobManager jobManager,
     IServerControl serverControl,
     IAsyncDelayer asyncDelayer,
     IIOManager diagnosticsIOManager,
     IEventConsumer eventConsumer,
     IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
     ILogger <BasicWatchdog> logger,
     DreamDaemonLaunchParameters initialLaunchParameters,
     Api.Models.Instance instance,
     bool autoStart)
     : base(
         chat,
         sessionControllerFactory,
         dmbFactory,
         sessionPersistor,
         jobManager,
         serverControl,
         asyncDelayer,
         diagnosticsIOManager,
         eventConsumer,
         remoteDeploymentManagerFactory,
         logger,
         initialLaunchParameters,
         instance,
         autoStart)
 {
 }
Exemplo n.º 2
0
        /// <summary>
        /// Construct a <see cref="IWatchdog"/>
        /// </summary>
        /// <param name="chat">The value of <see cref="chat"/></param>
        /// <param name="sessionControllerFactory">The value of <see cref="sessionControllerFactory"/></param>
        /// <param name="dmbFactory">The value of <see cref="dmbFactory"/></param>
        /// <param name="serverUpdater">The <see cref="IServerControl"/> for the <see cref="Watchdog"/></param>
        /// <param name="logger">The value of <see cref="logger"/></param>
        /// <param name="reattachInfoHandler">The value of <see cref="reattachInfoHandler"/></param>
        /// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/></param>
        /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param>
        /// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/></param>
        /// <param name="instance">The value of <see cref="instance"/></param>
        /// <param name="autoStart">The value of <see cref="autoStart"/></param>
        /// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
        public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger <Watchdog> logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
        {
            this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
            this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
            this.dmbFactory             = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
            this.logger                 = logger ?? throw new ArgumentNullException(nameof(logger));
            this.reattachInfoHandler    = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
            this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
            this.byondTopicSender       = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
            this.eventConsumer          = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
            this.instance               = instance ?? throw new ArgumentNullException(nameof(instance));
            this.autoStart              = autoStart;

            if (serverUpdater == null)
            {
                throw new ArgumentNullException(nameof(serverUpdater));
            }

            serverUpdater.RegisterForRestart(() => releaseServers = true);

            chat.RegisterCommandHandler(this);

            AlphaIsActive          = true;
            ActiveLaunchParameters = initialLaunchParameters;
            releaseServers         = false;
            semaphore = new SemaphoreSlim(1);
            activeParametersUpdated = new TaskCompletionSource <object>();
        }
Exemplo n.º 3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="BasicWatchdog"/> <see langword="class"/>.
 /// </summary>
 /// <param name="chat">The <see cref="IChatManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="reattachInfoHandler">The <see cref="IReattachInfoHandler"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="databaseContextFactory">The <see cref="IDatabaseContextFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
 public BasicWatchdog(
     IChatManager chat,
     ISessionControllerFactory sessionControllerFactory,
     IDmbFactory dmbFactory,
     IReattachInfoHandler reattachInfoHandler,
     IDatabaseContextFactory databaseContextFactory,
     IJobManager jobManager,
     IServerControl serverControl,
     IAsyncDelayer asyncDelayer,
     ILogger <BasicWatchdog> logger,
     DreamDaemonLaunchParameters initialLaunchParameters,
     Api.Models.Instance instance,
     bool autoStart)
     : base(
         chat,
         sessionControllerFactory,
         dmbFactory,
         reattachInfoHandler,
         databaseContextFactory,
         jobManager,
         serverControl,
         asyncDelayer,
         logger,
         initialLaunchParameters,
         instance,
         autoStart)
 {
 }
Exemplo n.º 4
0
 /// <inheritdoc />
 public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken)
 {
     using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
     {
         ActiveLaunchParameters = launchParameters;
         if (Running)
         {
             //queue an update
             activeParametersUpdated.TrySetResult(null);
         }
     }
 }
Exemplo n.º 5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="WatchdogBase"/> <see langword="class"/>.
        /// </summary>
        /// <param name="chat">The value of <see cref="Chat"/></param>
        /// <param name="sessionControllerFactory">The value of <see cref="SessionControllerFactory"/></param>
        /// <param name="dmbFactory">The value of <see cref="DmbFactory"/></param>
        /// <param name="reattachInfoHandler">The value of <see cref="reattachInfoHandler"/></param>
        /// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/></param>
        /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param>
        /// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
        /// <param name="jobManager">The value of <see cref="jobManager"/></param>
        /// <param name="serverControl">The <see cref="IServerControl"/> to populate <see cref="restartRegistration"/> with</param>
        /// <param name="asyncDelayer">The value of <see cref="AsyncDelayer"/>.</param>
        /// <param name="logger">The value of <see cref="Logger"/></param>
        /// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/>. May be modified</param>
        /// <param name="instance">The value of <see cref="instance"/></param>
        /// <param name="autoStart">The value of <see cref="autoStart"/></param>
        protected WatchdogBase(
            IChat chat,
            ISessionControllerFactory sessionControllerFactory,
            IDmbFactory dmbFactory,
            IReattachInfoHandler reattachInfoHandler,
            IDatabaseContextFactory databaseContextFactory,
            IByondTopicSender byondTopicSender,
            IEventConsumer eventConsumer,
            IJobManager jobManager,
            IServerControl serverControl,
            IAsyncDelayer asyncDelayer,
            ILogger logger,
            DreamDaemonLaunchParameters initialLaunchParameters,
            Api.Models.Instance instance,
            bool autoStart)
        {
            Chat = chat ?? throw new ArgumentNullException(nameof(chat));
            SessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
            DmbFactory                  = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
            AsyncDelayer                = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
            this.reattachInfoHandler    = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
            this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
            this.byondTopicSender       = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
            this.eventConsumer          = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
            this.jobManager             = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
            ActiveLaunchParameters = initialLaunchParameters ?? throw new ArgumentNullException(nameof(initialLaunchParameters));
            this.instance          = instance ?? throw new ArgumentNullException(nameof(instance));
            this.autoStart         = autoStart;

            if (serverControl == null)
            {
                throw new ArgumentNullException(nameof(serverControl));
            }

            chat.RegisterCommandHandler(this);

            ActiveLaunchParameters  = initialLaunchParameters;
            releaseServers          = false;
            ActiveParametersUpdated = new TaskCompletionSource <object>();

            restartRegistration = serverControl.RegisterForRestart(this);
            try
            {
                Semaphore = new SemaphoreSlim(1);
            }
            catch
            {
                restartRegistration.Dispose();
                throw;
            }
        }
Exemplo n.º 6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WindowsWatchdog"/> class.
 /// </summary>
 /// <param name="chat">The <see cref="IChatManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionPersistor">The <see cref="ISessionPersistor"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="diagnosticsIOManager">The <see cref="IIOManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="remoteDeploymentManagerFactory">The <see cref="IRemoteDeploymentManagerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="gameIOManager">The value of <see cref="GameIOManager"/>.</param>
 /// <param name="symlinkFactory">The value of <see cref="symlinkFactory"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
 public WindowsWatchdog(
     IChatManager chat,
     ISessionControllerFactory sessionControllerFactory,
     IDmbFactory dmbFactory,
     ISessionPersistor sessionPersistor,
     IJobManager jobManager,
     IServerControl serverControl,
     IAsyncDelayer asyncDelayer,
     IIOManager diagnosticsIOManager,
     IEventConsumer eventConsumer,
     IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
     IIOManager gameIOManager,
     ISymlinkFactory symlinkFactory,
     ILogger <WindowsWatchdog> logger,
     DreamDaemonLaunchParameters initialLaunchParameters,
     Api.Models.Instance instance,
     bool autoStart)
     : base(
         chat,
         sessionControllerFactory,
         dmbFactory,
         sessionPersistor,
         jobManager,
         serverControl,
         asyncDelayer,
         diagnosticsIOManager,
         eventConsumer,
         remoteDeploymentManagerFactory,
         logger,
         initialLaunchParameters,
         instance,
         autoStart)
 {
     try
     {
         GameIOManager       = gameIOManager ?? throw new ArgumentNullException(nameof(gameIOManager));
         this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
     }
     catch
     {
         _ = DisposeAsync();
         throw;
     }
 }
Exemplo n.º 7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WindowsWatchdog"/> <see langword="class"/>.
 /// </summary>
 /// <param name="chat">The <see cref="IChat"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="reattachInfoHandler">The <see cref="IReattachInfoHandler"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="databaseContextFactory">The <see cref="IDatabaseContextFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="byondTopicSender">The <see cref="IByondTopicSender"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
 /// <param name="symlinkFactory">The value of <see cref="symlinkFactory"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
 public WindowsWatchdog(
     IChat chat,
     ISessionControllerFactory sessionControllerFactory,
     IDmbFactory dmbFactory,
     IReattachInfoHandler reattachInfoHandler,
     IDatabaseContextFactory databaseContextFactory,
     IByondTopicSender byondTopicSender,
     IEventConsumer eventConsumer,
     IJobManager jobManager,
     IServerControl serverControl,
     IAsyncDelayer asyncDelayer,
     IIOManager ioManager,
     ISymlinkFactory symlinkFactory,
     ILogger <WindowsWatchdog> logger,
     DreamDaemonLaunchParameters initialLaunchParameters,
     Api.Models.Instance instance, bool autoStart)
     : base(
         chat,
         sessionControllerFactory,
         dmbFactory,
         reattachInfoHandler,
         databaseContextFactory,
         byondTopicSender,
         eventConsumer,
         jobManager,
         serverControl,
         asyncDelayer,
         logger,
         initialLaunchParameters,
         instance,
         autoStart)
 {
     try
     {
         this.ioManager      = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
         this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
     }
     catch
     {
         Dispose();
         throw;
     }
 }
Exemplo n.º 8
0
        /// <summary>
        /// Run a quick DD instance to test the DMAPI is installed on the target code
        /// </summary>
        /// <param name="timeout">The timeout in seconds for validation</param>
        /// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to use to validate the API</param>
        /// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param>
        /// <param name="byondLock">The current <see cref="IByondExecutableLock"/></param>
        /// <param name="portToUse">The port to use for API validation</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task{TResult}"/> resulting in <see langword="true"/> if the DMAPI was successfully validated, <see langword="false"/> otherwise</returns>
        async Task <bool> VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
        {
            logger.LogTrace("Verifying DMAPI...");
            var launchParameters = new DreamDaemonLaunchParameters
            {
                AllowWebClient = false,
                PrimaryPort    = portToUse,
                SecurityLevel  = securityLevel,                   //all it needs to read the file and exit
                StartupTimeout = timeout
            };

            var dirA     = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
            var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job);

            var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout);

            using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false))
            {
                var launchResult = await controller.LaunchResult.ConfigureAwait(false);

                var now = DateTimeOffset.Now;
                if (now < timeoutAt && launchResult.StartupTime.HasValue)
                {
                    var timeoutTask = Task.Delay(timeoutAt - now, cancellationToken);

                    await Task.WhenAny(controller.Lifetime, timeoutTask).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                }

                if (!controller.Lifetime.IsCompleted)
                {
                    logger.LogDebug("API validation timed out!");
                    return(false);
                }

                var validated = controller.ApiValidated;
                logger.LogTrace("API valid: {0}", validated);
                return(validated);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Run a quick DD instance to test the DMAPI is installed on the target code
        /// </summary>
        /// <param name="timeout">The timeout in seconds for validation</param>
        /// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to use to validate the API</param>
        /// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param>
        /// <param name="byondLock">The current <see cref="IByondExecutableLock"/></param>
        /// <param name="portToUse">The port to use for API validation</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
        {
            logger.LogTrace("Verifying DMAPI...");
            var launchParameters = new DreamDaemonLaunchParameters
            {
                AllowWebClient = false,
                PrimaryPort    = portToUse,
                SecurityLevel  = securityLevel,
                StartupTimeout = timeout
            };

            var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);

            job.MinimumSecurityLevel = securityLevel;             // needed for the TempDmbProvider
            var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout);

            using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job))
                using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false))
                {
                    var launchResult = await controller.LaunchResult.ConfigureAwait(false);

                    var now = DateTimeOffset.Now;
                    if (now < timeoutAt && launchResult.StartupTime.HasValue)
                    {
                        var timeoutTask = Task.Delay(timeoutAt - now, cancellationToken);

                        await Task.WhenAny(controller.Lifetime, timeoutTask).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    if (controller.Lifetime.IsCompleted)
                    {
                        var validationStatus = controller.ApiValidationStatus;
                        logger.LogTrace("API validation status: {0}", validationStatus);
                        switch (validationStatus)
                        {
                        case ApiValidationStatus.RequiresUltrasafe:
                            job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
                            return;

                        case ApiValidationStatus.RequiresSafe:
                            if (securityLevel == DreamDaemonSecurity.Ultrasafe)
                            {
                                throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!");
                            }
                            job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
                            return;

                        case ApiValidationStatus.RequiresTrusted:
                            if (securityLevel != DreamDaemonSecurity.Trusted)
                            {
                                throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!");
                            }
                            job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
                            return;

                        case ApiValidationStatus.NeverValidated:
                            break;

                        case ApiValidationStatus.BadValidationRequest:
                            throw new JobException("Recieved an unrecognized API validation request from DreamDaemon!");

                        case ApiValidationStatus.UnaskedValidationRequest:
                        default:
                            throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Session controller returned unexpected ApiValidationStatus: {0}", validationStatus));
                        }
                    }

                    throw new JobException("DMAPI validation timed out!");
                }
        }
        /// <inheritdoc />
                #pragma warning disable CA1506 // TODO: Decomplexify
        public async Task <ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken)
        {
            var portToUse = primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort;

            if (!portToUse.HasValue)
            {
                throw new InvalidOperationException("Given port is null!");
            }
            var accessIdentifier = cryptographySuite.GetSecureString();

            const string JsonPostfix = "tgs.json";

            var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory;

            // delete all previous tgs json files
            var files = await ioManager.GetFilesWithExtension(basePath, JsonPostfix, cancellationToken).ConfigureAwait(false);

            await Task.WhenAll(files.Select(x => ioManager.DeleteFile(x, cancellationToken))).ConfigureAwait(false);

            // i changed this back from guids, hopefully i don't regret that
            string JsonFile(string name) => String.Format(CultureInfo.InvariantCulture, "{0}.{1}", name, JsonPostfix);

            var securityLevelToUse = launchParameters.SecurityLevel.Value;

            switch (dmbProvider.CompileJob.MinimumSecurityLevel)
            {
            case DreamDaemonSecurity.Ultrasafe:
                break;

            case DreamDaemonSecurity.Safe:
                if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe)
                {
                    securityLevelToUse = DreamDaemonSecurity.Safe;
                }
                break;

            case DreamDaemonSecurity.Trusted:
                securityLevelToUse = DreamDaemonSecurity.Trusted;
                break;

            default:
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel));
            }

            // setup interop files
            var interopInfo = new JsonFile
            {
                AccessIdentifier   = accessIdentifier,
                ApiValidateOnly    = apiValidate,
                ChatChannelsJson   = JsonFile("chat_channels"),
                ChatCommandsJson   = JsonFile("chat_commands"),
                ServerCommandsJson = JsonFile("server_commands"),
                InstanceName       = instance.Name,
                SecurityLevel      = securityLevelToUse,
                Revision           = new Api.Models.Internal.RevisionInformation
                {
                    CommitSha       = dmbProvider.CompileJob.RevisionInformation.CommitSha,
                    OriginCommitSha = dmbProvider.CompileJob.RevisionInformation.OriginCommitSha
                }
            };

            interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x => new Interop.TestMerge(x, interopInfo.Revision)));

            var interopJsonFile = JsonFile("interop");

            var interopJson = JsonConvert.SerializeObject(interopInfo, new JsonSerializerSettings
            {
                ContractResolver      = new CamelCasePropertyNamesContractResolver(),
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });

            var chatJsonTrackingTask = chat.TrackJsons(basePath, interopInfo.ChatChannelsJson, interopInfo.ChatCommandsJson, cancellationToken);

            await ioManager.WriteAllBytes(ioManager.ConcatPath(basePath, interopJsonFile), Encoding.UTF8.GetBytes(interopJson), cancellationToken).ConfigureAwait(false);

            var chatJsonTrackingContext = await chatJsonTrackingTask.ConfigureAwait(false);

            try
            {
                // get the byond lock
                var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false);

                try
                {
                    // create interop context
                    var context = new CommContext(ioManager, loggerFactory.CreateLogger <CommContext>(), basePath, interopInfo.ServerCommandsJson);
                    try
                    {
                        // set command line options
                        // more sanitization here cause it uses the same scheme
                        var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson));

                        var visibility = apiValidate ? "invisible" : "public";

                        // important to run on all ports to allow port changing
                        var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -{5} -public -params \"{4}\"",
                                                      dmbProvider.DmbName,
                                                      primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort,
                                                      launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
                                                      SecurityWord(securityLevelToUse),
                                                      parameters,
                                                      visibility);

                        // See https://github.com/tgstation/tgstation-server/issues/719
                        var noShellExecute = !platformIdentifier.IsWindows;

                        // launch dd
                        var process = processExecutor.LaunchProcess(byondLock.DreamDaemonPath, basePath, arguments, noShellExecute: noShellExecute);
                        try
                        {
                            networkPromptReaper.RegisterProcess(process);

                            // return the session controller for it
                            var result = new SessionController(new ReattachInformation
                            {
                                AccessIdentifier = accessIdentifier,
                                Dmb                = dmbProvider,
                                IsPrimary          = primaryDirectory,
                                Port               = portToUse.Value,
                                ProcessId          = process.Id,
                                ChatChannelsJson   = interopInfo.ChatChannelsJson,
                                ChatCommandsJson   = interopInfo.ChatCommandsJson,
                                ServerCommandsJson = interopInfo.ServerCommandsJson,
                            }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger <SessionController>(), launchParameters.SecurityLevel, launchParameters.StartupTimeout);

                            // writeback launch parameter's fixed security level
                            launchParameters.SecurityLevel = securityLevelToUse;

                            return(result);
                        }
                        catch
                        {
                            process.Dispose();
                            throw;
                        }
                    }
                    catch
                    {
                        context.Dispose();
                        throw;
                    }
                }
                catch
                {
                    if (currentByondLock == null)
                    {
                        byondLock.Dispose();
                    }
                    throw;
                }
            }
            catch
            {
                chatJsonTrackingContext.Dispose();
                throw;
            }
        }
Exemplo n.º 11
0
        async Task <WatchdogLaunchResult> LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken)
        {
            logger.LogTrace("Begin LaunchNoLock");
            using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
            {
                if (Running)
                {
                    logger.LogTrace("Aborted due to already running!");
                    return(null);
                }

                Task chatTask;
                //this is necessary, the monitor could be in it's sleep loop trying to restart
                if (startMonitor && await StopMonitor().ConfigureAwait(false))
                {
                    chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken);
                }
                else if (announce)
                {
                    chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken);
                }
                else
                {
                    chatTask = Task.CompletedTask;
                }
                //start both servers
                LastLaunchParameters = ActiveLaunchParameters;
                try
                {
                    //good ole sanity
                    if (alphaServer != null || bravoServer != null)
                    {
                        throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!");
                    }

                    var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null;

                    var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null;
                    var dmbToUse         = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2);

                    try
                    {
                        Task <ISessionController> alphaServerTask;
                        if (!doesntNeedNewDmb)
                        {
                            alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, alphaStartCts.Token);
                        }
                        else
                        {
                            alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
                        }

                        //wait until this boy officially starts so as not to confuse the servers as to who came first
                        var startTime = DateTimeOffset.Now;
                        alphaServer = await alphaServerTask.ConfigureAwait(false);

                        alphaServer.SetHighPriority();

                        //extra delay for total ordering
                        var now   = DateTimeOffset.Now;
                        var delay = now - startTime;

                        if (delay.TotalSeconds < AlphaBravoStartupSeperationInterval)
                        {
                            await Task.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false);
                        }

                        Task <ISessionController> bravoServerTask;
                        if (!doesntNeedNewDmb)
                        {
                            bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken);
                        }
                        else
                        {
                            bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken);
                        }

                        bravoServer = await bravoServerTask.ConfigureAwait(false);

                        bravoServer.SetHighPriority();

                        async Task <LaunchResult> CheckLaunch(ISessionController controller, string serverName)
                        {
                            var launch = await controller.LaunchResult.ConfigureAwait(false);

                            if (launch.ExitCode.HasValue)
                            {
                                //you killed us ray...
                                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName));
                            }
                            if (!launch.StartupTime.HasValue)
                            {
                                throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server timed out on startup: {0}s", launch.ToString(), ActiveLaunchParameters.StartupTimeout.Value));
                            }
                            return(launch);
                        }

                        var alphaLrt = CheckLaunch(alphaServer, "Alpha");
                        var bravoLrt = CheckLaunch(bravoServer, "Bravo");
                        //now we have two booting servers, get them up and running
                        var allTask = Task.WhenAll(alphaLrt, bravoLrt);

                        //don't forget about the cancelationToken
                        var cancelTcs = new TaskCompletionSource <object>();
                        using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
                            await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
                        cancellationToken.ThrowIfCancellationRequested();

                        //both servers are now running, alpha is the active server, huzzah
                        AlphaIsActive    = doReattach ? reattachInfo.AlphaIsActive : true;
                        LastLaunchResult = alphaLrt.Result;
                        logger.LogInformation("Launched servers successfully");
                        Running = true;

                        if (startMonitor)
                        {
                            await StopMonitor().ConfigureAwait(false);

                            monitorCts  = new CancellationTokenSource();
                            monitorTask = MonitorLifetimes(monitorCts.Token);
                        }
                        return(new WatchdogLaunchResult
                        {
                            Alpha = alphaLrt.Result,
                            Bravo = bravoLrt.Result
                        });
                    }
                    catch
                    {
                        if (!doesntNeedNewDmb && (alphaServer == null && bravoServer == null))
                        {
                            dmbToUse.Dispose();                             //guaranteed to not be null here
                            dmbToUse.Dispose();                             //yes, dispose it twice. See the definition of IDmbFactory.LockNextDmb(), we called it with 2 locks
                        }
                        DisposeAndNullControllers();
                        throw;
                    }
                }
                catch (Exception e)
                {
                    logger.LogWarning("Failed to start watchdog: {0}", e.ToString());
                    throw;
                }
                finally
                {
                    try
                    {
                        await chatTask.ConfigureAwait(false);
                    }
                    catch (OperationCanceledException) { }
                }
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Handles the actions to take when the monitor has to "wake up"
        /// </summary>
        /// <param name="activationReason">The <see cref="MonitorActivationReason"/> that caused the invocation</param>
        /// <param name="monitorState">The current <see cref="MonitorState"/>. Will be modified upon retrn</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState, CancellationToken cancellationToken)
        {
            logger.LogDebug("Monitor activation. Reason: {0}", activationReason);

            //returns true if the inactive server can't be used immediately
            bool FullRestartDeadInactive()
            {
                if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail)
                {
                    logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting");
                    monitorState.NextAction = MonitorAction.Restart;                        //will dispose server
                    return(true);
                }
                return(false);
            };

            //trys to set inactive server's port to the public port
            //doesn't handle closing active server's port
            async Task <bool> MakeInactiveActive()
            {
                logger.LogDebug("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value);
                var result = await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.PrimaryPort.Value, cancellationToken).ConfigureAwait(false);

                if (!result)
                {
                    logger.LogWarning("Failed to activate inactive server! Restarting monitor...");
                    monitorState.NextAction = MonitorAction.Restart;                        //will dispose server
                    return(false);
                }

                //inactive server should always be using active launch parameters
                LastLaunchParameters = ActiveLaunchParameters;

                var tmp = monitorState.ActiveServer;

                monitorState.ActiveServer   = monitorState.InactiveServer;
                monitorState.InactiveServer = tmp;
                AlphaIsActive = !AlphaIsActive;
                return(true);
            }

            // Tries to load inactive server with latest dmb, falling back to current dmb on failure. Requires a lock on <see cref="semaphore"/>
            async Task <bool> RestartInactiveServer()
            {
                logger.LogInformation("Rebooting inactive server...");
                var  newDmb = dmbFactory.LockNextDmb(1);
                bool usedMostRecentDmb;

                try
                {
                    monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, newDmb, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);

                    usedMostRecentDmb = true;
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception e)
                {
                    logger.LogError("Error occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString());
                    //ahh jeez, what do we do here?
                    //this is our fault, so it should never happen but
                    //idk maybe a database error while handling the newest dmb?
                    //either way try to start it using the active server's dmb as a backup
                    try
                    {
                        var dmbBackup = await dmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob, cancellationToken).ConfigureAwait(false);

                        if (dmbBackup == null)                          //NANI!?
                        //just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has
                        {
                            throw new JobException("Creating backup DMB provider failed!");
                        }

                        monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);

                        usedMostRecentDmb = false;
                        await chat.SendWatchdogMessage("Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", cancellationToken).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                        throw;
                    }
                    catch (Exception e2)
                    {
                        //fuuuuucckkk
                        logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! Exception: {0}", e2.ToString());
                        monitorState.InactiveServerCritFail = true;
                        await chat.SendWatchdogMessage("Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", cancellationToken).ConfigureAwait(false);

                        return(true);                           //we didn't use the old dmb
                    }
                }

                logger.LogInformation("Successfully relaunched inactive server!");
                monitorState.RebootingInactiveServer = true;
                return(usedMostRecentDmb);
            }

            async Task UpdateAndRestartInactiveServer(bool breakAfter)
            {
                //replace the notification tcs here so that the next loop will read a fresh one
                activeParametersUpdated = new TaskCompletionSource <object>();
                monitorState.InactiveServer.Dispose();                  //kill or recycle it
                monitorState.NextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue;

                var usedLatestDmb = await RestartInactiveServer().ConfigureAwait(false);

                if (monitorState.NextAction == (breakAfter ? MonitorAction.Break : MonitorAction.Continue))
                {
                    monitorState.ActiveServer.ClosePortOnReboot = false;
                    if (monitorState.InactiveServerHasStagedDmb && !usedLatestDmb)
                    {
                        monitorState.InactiveServerHasStagedDmb = false;                                //don't try to load it again though
                    }
                }
            };

            string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed";

            //reason handling
            switch (activationReason)
            {
            case MonitorActivationReason.ActiveServerCrashed:
                if (monitorState.ActiveServer.RebootState == Components.Watchdog.RebootState.Shutdown)
                {
                    await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Exiting due to graceful termination request...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);

                    monitorState.NextAction = MonitorAction.Exit;
                    break;
                }

                if (FullRestartDeadInactive())
                {
                    await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Inactive server unable to online!", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);

                    break;
                }

                await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Onlining inactive server...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);

                if (!await MakeInactiveActive().ConfigureAwait(false))
                {
                    break;
                }

                await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);

                break;

            case MonitorActivationReason.InactiveServerCrashed:
                await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Inactive server {0}! Rebooting...", ExitWord(monitorState.InactiveServer)), cancellationToken).ConfigureAwait(false);
                await UpdateAndRestartInactiveServer(false).ConfigureAwait(false);

                break;

            case MonitorActivationReason.ActiveServerRebooted:
                if (FullRestartDeadInactive())
                {
                    break;
                }

                //what matters here is the RebootState
                bool restartOnceSwapped = false;
                var  rebootState        = monitorState.ActiveServer.RebootState;
                monitorState.ActiveServer.ResetRebootState();                           //the DMAPI has already done this internally

                switch (rebootState)
                {
                case Components.Watchdog.RebootState.Normal:
                    break;

                case Components.Watchdog.RebootState.Restart:
                    restartOnceSwapped = true;
                    break;

                case Components.Watchdog.RebootState.Shutdown:
                    await chat.SendWatchdogMessage("Active server rebooted! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false);

                    DisposeAndNullControllers();
                    monitorState.NextAction = MonitorAction.Exit;
                    return;
                }

                var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id;
                if (sameCompileJob && monitorState.InactiveServerHasStagedDmb)
                {
                    //both servers up to date
                    monitorState.InactiveServerHasStagedDmb = false;
                }
                if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters)
                {
                    //need a new launch in ActiveServer
                    restartOnceSwapped = true;
                }

                if (restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot)
                {
                    //we need to manually restart active server
                    //it won't listen to us right now so just kill it
                    monitorState.ActiveServer.Dispose();
                }

                if ((!restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot) || !await MakeInactiveActive().ConfigureAwait(false))
                {
                    break;
                }

                monitorState.ActiveServer.ClosePortOnReboot = true;

                if (!restartOnceSwapped)
                {
                    monitorState.InactiveServer.ClosePortOnReboot = false;
                    //try to reopen inactive server on the private port so it's not pinging all the time
                    //failing that, just reboot it
                    restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false);
                }

                if (restartOnceSwapped)                                               //for one reason or another
                {
                    await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //break because worse case, active server is still booting
                }
                else
                {
                    monitorState.InactiveServer.ClosePortOnReboot = false;
                    monitorState.NextAction = MonitorAction.Break;
                }
                break;

            case MonitorActivationReason.InactiveServerRebooted:
                monitorState.RebootingInactiveServer = true;
                monitorState.InactiveServer.ResetRebootState();
                monitorState.ActiveServer.ClosePortOnReboot = false;
                monitorState.NextAction = MonitorAction.Continue;
                break;

            case MonitorActivationReason.InactiveServerStartupComplete:
                //eziest case of my life
                monitorState.RebootingInactiveServer        = false;
                monitorState.ActiveServer.ClosePortOnReboot = true;
                monitorState.NextAction = MonitorAction.Continue;
                break;

            case MonitorActivationReason.NewDmbAvailable:
                monitorState.InactiveServerHasStagedDmb = true;
                await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);                               //next case does same thing

                break;

            case MonitorActivationReason.ActiveLaunchParametersUpdated:
                await UpdateAndRestartInactiveServer(false).ConfigureAwait(false);

                break;
            }
        }
        public async Task TestSuccessfulLaunchAndShutdown()
        {
            var mockChat = new Mock <IChat>();

            mockChat.Setup(x => x.RegisterCommandHandler(It.IsNotNull <ICustomCommandHandler>())).Verifiable();
            var mockSessionControllerFactory = new Mock <ISessionControllerFactory>();
            var mockDmbFactory             = new Mock <IDmbFactory>();
            var mockLogger                 = new Mock <ILogger <ExperimentalWatchdog> >();
            var mockReattachInfoHandler    = new Mock <IReattachInfoHandler>();
            var mockDatabaseContextFactory = new Mock <IDatabaseContextFactory>();
            var mockByondTopicSender       = new Mock <IByondTopicSender>();
            var mockEventConsumer          = new Mock <IEventConsumer>();
            var mockJobManager             = new Mock <IJobManager>();
            var mockRestartRegistration    = new Mock <IRestartRegistration>();

            mockRestartRegistration.Setup(x => x.Dispose()).Verifiable();
            var mockServerControl = new Mock <IServerControl>();

            mockServerControl.Setup(x => x.RegisterForRestart(It.IsNotNull <IRestartHandler>())).Returns(mockRestartRegistration.Object).Verifiable();
            var mockLaunchParameters = new DreamDaemonLaunchParameters();
            var mockInstance         = new Models.Instance();
            var mockAsyncDelayer     = new Mock <IAsyncDelayer>();

            using (var wd = new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, mockAsyncDelayer.Object, mockLogger.Object, mockLaunchParameters, mockInstance, default))
                using (var cts = new CancellationTokenSource())
                {
                    var mockCompileJob  = new Models.CompileJob();
                    var mockDmbProvider = new Mock <IDmbProvider>();
                    mockDmbProvider.SetupGet(x => x.CompileJob).Returns(mockCompileJob).Verifiable();
                    var mDmbP = mockDmbProvider.Object;

                    var infiniteTask = new TaskCompletionSource <int>().Task;

                    mockDmbFactory.SetupGet(x => x.OnNewerDmb).Returns(infiniteTask);
                    mockDmbFactory.SetupGet(x => x.DmbAvailable).Returns(true).Verifiable();
                    mockDmbFactory.Setup(x => x.LockNextDmb(2)).Returns(mDmbP).Verifiable();

                    var sessionsToVerify = new List <Mock <ISessionController> >();

                    var cancellationToken = cts.Token;
                    mockSessionControllerFactory.Setup(x => x.LaunchNew(mockLaunchParameters, mDmbP, null, It.IsAny <bool>(), It.IsAny <bool>(), false, cancellationToken)).Returns(() =>
                    {
                        var mockSession = new Mock <ISessionController>();
                        mockSession.SetupGet(x => x.Lifetime).Returns(infiniteTask).Verifiable();
                        mockSession.SetupGet(x => x.OnReboot).Returns(infiniteTask).Verifiable();
                        mockSession.SetupGet(x => x.Dmb).Returns(mDmbP).Verifiable();
                        mockSession.SetupGet(x => x.LaunchResult).Returns(Task.FromResult(new LaunchResult
                        {
                            StartupTime = TimeSpan.FromSeconds(1)
                        })).Verifiable();
                        sessionsToVerify.Add(mockSession);
                        return(Task.FromResult(mockSession.Object));
                    }).Verifiable();
                    mockAsyncDelayer.Setup(x => x.Delay(It.IsAny <TimeSpan>(), cancellationToken)).Returns(Task.CompletedTask).Verifiable();

                    cts.CancelAfter(TimeSpan.FromSeconds(15));

                    try
                    {
                        await wd.Launch(cancellationToken).ConfigureAwait(false);

                        await wd.Terminate(false, cancellationToken).ConfigureAwait(false);
                    }
                    finally
                    {
                        cts.Cancel();
                    }
                    Assert.AreEqual(2, sessionsToVerify.Count);
                    foreach (var I in sessionsToVerify)
                    {
                        I.VerifyAll();
                    }
                    mockDmbProvider.VerifyAll();
                }

            mockSessionControllerFactory.VerifyAll();
            mockDmbFactory.VerifyAll();
            mockRestartRegistration.VerifyAll();
            mockServerControl.VerifyAll();
            mockChat.VerifyAll();
            mockAsyncDelayer.VerifyAll();
        }
        public void TestConstruction()
        {
            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(null, null, null, null, null, null, null, null, null, null, null, null, null, default));

            var mockChat = new Mock <IChat>();

            mockChat.Setup(x => x.RegisterCommandHandler(It.IsNotNull <ICustomCommandHandler>())).Verifiable();
            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, null, null, null, null, null, null, null, null, null, null, null, null, default));

            var mockSessionControllerFactory = new Mock <ISessionControllerFactory>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, null, null, null, null, null, null, null, null, null, null, null, default));

            var mockDmbFactory = new Mock <IDmbFactory>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, null, null, null, null, null, null, null, null, null, null, default));

            var mockReattachInfoHandler = new Mock <IReattachInfoHandler>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, null, null, null, null, null, null, null, null, null, default));

            var mockDatabaseContextFactory = new Mock <IDatabaseContextFactory>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, null, null, null, null, null, null, null, null, default));

            var mockByondTopicSender = new Mock <IByondTopicSender>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, null, null, null, null, null, null, null, default));

            var mockEventConsumer = new Mock <IEventConsumer>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, null, null, null, null, null, null, default));

            var mockJobManager = new Mock <IJobManager>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, null, null, null, null, null, default));

            var mockRestartRegistration = new Mock <IRestartRegistration>();

            mockRestartRegistration.Setup(x => x.Dispose()).Verifiable();
            var mockServerControl = new Mock <IServerControl>();

            mockServerControl.Setup(x => x.RegisterForRestart(It.IsNotNull <IRestartHandler>())).Returns(mockRestartRegistration.Object).Verifiable();
            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, null, null, null, null, default));

            var mockAsyncDelayer = new Mock <IAsyncDelayer>();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, mockAsyncDelayer.Object, null, null, null, default));

            var mockLogger = new Mock <ILogger <ExperimentalWatchdog> >();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, mockAsyncDelayer.Object, mockLogger.Object, null, null, default));

            var mockLaunchParameters = new DreamDaemonLaunchParameters();

            Assert.ThrowsException <ArgumentNullException>(() => new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, mockAsyncDelayer.Object, mockLogger.Object, mockLaunchParameters, null, default));

            var mockInstance = new Models.Instance();

            new ExperimentalWatchdog(mockChat.Object, mockSessionControllerFactory.Object, mockDmbFactory.Object, mockReattachInfoHandler.Object, mockDatabaseContextFactory.Object, mockByondTopicSender.Object, mockEventConsumer.Object, mockJobManager.Object, mockServerControl.Object, mockAsyncDelayer.Object, mockLogger.Object, mockLaunchParameters, mockInstance, default).Dispose();

            mockRestartRegistration.VerifyAll();
            mockServerControl.VerifyAll();
            mockChat.VerifyAll();
        }
Exemplo n.º 15
0
        /// <summary>
        /// Run a quick DD instance to test the DMAPI is installed on the target code
        /// </summary>
        /// <param name="timeout">The timeout in seconds for validation</param>
        /// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to use to validate the API</param>
        /// <param name="job">The <see cref="CompileJob"/> for the operation</param>
        /// <param name="byondLock">The current <see cref="IByondExecutableLock"/></param>
        /// <param name="portToUse">The port to use for API validation</param>
        /// <param name="requireValidate">If the API validation is required to complete the deployment.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task VerifyApi(
            uint timeout,
            DreamDaemonSecurity securityLevel,
            Models.CompileJob job,
            IByondExecutableLock byondLock,
            ushort portToUse,
            bool requireValidate,
            CancellationToken cancellationToken)
        {
            logger.LogTrace("Verifying {0}DMAPI...", requireValidate ? "required " : String.Empty);
            var launchParameters = new DreamDaemonLaunchParameters
            {
                AllowWebClient      = false,
                Port                = portToUse,
                SecurityLevel       = securityLevel,
                StartupTimeout      = timeout,
                TopicRequestTimeout = 0,             // not used
                HeartbeatSeconds    = 0              // not used
            };

            job.MinimumSecurityLevel = securityLevel;             // needed for the TempDmbProvider

            ApiValidationStatus validationStatus;

            using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job))
                await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken).ConfigureAwait(false))
                {
                    var launchResult = await controller.LaunchResult.ConfigureAwait(false);

                    if (launchResult.StartupTime.HasValue)
                    {
                        await controller.Lifetime.WithToken(cancellationToken).ConfigureAwait(false);
                    }

                    if (!controller.Lifetime.IsCompleted)
                    {
                        await controller.DisposeAsync().ConfigureAwait(false);
                    }

                    validationStatus = controller.ApiValidationStatus;

                    if (requireValidate && validationStatus == ApiValidationStatus.NeverValidated)
                    {
                        throw new JobException(ErrorCode.DreamMakerNeverValidated);
                    }

                    logger.LogTrace("API validation status: {0}", validationStatus);

                    job.DMApiVersion = controller.DMApiVersion;
                }

            switch (validationStatus)
            {
            case ApiValidationStatus.RequiresUltrasafe:
                job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
                return;

            case ApiValidationStatus.RequiresSafe:
                job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
                return;

            case ApiValidationStatus.RequiresTrusted:
                job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
                return;

            case ApiValidationStatus.NeverValidated:
                if (requireValidate)
                {
                    throw new JobException(ErrorCode.DreamMakerNeverValidated);
                }
                job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
                break;

            case ApiValidationStatus.BadValidationRequest:
            case ApiValidationStatus.Incompatible:
                throw new JobException(ErrorCode.DreamMakerInvalidValidation);

            case ApiValidationStatus.UnaskedValidationRequest:
            default:
                throw new InvalidOperationException(
                          $"Session controller returned unexpected ApiValidationStatus: {validationStatus}");
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="ExperimentalWatchdog"/> <see langword="class"/>.
 /// </summary>
 /// <param name="chat">The <see cref="IChat"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="reattachInfoHandler">The <see cref="IReattachInfoHandler"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="databaseContextFactory">The <see cref="IDatabaseContextFactory"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="byondTopicSender">The <see cref="IByondTopicSender"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
 /// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
 public ExperimentalWatchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, IServerControl serverControl, IAsyncDelayer asyncDelayer, ILogger <ExperimentalWatchdog> logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
     : base(
         chat,
         sessionControllerFactory,
         dmbFactory,
         reattachInfoHandler,
         databaseContextFactory,
         byondTopicSender,
         eventConsumer,
         jobManager,
         serverControl,
         asyncDelayer,
         logger,
         initialLaunchParameters,
         instance,
         autoStart)
 {
     alphaIsActive = true;
 }