예제 #1
0
        public void GetsArg()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--agent", "some agent" });

                // Act.
                string actual = command.GetAgent();

                // Assert.
                Assert.Equal("some agent", actual);
            }
        }
예제 #2
0
        public void GetsCommandUnconfigure()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "remove" });

                // Act.
                bool actual = command.Unconfigure;

                // Assert.
                Assert.True(actual);
            }
        }
예제 #3
0
        public void GetsCommandRun()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "run" });

                // Act.
                bool actual = command.Run;

                // Assert.
                Assert.True(actual);
            }
        }
 public void WindowsServiceControlManagerShouldInstallService()
 {
     using (var tc = CreateTestContext())
     {
         var serviceControlManager = new WindowsServiceControlManager();
         serviceControlManager.Initialize(tc);
         var agentSettings = new AgentSettings { ServerUrl = "http://server.name", AgentName = "myagent" };
         var command = new CommandSettings(
             tc,
             new[]
             {
                 "--windowslogonaccount", _expectedLogonAccount,
                 "--windowslogonpassword", _expectedLogonPassword,
                 "--unattended"
             });
         serviceControlManager.ConfigureService(agentSettings, command);
         Assert.Equal("vstsagent.server.myagent", serviceControlManager.ServiceName);
         Assert.Equal("VSTS Agent (server.myagent)", serviceControlManager.ServiceDisplayName);
     }
 }
예제 #5
0
        //process 2 new job messages, and one cancel message
        public async void TestRunAsync()
        {
            using (var hc = new TestHostContext(this))
            using (var tokenSource = new CancellationTokenSource())
            {
                //Arrange
                var agent = new Agent.Listener.Agent();
                agent.TokenSource = tokenSource;
                hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
                hc.SetSingleton<IJobNotification>(_jobNotification.Object);
                hc.SetSingleton<IMessageListener>(_messageListener.Object);
                hc.SetSingleton<IPromptManager>(_promptManager.Object);                
                hc.SetSingleton<IAgentServer>(_agentServer.Object);
                agent.Initialize(hc);
                var settings = new AgentSettings
                {
                    PoolId = 43242
                };
                var taskAgentSession = new TaskAgentSession();
                //we use reflection to achieve this, because "set" is internal
                PropertyInfo sessionIdProperty = taskAgentSession.GetType().GetProperty("SessionId", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                Assert.NotNull(sessionIdProperty);
                sessionIdProperty.SetValue(taskAgentSession, Guid.NewGuid());

                var message = new TaskAgentMessage()
                {
                    Body = JsonUtility.ToString(CreateJobRequestMessage("job1")),
                    MessageId = 4234,
                    MessageType = JobRequestMessage.MessageType
                };

                var messages = new Queue<TaskAgentMessage>();
                messages.Enqueue(message);
                var signalWorkerComplete = new SemaphoreSlim(0, 1);
                _configurationManager.Setup(x => x.LoadSettings())
                    .Returns(settings);
                _configurationManager.Setup(x => x.IsConfigured())
                    .Returns(true);
                _configurationManager.Setup(x => x.EnsureConfiguredAsync(It.IsAny<CommandSettings>()))
                    .Returns(Task.CompletedTask);
                _messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
                    .Returns(Task.FromResult<bool>(true));
                _messageListener.Setup(x => x.Session)
                    .Returns(taskAgentSession);
                _messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
                    .Returns(async () =>
                        {
                            if (0 == messages.Count)
                            {
                                signalWorkerComplete.Release();
                                await Task.Delay(2000, tokenSource.Token);
                            }

                            return messages.Dequeue();
                        });
                _messageListener.Setup(x => x.DeleteSessionAsync())
                    .Returns(Task.CompletedTask);
                _jobDispatcher.Setup(x => x.Run(It.IsAny<JobRequestMessage>()))
                    .Callback(()=>
                    {

                    });
                _agentServer.Setup(x => x.DeleteAgentMessageAsync(settings.PoolId, message.MessageId, taskAgentSession.SessionId, It.IsAny<CancellationToken>()))
                    .Returns((Int32 poolId, Int64 messageId, Guid sessionId, CancellationToken cancellationToken) =>
                   {
                       return Task.CompletedTask;
                   });
                _jobNotification.Setup(x => x.StartClient(It.IsAny<String>(), It.IsAny<CancellationToken>()))
                    .Callback(() =>
                    {

                    });

                hc.EnqueueInstance<IJobDispatcher>(_jobDispatcher.Object);

                //Act
                var command = new CommandSettings(hc, new string[0]);
                Task agentTask = agent.ExecuteCommand(command);

                //Assert
                //wait for the agent to run one job
                if (!await signalWorkerComplete.WaitAsync(2000))
                {
                    Assert.True(false, $"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked.");
                }
                else
                {
                    //Act
                    tokenSource.Cancel(); //stop Agent

                    //Assert
                    Task[] taskToWait2 = { agentTask, Task.Delay(2000) };
                    //wait for the Agent to exit
                    await Task.WhenAny(taskToWait2);

                    Assert.True(agentTask.IsCompleted, $"{nameof(agent.ExecuteCommand)} timed out.");
                    Assert.True(!agentTask.IsFaulted, agentTask.Exception?.ToString());
                    Assert.True(agentTask.IsCanceled);

                    _jobDispatcher.Verify(x => x.Run(It.IsAny<JobRequestMessage>()), Times.Once(),
                         $"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
                    _messageListener.Verify(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()), Times.AtLeastOnce());
                    _messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
                    _messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
                    _agentServer.Verify(x => x.DeleteAgentMessageAsync(settings.PoolId, message.MessageId, taskAgentSession.SessionId, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
                }
            }
        }
예제 #6
0
        // Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener)
        // 0: Agent exit
        // 1: Terminate failure
        // 2: Retriable failure
        // 3: Exit for self update
        private async static Task <int> MainAsync(IHostContext context, string[] args)
        {
            Tracing trace = context.GetTrace("AgentProcess");

            trace.Info($"Agent package {BuildConstants.AgentPackage.PackageName}.");
            trace.Info($"Running on {PlatformUtil.HostOS} ({PlatformUtil.HostArchitecture}).");
            trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}.");
            context.WritePerfCounter("AgentProcessStarted");
            var terminal = context.GetService <ITerminal>();

            // TODO: check that the right supporting tools are available for this platform
            // (replaces the check for build platform vs runtime platform)

            try
            {
                trace.Info($"Version: {BuildConstants.AgentPackage.Version}");
                trace.Info($"Commit: {BuildConstants.Source.CommitHash}");
                trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}");
                trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}");

                // Validate directory permissions.
                string agentDirectory = context.GetDirectory(WellKnownDirectory.Root);
                trace.Info($"Validating directory permissions for: '{agentDirectory}'");
                try
                {
                    IOUtil.ValidateExecutePermission(agentDirectory);
                }
                catch (Exception e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                if (PlatformUtil.RunningOnWindows)
                {
                    // Validate PowerShell 3.0 or higher is installed.
                    var powerShellExeUtil = context.GetService <IPowerShellExeUtil>();
                    try
                    {
                        powerShellExeUtil.GetPath();
                    }
                    catch (Exception e)
                    {
                        terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                        trace.Error(e);
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }

                    // Validate .NET Framework 4.5 or higher is installed.
                    if (!NetFrameworkUtil.Test(new Version(4, 5), trace))
                    {
                        terminal.WriteError(StringUtil.Loc("MinimumNetFramework"));
                        // warn only, like configurationmanager.cs does. this enables windows edition with just .netcore to work
                    }
                }

                // Add environment variables from .env file
                string envFile = Path.Combine(context.GetDirectory(WellKnownDirectory.Root), ".env");
                if (File.Exists(envFile))
                {
                    var envContents = File.ReadAllLines(envFile);
                    foreach (var env in envContents)
                    {
                        if (!string.IsNullOrEmpty(env) && env.IndexOf('=') > 0)
                        {
                            string envKey   = env.Substring(0, env.IndexOf('='));
                            string envValue = env.Substring(env.IndexOf('=') + 1);
                            Environment.SetEnvironmentVariable(envKey, envValue);
                        }
                    }
                }

                // Parse the command line args.
                var command = new CommandSettings(context, args, new SystemEnvironment());
                trace.Info("Arguments parsed");

                // Print any Parse Errros
                if (command.ParseErrors?.Any() == true)
                {
                    List <string> errorStr = new List <string>();

                    foreach (var error in command.ParseErrors)
                    {
                        if (error is TokenError tokenError)
                        {
                            errorStr.Add(tokenError.Token);
                        }
                        else
                        {
                            // Unknown type of error dump to log
                            terminal.WriteError(StringUtil.Loc("ErrorOccurred", error.Tag));
                        }
                    }

                    terminal.WriteError(
                        StringUtil.Loc("UnrecognizedCmdArgs",
                                       string.Join(", ", errorStr)));
                }

                // Defer to the Agent class to execute the command.
                IAgent agent = context.GetService <IAgent>();
                try
                {
                    return(await agent.ExecuteCommand(command));
                }
                catch (OperationCanceledException) when(context.AgentShutdownToken.IsCancellationRequested)
                {
                    trace.Info("Agent execution been cancelled.");
                    return(Constants.Agent.ReturnCode.Success);
                }
                catch (NonRetryableException e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
            }
            catch (Exception e)
            {
                terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                trace.Error(e);
                return(Constants.Agent.ReturnCode.RetryableError);
            }
        }
예제 #7
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                var proxyConfig = HostContext.GetService <IProxyConfiguration>();
                proxyConfig.ApplyProxySettings();

                _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 and/or configures if not configured

                // TODO: Invalid config prints usage

                if (command.Help)
                {
                    PrintUsage();
                    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);
                    }
                }

                // Unconfigure, remove config files, service and exit
                if (command.Unconfigure)
                {
                    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();
                bool          runAsService = configManager.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();
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                // Run the agent interactively or as service
                Trace.Verbose($"Run as service: '{runAsService}'");
                return(await RunAsync(TokenSource.Token, settings, runAsService));

                //}

                // #if OS_WINDOWS
                //                 // this code is for migrated .net windows agent that running as windows service.
                //                 // leave the code as is untill we have a real plan for auto-migration.
                //                 if (runAsService && configManager.IsConfigured() && File.Exists(Path.Combine(IOUtil.GetBinPath(), "VsoAgentService.exe")))
                //                 {
                //                     // The old .net windows servicehost doesn't pass correct args while invoke Agent.Listener.exe
                //                     // When we detect the agent is a migrated .net windows agent, we will just run the agent.listener.exe even the servicehost doesn't pass correct args.
                //                     Trace.Verbose($"Run the agent for compat reason.");
                //                     return await RunAsync(TokenSource.Token, settings, runAsService);
                //                 }
                // #endif

                //                 Trace.Info("Doesn't match any existing command option, print usage.");
                //                 PrintUsage();
                //                 return Constants.Agent.ReturnCode.TerminatedError;
            }
            finally
            {
                _term.CancelKeyPress  -= CtrlCHandler;
                HostContext.Unloading -= Agent_Unloading;
                _completedCommand.Set();
            }
        }
예제 #8
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();
            }
        }
예제 #9
0
        public void PromptsWhenInvalid()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--url", "notValid" });
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.Url, // argName
                        StringUtil.Loc("ServerUrl"), // description
                        false, // secret
                        string.Empty, // defaultValue
                        Validators.ServerUrlValidator, // validator
                        false)) // unattended
                    .Returns("some url");

                // Act.
                string actual = command.GetUrl();

                // Assert.
                Assert.Equal("some url", actual);
            }
        }
예제 #10
0
        public void PromptsForWindowsLogonPassword()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                string accountName = "somewindowsaccount";
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.WindowsLogonPassword, // argName
                        StringUtil.Loc("WindowsLogonPasswordDescription", accountName), // description
                        true, // secret
                        string.Empty, // defaultValue
                        Validators.NonEmptyValidator, // validator
                        false)) // unattended
                    .Returns("some windows logon password");

                // Act.
                string actual = command.GetWindowsLogonPassword(accountName);

                // Assert.
                Assert.Equal("some windows logon password", actual);
            }
        }
예제 #11
0
        public void GetsFlagRunAsService()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--runasservice" });

                // Act.
                bool actual = command.GetRunAsService();

                // Assert.
                Assert.True(actual);
            }
        }
예제 #12
0
        public void GetsFlagNoStart()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--nostart" });

                // Act.
                bool actual = command.NoStart;

                // Assert.
                Assert.True(actual);
            }
        }
예제 #13
0
        // Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener)
        // 0: Agent exit
        // 1: Terminate failure
        // 2: Retriable failure
        // 3: Exit for self update
        public async static Task <int> MainAsync(string[] args)
        {
            using (HostContext context = new HostContext("Agent"))
            {
                s_trace = context.GetTrace("AgentProcess");
                s_trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}.");
                s_trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}.");

                // Validate the binaries intended for one OS are not running on a different OS.
                switch (Constants.Agent.Platform)
                {
                case Constants.OSPlatform.Linux:
                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        Console.WriteLine(StringUtil.Loc("NotLinux"));
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                    break;

                case Constants.OSPlatform.OSX:
                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                    {
                        Console.WriteLine(StringUtil.Loc("NotOSX"));
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                    break;

                case Constants.OSPlatform.Windows:
                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        Console.WriteLine(StringUtil.Loc("NotWindows"));
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                    break;

                default:
                    Console.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString()));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                int rc = Constants.Agent.ReturnCode.Success;
                try
                {
                    s_trace.Info($"Version: {Constants.Agent.Version}");
                    s_trace.Info($"Commit: {BuildConstants.Source.CommitHash}");

                    //
                    // TODO (bryanmac): Need VsoAgent.exe compat shim for SCM
                    //                  That shim will also provide a compat arg parse
                    //                  and translate / to -- etc...
                    //

                    // Parse the command line args.
                    var command = new CommandSettings(context, args);
                    s_trace.Info("Arguments parsed");

                    // Defer to the Agent class to execute the command.
                    IAgent agent = context.GetService <IAgent>();
                    using (agent.TokenSource = new CancellationTokenSource())
                    {
                        try
                        {
                            rc = await agent.ExecuteCommand(command);
                        }
                        catch (OperationCanceledException) when(agent.TokenSource.IsCancellationRequested)
                        {
                            s_trace.Info("Agent execution been cancelled.");
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine(StringUtil.Format("An error occured.  {0}", e.Message));
                    s_trace.Error(e);
                    rc = Constants.Agent.ReturnCode.RetryableError;
                }

                return(rc);
            }
        }
예제 #14
0
        // Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener)
        // 0: Agent exit
        // 1: Terminate failure
        // 2: Retriable failure
        // 3: Exit for self update
        public async static Task <int> MainAsync(IHostContext context, string[] args)
        {
            Tracing trace = context.GetTrace("AgentProcess");

            trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}.");
            trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}.");
            var terminal = context.GetService <ITerminal>();

            // Validate the binaries intended for one OS are not running on a different OS.
            switch (Constants.Agent.Platform)
            {
            case Constants.OSPlatform.Linux:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    terminal.WriteLine(StringUtil.Loc("NotLinux"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            case Constants.OSPlatform.OSX:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    terminal.WriteLine(StringUtil.Loc("NotOSX"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            case Constants.OSPlatform.Windows:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    terminal.WriteLine(StringUtil.Loc("NotWindows"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            default:
                terminal.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString()));
                return(Constants.Agent.ReturnCode.TerminatedError);
            }

            try
            {
                trace.Info($"Version: {Constants.Agent.Version}");
                trace.Info($"Commit: {BuildConstants.Source.CommitHash}");
                trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}");
                trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}");

                // Validate directory permissions.
                string agentDirectory = context.GetDirectory(WellKnownDirectory.Root);
                trace.Info($"Validating directory permissions for: '{agentDirectory}'");
                try
                {
                    IOUtil.ValidateExecutePermission(agentDirectory);
                }
                catch (Exception e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                // Parse the command line args.
                var command = new CommandSettings(context, args);
                trace.Info("Arguments parsed");

                // Defer to the Agent class to execute the command.
                IAgent agent = context.GetService <IAgent>();
                using (agent.TokenSource = new CancellationTokenSource())
                {
                    try
                    {
                        return(await agent.ExecuteCommand(command));
                    }
                    catch (OperationCanceledException) when(agent.TokenSource.IsCancellationRequested)
                    {
                        trace.Info("Agent execution been cancelled.");
                        return(Constants.Agent.ReturnCode.Success);
                    }
                    catch (NonRetryableException e)
                    {
                        terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                        trace.Error(e);
                        return(Constants.Agent.ReturnCode.TerminatedError);
                    }
                }
            }
            catch (Exception e)
            {
                terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                trace.Error(e);
                return(Constants.Agent.ReturnCode.RetryableError);
            }
        }
예제 #15
0
        public void CanEnsureConfigure()
        {
            using (TestHostContext tc = CreateTestContext())
            {
                Tracing trace = tc.GetTrace();

                trace.Info("Creating config manager");
                IConfigurationManager configManager = new ConfigurationManager();
                configManager.Initialize(tc);

                trace.Info("Preparing command line arguments");
                var command = new CommandSettings(
                    tc,
                    new[]
                    {
                        "configure",
                        "--url", _expectedServerUrl, 
                        "--agent", _expectedAgentName, 
                        "--pool", _expectedPoolName,  
                        "--work", _expectedWorkFolder,
                        "--auth", _expectedAuthType,
                        "--token", _expectedToken
                    });
                trace.Info("Constructed.");
                
                var expectedPools = new List<TaskAgentPool>() { new TaskAgentPool(_expectedPoolName) { Id = 1 } };
                _agentServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny<string>())).Returns(Task.FromResult(expectedPools));
                
                var expectedAgents = new List<TaskAgent>();
                _agentServer.Setup(x => x.GetAgentsAsync(It.IsAny<int>(), It.IsAny<string>())).Returns(Task.FromResult(expectedAgents));
                
                var expectedAgent = new TaskAgent(_expectedAgentName) { Id = 1 };
                _agentServer.Setup(x => x.AddAgentAsync(It.IsAny<int>(), It.IsAny<TaskAgent>())).Returns(Task.FromResult(expectedAgent));
                _agentServer.Setup(x => x.UpdateAgentAsync(It.IsAny<int>(), It.IsAny<TaskAgent>())).Returns(Task.FromResult(expectedAgent));
                
                trace.Info("Ensuring all the required parameters are available in the command line parameter");
                configManager.ConfigureAsync(command);

                _store.Setup(x => x.IsConfigured()).Returns(true);
                
                trace.Info("Configured, verifying all the parameter value");
                var s = configManager.LoadSettings();
                Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
                Assert.True(s.AgentName.Equals(_expectedAgentName));
                Assert.True(s.PoolName.Equals(_expectedPoolName));
                Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
            }
        }
예제 #16
0
        // Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener)
        // 0: Agent exit
        // 1: Terminate failure
        // 2: Retriable failure
        // 3: Exit for self update
        public async static Task<int> MainAsync(string[] args)
        {
            using (HostContext context = new HostContext("Agent"))
            {
                s_trace = context.GetTrace("AgentProcess");
                s_trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}.");
                s_trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}.");

                // Validate the binaries intended for one OS are not running on a different OS.
                switch (Constants.Agent.Platform)
                {
                    case Constants.OSPlatform.Linux:
                        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                        {
                            Console.WriteLine(StringUtil.Loc("NotLinux"));
                            return Constants.Agent.ReturnCode.TerminatedError;
                        }
                        break;
                    case Constants.OSPlatform.OSX:
                        if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                        {
                            Console.WriteLine(StringUtil.Loc("NotOSX"));
                            return Constants.Agent.ReturnCode.TerminatedError;
                        }
                        break;
                    case Constants.OSPlatform.Windows:
                        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                        {
                            Console.WriteLine(StringUtil.Loc("NotWindows"));
                            return Constants.Agent.ReturnCode.TerminatedError;
                        }
                        break;
                    default:
                        Console.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString()));
                        return Constants.Agent.ReturnCode.TerminatedError;
                }

                int rc = Constants.Agent.ReturnCode.Success;
                try
                {
                    s_trace.Info($"Version: {Constants.Agent.Version}");
                    s_trace.Info($"Commit: {BuildConstants.Source.CommitHash}");
                    s_trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}");
                    s_trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}");

                    //
                    // TODO (bryanmac): Need VsoAgent.exe compat shim for SCM
                    //                  That shim will also provide a compat arg parse 
                    //                  and translate / to -- etc...
                    //

                    // Parse the command line args.
                    var command = new CommandSettings(context, args);
                    s_trace.Info("Arguments parsed");

                    // Defer to the Agent class to execute the command.
                    IAgent agent = context.GetService<IAgent>();
                    using (agent.TokenSource = new CancellationTokenSource())
                    {
                        try
                        {
                            rc = await agent.ExecuteCommand(command);
                        }
                        catch (OperationCanceledException) when (agent.TokenSource.IsCancellationRequested)
                        {
                            s_trace.Info("Agent execution been cancelled.");
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine(StringUtil.Format("An error occured.  {0}", e.Message));
                    s_trace.Error(e);
                    rc = Constants.Agent.ReturnCode.RetryableError;
                }

                return rc;
            }
        }
예제 #17
0
        public void PromptsForWindowsLogonAccount()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.WindowsLogonAccount, // argName
                        StringUtil.Loc("WindowsLogonAccountNameDescription"), // description
                        false, // secret
                        "some default account", // defaultValue
                        Validators.NTAccountValidator, // validator
                        false)) // unattended
                    .Returns("some windows logon account");

                // Act.
                string actual = command.GetWindowsLogonAccount("some default account");

                // Assert.
                Assert.Equal("some windows logon account", actual);
            }
        }
예제 #18
0
        public void GetsFlagUnattended()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--unattended" });

                // Act.
                bool actual = command.Unattended;

                // Assert.
                Assert.True(actual);
            }
        }
예제 #19
0
        public void GetsFlagAcceptTeeEula()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--acceptteeeula" });

                // Act.
                bool actual = command.GetAcceptTeeEula();

                // Assert.
                Assert.True(actual);
            }
        }
예제 #20
0
        public void PromptsForWork()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.Work, // argName
                        StringUtil.Loc("WorkFolderDescription"), // description
                        false, // secret
                        "_work", // defaultValue
                        Validators.NonEmptyValidator, // validator
                        false)) // unattended
                    .Returns("some work");

                // Act.
                string actual = command.GetWork();

                // Assert.
                Assert.Equal("some work", actual);
            }
        }
예제 #21
0
        public async Task <int> LocalRunAsync(CommandSettings command, CancellationToken token)
        {
            Trace.Info(nameof(LocalRunAsync));

            // Warn preview.
            _term.WriteLine("This command is currently in preview. The interface and behavior will change in a future version.");
            if (!command.Unattended)
            {
                _term.WriteLine("Press Enter to continue.");
                _term.ReadLine();
            }

            HostContext.RunMode = RunMode.Local;

            // Resolve the YAML file path.
            string ymlFile = command.GetYml();

            if (string.IsNullOrEmpty(ymlFile))
            {
                string[] ymlFiles =
                    Directory.GetFiles(Directory.GetCurrentDirectory())
                    .Where((string filePath) =>
                {
                    return(filePath.EndsWith(".yml", IOUtil.FilePathStringComparison));
                })
                    .ToArray();
                if (ymlFiles.Length > 1)
                {
                    throw new Exception($"More than one .yml file exists in the current directory. Specify which file to use via the --'{Constants.Agent.CommandLine.Args.Yml}' command line argument.");
                }

                ymlFile = ymlFiles.FirstOrDefault();
            }

            if (string.IsNullOrEmpty(ymlFile))
            {
                throw new Exception($"Unable to find a .yml file in the current directory. Specify which file to use via the --'{Constants.Agent.CommandLine.Args.Yml}' command line argument.");
            }

            // Load the YAML file.
            var parseOptions = new ParseOptions
            {
                MaxFiles = 10,
                MustacheEvaluationMaxResultLength = 512 * 1024, // 512k string length
                MustacheEvaluationTimeout         = TimeSpan.FromSeconds(10),
                MustacheMaxDepth = 5,
            };
            var pipelineParser = new PipelineParser(new PipelineTraceWriter(), new PipelineFileProvider(), parseOptions);

            if (command.WhatIf)
            {
                pipelineParser.DeserializeAndSerialize(
                    defaultRoot: Directory.GetCurrentDirectory(),
                    path: ymlFile,
                    mustacheContext: null,
                    cancellationToken: HostContext.AgentShutdownToken);
                return(Constants.Agent.ReturnCode.Success);
            }

            YamlContracts.Process process = pipelineParser.LoadInternal(
                defaultRoot: Directory.GetCurrentDirectory(),
                path: ymlFile,
                mustacheContext: null,
                cancellationToken: HostContext.AgentShutdownToken);
            ArgUtil.NotNull(process, nameof(process));

            // Verify the current directory is the root of a git repo.
            string repoDirectory = Directory.GetCurrentDirectory();

            if (!Directory.Exists(Path.Combine(repoDirectory, ".git")))
            {
                throw new Exception("Unable to run the build locally. The command must be executed from the root directory of a local git repository.");
            }

            // Verify at least one phase was found.
            if (process.Phases == null || process.Phases.Count == 0)
            {
                throw new Exception($"No phases or steps were discovered from the file: '{ymlFile}'");
            }

            // Filter the phases.
            string phaseName = command.GetPhase();

            if (!string.IsNullOrEmpty(phaseName))
            {
                process.Phases = process.Phases
                                 .Cast <YamlContracts.Phase>()
                                 .Where(x => string.Equals(x.Name, phaseName, StringComparison.OrdinalIgnoreCase))
                                 .Cast <YamlContracts.IPhase>()
                                 .ToList();
                if (process.Phases.Count == 0)
                {
                    throw new Exception($"Phase '{phaseName}' not found.");
                }
            }

            // Verify a phase was specified if more than one phase was found.
            if (process.Phases.Count > 1)
            {
                throw new Exception($"More than one phase was discovered. Use the --{Constants.Agent.CommandLine.Args.Phase} argument to specify a phase.");
            }

            // Get the matrix.
            var phase       = process.Phases[0] as YamlContracts.Phase;
            var queueTarget = phase.Target as QueueTarget;

            // Filter to a specific matrix.
            string matrixName = command.GetMatrix();

            if (!string.IsNullOrEmpty(matrixName))
            {
                if (queueTarget?.Matrix != null)
                {
                    queueTarget.Matrix = queueTarget.Matrix.Keys
                                         .Where(x => string.Equals(x, matrixName, StringComparison.OrdinalIgnoreCase))
                                         .ToDictionary(keySelector: x => x, elementSelector: x => queueTarget.Matrix[x]);
                }

                if (queueTarget?.Matrix == null || queueTarget.Matrix.Count == 0)
                {
                    throw new Exception($"Job configuration matrix '{matrixName}' not found.");
                }
            }

            // Verify a matrix was specified if more than one matrix was found.
            if (queueTarget?.Matrix != null && queueTarget.Matrix.Count > 1)
            {
                throw new Exception($"More than one job configuration matrix was discovered. Use the --{Constants.Agent.CommandLine.Args.Matrix} argument to specify a matrix.");
            }

            // Get the URL - required if missing tasks.
            string url = command.GetUrl(suppressPromptIfEmpty: true);

            if (string.IsNullOrEmpty(url))
            {
                if (!TestAllTasksCached(process, token))
                {
                    url = command.GetUrl(suppressPromptIfEmpty: false);
                }
            }

            if (!string.IsNullOrEmpty(url))
            {
                // Initialize and store the HTTP client.
                var credentialManager = HostContext.GetService <ICredentialManager>();

                // Defaults to PAT authentication.
                string defaultAuthType       = Constants.Configuration.PAT;
                string authType              = command.GetAuth(defaultValue: defaultAuthType);
                ICredentialProvider provider = credentialManager.GetCredentialProvider(authType);
                provider.EnsureCredential(HostContext, command, url);
                _taskStore.HttpClient = new TaskAgentHttpClient(new Uri(url), provider.GetVssCredentials(HostContext));
            }

            var           configStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings    = configStore.GetSettings();

            // Create job message.
            JobInfo        job           = (await ConvertToJobMessagesAsync(process, repoDirectory, token)).Single();
            IJobDispatcher jobDispatcher = null;

            try
            {
                jobDispatcher = HostContext.CreateService <IJobDispatcher>();
                job.RequestMessage.Environment.Variables[Constants.Variables.Agent.RunMode] = RunMode.Local.ToString();
                jobDispatcher.Run(Pipelines.AgentJobRequestMessageUtil.Convert(job.RequestMessage));
                Task jobDispatch = jobDispatcher.WaitAsync(token);
                if (!Task.WaitAll(new[] { jobDispatch }, job.Timeout))
                {
                    jobDispatcher.Cancel(job.CancelMessage);

                    // Finish waiting on the job dispatch task. The call to jobDispatcher.WaitAsync dequeues
                    // the job dispatch task. In the cancel flow, we need to continue awaiting the task instance
                    // (queue is now empty).
                    await jobDispatch;
                }

                // Translate the job result to an agent return code.
                TaskResult jobResult = jobDispatcher.GetLocalRunJobResult(job.RequestMessage);
                switch (jobResult)
                {
                case TaskResult.Succeeded:
                case TaskResult.SucceededWithIssues:
                    return(Constants.Agent.ReturnCode.Success);

                default:
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
            }
            finally
            {
                if (jobDispatcher != null)
                {
                    await jobDispatcher.ShutdownAsync();
                }
            }
        }
예제 #22
0
        public void PassesUnattendedToReadBool()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--unattended" });
                _promptManager
                    .Setup(x => x.ReadBool(
                        Constants.Agent.CommandLine.Flags.AcceptTeeEula, // argName
                        StringUtil.Loc("AcceptTeeEula"), // description
                        false, // defaultValue
                        true)) // unattended
                    .Returns(true);

                // Act.
                bool actual = command.GetAcceptTeeEula();

                // Assert.
                Assert.True(actual);
            }
        }
예제 #23
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                _inConfigStage        = true;
                _term.CancelKeyPress += CtrlCHandler;
                // 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 and/or configures if not configured

                // TODO: Invalid config prints usage

                if (command.Help)
                {
                    PrintUsage();
                    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);
                }

                // Unconfigure, remove config files, service and exit
                if (command.Unconfigure)
                {
                    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);
                    }
                }

                if (command.Run && !configManager.IsConfigured())
                {
                    _term.WriteError(StringUtil.Loc("AgentIsNotConfigured"));
                    PrintUsage();
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                // unattend mode will not prompt for args if not supplied.  Instead will error.
                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);
                    }
                }

                Trace.Info("Done evaluating commands");
                await configManager.EnsureConfiguredAsync(command);

                _inConfigStage = false;

                if (command.NoStart)
                {
                    Trace.Info("No start.");
                    return(Constants.Agent.ReturnCode.Success);
                }

                AgentSettings settings     = configManager.LoadSettings();
                bool          runAsService = configManager.IsServiceConfigured();
                if (command.Run || !runAsService)
                {
                    // Run the agent interactively
                    Trace.Verbose($"Run as service: '{runAsService}'");
                    return(await RunAsync(TokenSource.Token, settings, runAsService));
                }

                if (runAsService)
                {
                    // This is helpful if the user tries to start the agent.listener which is already configured or running as service
                    // However user can execute the agent by calling the run command
                    // TODO: Should we check if the service is running and prompt user to start the service if its not already running?
                    _term.WriteLine(StringUtil.Loc("ConfiguredAsRunAsService"));
                }

                return(Constants.Agent.ReturnCode.Success);
            }
            finally
            {
                _term.CancelKeyPress -= CtrlCHandler;
            }
        }
예제 #24
0
        // Return code definition: (this will be used by service host to determine whether it will re-launch agent.listener)
        // 0: Agent exit
        // 1: Terminate failure
        // 2: Retriable failure
        // 3: Exit for self update
        public async static Task <int> MainAsync(IHostContext context, string[] args)
        {
            Tracing trace = context.GetTrace("AgentProcess");

            trace.Info($"Agent is built for {Constants.Agent.Platform} - {BuildConstants.AgentPackage.PackageName}.");
            trace.Info($"RuntimeInformation: {RuntimeInformation.OSDescription}.");
            var terminal = context.GetService <ITerminal>();

            // Validate the binaries intended for one OS are not running on a different OS.
            switch (Constants.Agent.Platform)
            {
            case Constants.OSPlatform.Linux:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    terminal.WriteLine(StringUtil.Loc("NotLinux"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            case Constants.OSPlatform.OSX:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    terminal.WriteLine(StringUtil.Loc("NotOSX"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            case Constants.OSPlatform.Windows:
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    terminal.WriteLine(StringUtil.Loc("NotWindows"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
                break;

            default:
                terminal.WriteLine(StringUtil.Loc("PlatformNotSupport", RuntimeInformation.OSDescription, Constants.Agent.Platform.ToString()));
                return(Constants.Agent.ReturnCode.TerminatedError);
            }

            try
            {
                trace.Info($"Version: {Constants.Agent.Version}");
                trace.Info($"Commit: {BuildConstants.Source.CommitHash}");
                trace.Info($"Culture: {CultureInfo.CurrentCulture.Name}");
                trace.Info($"UI Culture: {CultureInfo.CurrentUICulture.Name}");

                // Validate directory permissions.
                string agentDirectory = context.GetDirectory(WellKnownDirectory.Root);
                trace.Info($"Validating directory permissions for: '{agentDirectory}'");
                try
                {
                    IOUtil.ValidateExecutePermission(agentDirectory);
                }
                catch (Exception e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

#if OS_WINDOWS
                // Validate PowerShell 3.0 or higher is installed.
                var powerShellExeUtil = context.GetService <IPowerShellExeUtil>();
                try
                {
                    powerShellExeUtil.GetPath();
                }
                catch (Exception e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }

                // Validate .NET Framework 4.5 or higher is installed.
                var netFrameworkUtil = context.GetService <INetFrameworkUtil>();
                if (!netFrameworkUtil.Test(new Version(4, 5)))
                {
                    terminal.WriteError(StringUtil.Loc("MinimumNetFramework"));
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
#endif

                // Parse the command line args.
                var command = new CommandSettings(context, args);
                trace.Info("Arguments parsed");

                // Up front validation, warn for unrecognized commandline args.
                var unknownCommandlines = command.Validate();
                if (unknownCommandlines.Count > 0)
                {
                    terminal.WriteError(StringUtil.Loc("UnrecognizedCmdArgs", string.Join(", ", unknownCommandlines)));
                }

                // Defer to the Agent class to execute the command.
                IAgent agent = context.GetService <IAgent>();
                try
                {
                    return(await agent.ExecuteCommand(command));
                }
                catch (OperationCanceledException) when(context.AgentShutdownToken.IsCancellationRequested)
                {
                    trace.Info("Agent execution been cancelled.");
                    return(Constants.Agent.ReturnCode.Success);
                }
                catch (NonRetryableException e)
                {
                    terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                    trace.Error(e);
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
            }
            catch (Exception e)
            {
                terminal.WriteError(StringUtil.Loc("ErrorOccurred", e.Message));
                trace.Error(e);
                return(Constants.Agent.ReturnCode.RetryableError);
            }
        }
예제 #25
0
        public void PassesUnattendedToReadValue()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[] { "--unattended" });
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.Agent, // argName
                        StringUtil.Loc("AgentName"), // description
                        false, // secret
                        Environment.MachineName, // defaultValue
                        Validators.NonEmptyValidator, // validator
                        true)) // unattended
                    .Returns("some agent");

                // Act.
                string actual = command.GetAgent();

                // Assert.
                Assert.Equal("some agent", actual);
            }
        }
예제 #26
0
        public async Task <int> RunAsync(CommandSettings command, CancellationToken token)
        {
            Trace.Info(nameof(RunAsync));
            var           configStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings    = configStore.GetSettings();

            // Store the HTTP client.
            // todo: fix in master to allow URL to be empty and then rebase on master.
            const string DefaultUrl = "http://127.0.0.1/local-runner-default-url";
            string       url        = command.GetUrl(DefaultUrl);

            if (!string.Equals(url, DefaultUrl, StringComparison.Ordinal))
            {
                var    credentialManager     = HostContext.GetService <ICredentialManager>();
                string authType              = command.GetAuth(defaultValue: Constants.Configuration.Integrated);
                ICredentialProvider provider = credentialManager.GetCredentialProvider(authType);
                provider.EnsureCredential(HostContext, command, url);
                _httpClient = new TaskAgentHttpClient(new Uri(url), provider.GetVssCredentials(HostContext));
            }

            // Load the YAML file.
            string yamlFile = command.GetYaml();

            ArgUtil.File(yamlFile, nameof(yamlFile));
            var parseOptions = new ParseOptions
            {
                MaxFiles = 10,
                MustacheEvaluationMaxResultLength = 512 * 1024, // 512k string length
                MustacheEvaluationTimeout         = TimeSpan.FromSeconds(10),
                MustacheMaxDepth = 5,
            };
            var pipelineParser = new PipelineParser(new PipelineTraceWriter(), new PipelineFileProvider(), parseOptions);

            Pipelines.Process process = pipelineParser.Load(
                defaultRoot: Directory.GetCurrentDirectory(),
                path: yamlFile,
                mustacheContext: null,
                cancellationToken: HostContext.AgentShutdownToken);
            ArgUtil.NotNull(process, nameof(process));
            if (command.WhatIf)
            {
                return(Constants.Agent.ReturnCode.Success);
            }

            // Create job message.
            IJobDispatcher jobDispatcher = null;

            try
            {
                jobDispatcher = HostContext.CreateService <IJobDispatcher>();
                foreach (JobInfo job in await ConvertToJobMessagesAsync(process, token))
                {
                    job.RequestMessage.Environment.Variables[Constants.Variables.Agent.RunMode] = RunMode.Local.ToString();
                    jobDispatcher.Run(job.RequestMessage);
                    Task jobDispatch = jobDispatcher.WaitAsync(token);
                    if (!Task.WaitAll(new[] { jobDispatch }, job.Timeout))
                    {
                        jobDispatcher.Cancel(job.CancelMessage);

                        // Finish waiting on the same job dispatch task. The first call to WaitAsync dequeues
                        // the dispatch task and then proceeds to wait on it. So we need to continue awaiting
                        // the task instance (queue is now empty).
                        await jobDispatch;
                    }
                }
            }
            finally
            {
                if (jobDispatcher != null)
                {
                    await jobDispatcher.ShutdownAsync();
                }
            }

            return(Constants.Agent.ReturnCode.Success);
        }
예제 #27
0
        public void PromptsForAuth()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.Auth, // argName
                        StringUtil.Loc("AuthenticationType"), // description
                        false, // secret
                        "some default auth", // defaultValue
                        Validators.AuthSchemeValidator, // validator
                        false)) // unattended
                    .Returns("some auth");

                // Act.
                string actual = command.GetAuth("some default auth");

                // Assert.
                Assert.Equal("some auth", actual);
            }
        }
예제 #28
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.IsHelp())
                {
                    PrintUsage(command);
                    return Constants.Agent.ReturnCode.Success;
                }

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

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

                if (command.IsDiagnostics())
                {
                    PrintBanner();
                    _term.WriteLine("Running Diagnostics Only...");
                    _term.WriteLine(string.Empty);
                    DiagnosticTests diagnostics = new DiagnosticTests(_term);
                    diagnostics.Execute();
                    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.IsConfigureCommand())
                {
                    PrintBanner();
                    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.IsRemoveCommand())
                {
                    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;

                // warmup agent process (JIT/CLR)
                // In scenarios where the agent is single use (used and then thrown away), the system provisioning the agent can call `agent.listener --warmup` before the machine is made available to the pool for use.
                // this will optimizes the agent process startup time.
                if (command.IsWarmupCommand())
                {
                    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.Agent.ReturnCode.Success;
                }

                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 (PlatformUtil.RunningOnWindows)
                {
                    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");
                        }
                    }
                }
                // Run the agent interactively or as service
                return await RunAsync(settings, command.GetRunOnce());
            }
            finally
            {
                _term.CancelKeyPress -= CtrlCHandler;
                HostContext.Unloading -= Agent_Unloading;
                _completedCommand.Set();
            }
        }
예제 #29
0
        public void PromptsForRunAsService()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                _promptManager
                    .Setup(x => x.ReadBool(
                        Constants.Agent.CommandLine.Flags.RunAsService, // argName
                        StringUtil.Loc("RunAgentAsServiceDescription"), // description
                        false, // defaultValue
                        false)) // unattended
                    .Returns(true);

                // Act.
                bool actual = command.GetRunAsService();

                // Assert.
                Assert.True(actual);
            }
        }
예제 #30
0
        public async Task<int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                WebProxy.ApplyProxySettings();
                _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 and/or configures if not configured

                // TODO: Invalid config prints usage

                if (command.Help)
                {
                    PrintUsage();
                    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;
                }

                // Unconfigure, remove config files, service and exit
                if (command.Unconfigure)
                {
                    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;
                    }
                }

                if (command.Run && !configManager.IsConfigured())
                {
                    _term.WriteError(StringUtil.Loc("AgentIsNotConfigured"));
                    PrintUsage();
                    return Constants.Agent.ReturnCode.TerminatedError;
                }

                // unattend mode will not prompt for args if not supplied.  Instead will error.
                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;
                    }
                }

                Trace.Info("Done evaluating commands");
                await configManager.EnsureConfiguredAsync(command);

                _inConfigStage = false;

                if (command.NoStart)
                {
                    Trace.Info("No start.");
                    return Constants.Agent.ReturnCode.Success;
                }

                AgentSettings settings = configManager.LoadSettings();
                bool runAsService = configManager.IsServiceConfigured();
                if (command.Run || !runAsService)
                {
                    // Run the agent interactively
                    Trace.Verbose($"Run as service: '{runAsService}'");
                    return await RunAsync(TokenSource.Token, settings, runAsService);
                }

                if (runAsService)
                {
                    // This is helpful if the user tries to start the agent.listener which is already configured or running as service
                    // However user can execute the agent by calling the run command
                    // TODO: Should we check if the service is running and prompt user to start the service if its not already running?
                    _term.WriteLine(StringUtil.Loc("ConfiguredAsRunAsService"));
                }

                return Constants.Agent.ReturnCode.Success;
            }
            finally
            {
                _term.CancelKeyPress -= CtrlCHandler;
                HostContext.Unloading -= Agent_Unloading;
                _completedCommand.Set();
            }
        }
예제 #31
0
        public void PromptsForToken()
        {
            using (TestHostContext hc = CreateTestContext())
            {
                // Arrange.
                var command = new CommandSettings(hc, args: new string[0]);
                _promptManager
                    .Setup(x => x.ReadValue(
                        Constants.Agent.CommandLine.Args.Token, // argName
                        StringUtil.Loc("PersonalAccessToken"), // description
                        true, // secret
                        string.Empty, // defaultValue
                        Validators.NonEmptyValidator, // validator
                        false)) // unattended
                    .Returns("some token");

                // Act.
                string actual = command.GetToken();

                // Assert.
                Assert.Equal("some token", actual);
            }
        }
예제 #32
0
 public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl)
 {
 }
예제 #33
0
        public async void TestExecuteCommandForRunAsService(string[] args, bool configureAsService, Times expectedTimes)
        {
            using (var hc = new TestHostContext(this))
            {
                hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
                hc.SetSingleton<IPromptManager>(_promptManager.Object);
                hc.SetSingleton<IMessageListener>(_messageListener.Object);

                var command = new CommandSettings(hc, args);

                _configurationManager.Setup(x => x.IsConfigured()).Returns(true);
                _configurationManager.Setup(x => x.LoadSettings())
                    .Returns(new AgentSettings { });
                _configurationManager.Setup(x => x.IsServiceConfigured()).Returns(configureAsService);
                _messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
                    .Returns(Task.FromResult(false));

                var agent = new Agent.Listener.Agent();
                agent.Initialize(hc);
                agent.TokenSource = new CancellationTokenSource();
                await agent.ExecuteCommand(command);

                _messageListener.Verify(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()), expectedTimes);
            }
        }