Exemple #1
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy);

                _inConfigStage = true;
                _completedCommand.Reset();
                _term.CancelKeyPress += CtrlCHandler;

                //register a SIGTERM handler
                HostContext.Unloading += Runner_Unloading;

                // TODO Unit test to cover this logic
                Trace.Info(nameof(ExecuteCommand));
                var configManager = HostContext.GetService <IConfigurationManager>();

                // command is not required, if no command it just starts if configured

                // TODO: Invalid config prints usage

                if (command.Help)
                {
                    PrintUsage(command);
                    return(Constants.Runner.ReturnCode.Success);
                }

                if (command.Version)
                {
                    _term.WriteLine(BuildConstants.RunnerPackage.Version);
                    return(Constants.Runner.ReturnCode.Success);
                }

                if (command.Commit)
                {
                    _term.WriteLine(BuildConstants.Source.CommitHash);
                    return(Constants.Runner.ReturnCode.Success);
                }

                // Configure runner prompt for args if not supplied
                // Unattended configure mode will not prompt for args if not supplied and error on any missing or invalid value.
                if (command.Configure)
                {
                    try
                    {
                        await configManager.ConfigureAsync(command);

                        return(Constants.Runner.ReturnCode.Success);
                    }
                    catch (Exception ex)
                    {
                        Trace.Error(ex);
                        _term.WriteError(ex.Message);
                        return(Constants.Runner.ReturnCode.TerminatedError);
                    }
                }

                // remove config files, remove service, and exit
                if (command.Remove)
                {
                    try
                    {
                        await configManager.UnconfigureAsync(command);

                        return(Constants.Runner.ReturnCode.Success);
                    }
                    catch (Exception ex)
                    {
                        Trace.Error(ex);
                        _term.WriteError(ex.Message);
                        return(Constants.Runner.ReturnCode.TerminatedError);
                    }
                }

                _inConfigStage = false;

                // warmup runner process (JIT/CLR)
                // In scenarios where the runner is single use (used and then thrown away), the system provisioning the runner can call `Runner.Listener --warmup` before the machine is made available to the pool for use.
                // this will optimizes the runner process startup time.
                if (command.Warmup)
                {
                    var binDir = HostContext.GetDirectory(WellKnownDirectory.Bin);
                    foreach (var assemblyFile in Directory.EnumerateFiles(binDir, "*.dll"))
                    {
                        try
                        {
                            Trace.Info($"Load assembly: {assemblyFile}.");
                            var assembly = Assembly.LoadFrom(assemblyFile);
                            var types    = assembly.GetTypes();
                            foreach (Type loadedType in types)
                            {
                                try
                                {
                                    Trace.Info($"Load methods: {loadedType.FullName}.");
                                    var methods = loadedType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
                                    foreach (var method in methods)
                                    {
                                        if (!method.IsAbstract && !method.ContainsGenericParameters)
                                        {
                                            Trace.Verbose($"Prepare method: {method.Name}.");
                                            RuntimeHelpers.PrepareMethod(method.MethodHandle);
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Trace.Error(ex);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Trace.Error(ex);
                        }
                    }

                    return(Constants.Runner.ReturnCode.Success);
                }

                RunnerSettings settings = configManager.LoadSettings();

                var  store = HostContext.GetService <IConfigurationStore>();
                bool configuredAsService = store.IsServiceConfigured();

                // Run runner
                if (command.Run) // this line is current break machine provisioner.
                {
                    // Error if runner not configured.
                    if (!configManager.IsConfigured())
                    {
                        _term.WriteError("Runner is not configured.");
                        PrintUsage(command);
                        return(Constants.Runner.ReturnCode.TerminatedError);
                    }

                    Trace.Verbose($"Configured as service: '{configuredAsService}'");

                    //Get the startup type of the runner i.e., autostartup, service, manual
                    StartupType startType;
                    var         startupTypeAsString = command.GetStartupType();
                    if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
                    {
                        // We need try our best to make the startup type accurate
                        // The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
                        // At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
                        // We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
                        Trace.Info($"Try determine runner startup type base on console redirects.");
                        startType = (Console.IsErrorRedirected && Console.IsInputRedirected && Console.IsOutputRedirected) ? StartupType.Service : StartupType.Manual;
                    }
                    else
                    {
                        if (!Enum.TryParse(startupTypeAsString, true, out startType))
                        {
                            Trace.Info($"Could not parse the argument value '{startupTypeAsString}' for StartupType. Defaulting to {StartupType.Manual}");
                            startType = StartupType.Manual;
                        }
                    }

                    Trace.Info($"Set runner startup type - {startType}");
                    HostContext.StartupType = startType;

                    // Run the runner interactively or as service
                    return(await RunAsync(settings, command.RunOnce));
                }
                else
                {
                    PrintUsage(command);
                    return(Constants.Runner.ReturnCode.Success);
                }
            }
            finally
            {
                _term.CancelKeyPress  -= CtrlCHandler;
                HostContext.Unloading -= Runner_Unloading;
                _completedCommand.Set();
            }
        }
        public async Task <int> RunAsync(string pipeIn, string pipeOut)
        {
            // Validate args.
            ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn));
            ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut));
            var agentWebProxy    = HostContext.GetService <IVstsAgentWebProxy>();
            var agentCertManager = HostContext.GetService <IAgentCertificateManager>();

            VssUtil.InitializeVssClientSettings(HostContext.UserAgent, agentWebProxy.WebProxy, agentCertManager.VssClientCertificateManager);

            var jobRunner = HostContext.CreateService <IJobRunner>();

            using (var channel = HostContext.CreateService <IProcessChannel>())
                using (var jobRequestCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(HostContext.AgentShutdownToken))
                    using (var channelTokenSource = new CancellationTokenSource())
                    {
                        // Start the channel.
                        channel.StartClient(pipeIn, pipeOut);

                        // Wait for up to 30 seconds for a message from the channel.
                        HostContext.WritePerfCounter("WorkerWaitingForJobMessage");
                        Trace.Info("Waiting to receive the job message from the channel.");
                        WorkerMessage channelMessage;
                        using (var csChannelMessage = new CancellationTokenSource(_workerStartTimeout))
                        {
                            channelMessage = await channel.ReceiveAsync(csChannelMessage.Token);
                        }

                        // Deserialize the job message.
                        Trace.Info("Message received.");
                        ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType));
                        ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body));
                        var jobMessage = JsonUtility.FromString <Pipelines.AgentJobRequestMessage>(channelMessage.Body);
                        ArgUtil.NotNull(jobMessage, nameof(jobMessage));
                        HostContext.WritePerfCounter($"WorkerJobMessageReceived_{jobMessage.RequestId.ToString()}");

                        // Initialize the secret masker and set the thread culture.
                        InitializeSecretMasker(jobMessage);
                        SetCulture(jobMessage);

                        // Start the job.
                        Trace.Info($"Job message:{Environment.NewLine} {StringUtil.ConvertToJson(WorkerUtilities.ScrubPiiData(jobMessage))}");
                        Task <TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token);

                        bool cancel = false;
                        while (!cancel)
                        {
                            // Start listening for a cancel message from the channel.
                            Trace.Info("Listening for cancel message from the channel.");
                            Task <WorkerMessage> channelTask = channel.ReceiveAsync(channelTokenSource.Token);

                            // Wait for one of the tasks to complete.
                            Trace.Info("Waiting for the job to complete or for a cancel message from the channel.");
                            await Task.WhenAny(jobRunnerTask, channelTask);

                            // Handle if the job completed.
                            if (jobRunnerTask.IsCompleted)
                            {
                                Trace.Info("Job completed.");
                                channelTokenSource.Cancel(); // Cancel waiting for a message from the channel.
                                return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask));
                            }

                            // Otherwise a message was received from the channel.
                            channelMessage = await channelTask;
                            switch (channelMessage.MessageType)
                            {
                            case MessageType.CancelRequest:
                                Trace.Info("Cancellation/Shutdown message received.");
                                cancel = true;
                                jobRequestCancellationToken.Cancel(); // Expire the host cancellation token.
                                break;

                            case MessageType.AgentShutdown:
                                Trace.Info("Cancellation/Shutdown message received.");
                                cancel = true;
                                HostContext.ShutdownAgent(ShutdownReason.UserCancelled);
                                break;

                            case MessageType.OperatingSystemShutdown:
                                Trace.Info("Cancellation/Shutdown message received.");
                                cancel = true;
                                HostContext.ShutdownAgent(ShutdownReason.OperatingSystemShutdown);
                                break;

                            case MessageType.JobMetadataUpdate:
                                Trace.Info("Metadata update message received.");
                                var metadataMessage = JsonUtility.FromString <JobMetadataMessage>(channelMessage.Body);
                                jobRunner.UpdateMetadata(metadataMessage);
                                break;

                            default:
                                throw new ArgumentOutOfRangeException(nameof(channelMessage.MessageType), channelMessage.MessageType, nameof(channelMessage.MessageType));
                            }
                        }

                        // Await the job.
                        return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask));
                    }
        }
Exemple #3
0
        public async Task <int> RunAsync(string pipeIn, string pipeOut)
        {
            try
            {
                // Setup way to handle SIGTERM/unloading signals
                _completedCommand.Reset();
                HostContext.Unloading += Worker_Unloading;

                // Validate args.
                ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn));
                ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut));
                VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
                var jobRunner = HostContext.CreateService <IJobRunner>();

                using (var channel = HostContext.CreateService <IProcessChannel>())
                    using (var jobRequestCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken))
                        using (var channelTokenSource = new CancellationTokenSource())
                        {
                            // Start the channel.
                            channel.StartClient(pipeIn, pipeOut);

                            // Wait for up to 30 seconds for a message from the channel.
                            HostContext.WritePerfCounter("WorkerWaitingForJobMessage");
                            Trace.Info("Waiting to receive the job message from the channel.");
                            WorkerMessage channelMessage;
                            using (var csChannelMessage = new CancellationTokenSource(_workerStartTimeout))
                            {
                                channelMessage = await channel.ReceiveAsync(csChannelMessage.Token);
                            }

                            // Deserialize the job message.
                            Trace.Info("Message received.");
                            ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType));
                            ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body));
                            var jobMessage = StringUtil.ConvertFromJson <Pipelines.AgentJobRequestMessage>(channelMessage.Body);
                            ArgUtil.NotNull(jobMessage, nameof(jobMessage));
                            HostContext.WritePerfCounter($"WorkerJobMessageReceived_{jobMessage.RequestId.ToString()}");

                            // Initialize the secret masker and set the thread culture.
                            InitializeSecretMasker(jobMessage);
                            SetCulture(jobMessage);

                            // Start the job.
                            Trace.Info($"Job message:{Environment.NewLine} {StringUtil.ConvertToJson(jobMessage)}");
                            Task <TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token);

                            // Start listening for a cancel message from the channel.
                            Trace.Info("Listening for cancel message from the channel.");
                            Task <WorkerMessage> channelTask = channel.ReceiveAsync(channelTokenSource.Token);

                            // Wait for one of the tasks to complete.
                            Trace.Info("Waiting for the job to complete or for a cancel message from the channel.");
                            Task.WaitAny(jobRunnerTask, channelTask);
                            // Handle if the job completed.
                            if (jobRunnerTask.IsCompleted)
                            {
                                Trace.Info("Job completed.");
                                channelTokenSource.Cancel(); // Cancel waiting for a message from the channel.
                                return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask));
                            }

                            // Otherwise a cancel message was received from the channel.
                            Trace.Info("Cancellation/Shutdown message received.");
                            channelMessage = await channelTask;
                            switch (channelMessage.MessageType)
                            {
                            case MessageType.CancelRequest:
                                jobRequestCancellationToken.Cancel(); // Expire the host cancellation token.
                                break;

                            case MessageType.RunnerShutdown:
                                HostContext.ShutdownRunner(ShutdownReason.UserCancelled);
                                break;

                            case MessageType.OperatingSystemShutdown:
                                HostContext.ShutdownRunner(ShutdownReason.OperatingSystemShutdown);
                                break;

                            default:
                                throw new ArgumentOutOfRangeException(nameof(channelMessage.MessageType), channelMessage.MessageType, nameof(channelMessage.MessageType));
                            }

                            // Await the job.
                            return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask));
                        }
            }
            finally
            {
                HostContext.Unloading -= Worker_Unloading;
                _completedCommand.Set();
            }
        }
Exemple #4
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                var agentWebProxy    = HostContext.GetService <IVstsAgentWebProxy>();
                var agentCertManager = HostContext.GetService <IAgentCertificateManager>();
                VssUtil.InitializeVssClientSettings(HostContext.UserAgent, agentWebProxy.WebProxy, agentCertManager.VssClientCertificateManager);

                _inConfigStage = true;
                _completedCommand.Reset();
                _term.CancelKeyPress += CtrlCHandler;

                //register a SIGTERM handler
                HostContext.Unloading += Agent_Unloading;

                // TODO Unit test to cover this logic
                Trace.Info(nameof(ExecuteCommand));
                var configManager = HostContext.GetService <IConfigurationManager>();

                // command is not required, if no command it just starts if configured

                // TODO: Invalid config prints usage

                if (command.Help)
                {
                    PrintUsage(command);
                    return(Constants.Agent.ReturnCode.Success);
                }

                if (command.Version)
                {
                    _term.WriteLine(Constants.Agent.Version);
                    return(Constants.Agent.ReturnCode.Success);
                }

                if (command.Commit)
                {
                    _term.WriteLine(BuildConstants.Source.CommitHash);
                    return(Constants.Agent.ReturnCode.Success);
                }

                // Configure agent prompt for args if not supplied
                // Unattend configure mode will not prompt for args if not supplied and error on any missing or invalid value.
                if (command.Configure)
                {
                    try
                    {
                        await configManager.ConfigureAsync(command);

                        return(Constants.Agent.ReturnCode.Success);
                    }
                    catch (Exception ex)
                    {
                        Trace.Error(ex);
                        _term.WriteError(ex.Message);
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                }

                // remove config files, remove service, and exit
                if (command.Remove)
                {
                    try
                    {
                        await configManager.UnconfigureAsync(command);

                        return(Constants.Agent.ReturnCode.Success);
                    }
                    catch (Exception ex)
                    {
                        Trace.Error(ex);
                        _term.WriteError(ex.Message);
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                }

                _inConfigStage = false;

                AgentSettings settings = configManager.LoadSettings();

                var  store = HostContext.GetService <IConfigurationStore>();
                bool configuredAsService = store.IsServiceConfigured();

                // Run agent
                //if (command.Run) // this line is current break machine provisioner.
                //{

                // Error if agent not configured.
                if (!configManager.IsConfigured())
                {
                    _term.WriteError(StringUtil.Loc("AgentIsNotConfigured"));
                    PrintUsage(command);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                Trace.Verbose($"Configured as service: '{configuredAsService}'");

                //Get the startup type of the agent i.e., autostartup, service, manual
                StartupType startType;
                var         startupTypeAsString = command.GetStartupType();
                if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
                {
                    // We need try our best to make the startup type accurate
                    // The problem is coming from agent autoupgrade, which result an old version service host binary but a newer version agent binary
                    // At that time the servicehost won't pass --startuptype to agent.listener while the agent is actually running as service.
                    // We will guess the startup type only when the agent is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
                    Trace.Info($"Try determine agent startup type base on console redirects.");
                    startType = (Console.IsErrorRedirected && Console.IsInputRedirected && Console.IsOutputRedirected) ? StartupType.Service : StartupType.Manual;
                }
                else
                {
                    if (!Enum.TryParse(startupTypeAsString, true, out startType))
                    {
                        Trace.Info($"Could not parse the argument value '{startupTypeAsString}' for StartupType. Defaulting to {StartupType.Manual}");
                        startType = StartupType.Manual;
                    }
                }

                Trace.Info($"Set agent startup type - {startType}");
                HostContext.StartupType = startType;

#if OS_WINDOWS
                if (store.IsAutoLogonConfigured())
                {
                    if (HostContext.StartupType != StartupType.Service)
                    {
                        Trace.Info($"Autologon is configured on the machine, dumping all the autologon related registry settings");
                        var autoLogonRegManager = HostContext.GetService <IAutoLogonRegistryManager>();
                        autoLogonRegManager.DumpAutoLogonRegistrySettings();
                    }
                    else
                    {
                        Trace.Info($"Autologon is configured on the machine but current Agent.Listner.exe is launched from the windows service");
                    }
                }
#endif
                // Run the agent interactively or as service
                return(await RunAsync(settings));
            }
            finally
            {
                _term.CancelKeyPress  -= CtrlCHandler;
                HostContext.Unloading -= Agent_Unloading;
                _completedCommand.Set();
            }
        }