Ejemplo n.º 1
0
        public async Task<Dictionary<string, string>> GetCapabilitiesAsync(AgentSettings settings, CancellationToken cancellationToken)
        {
            Trace.Entering();
            ArgUtil.NotNull(settings, nameof(settings));

            // Get the providers.
            var extensionManager = HostContext.GetService<IExtensionManager>();
            IEnumerable<ICapabilitiesProvider> providers =
                extensionManager
                .GetExtensions<ICapabilitiesProvider>()
                ?.OrderBy(x => x.Order);

            // Initialize a dictionary of capabilities.
            var capabilities = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            // Add each capability returned from each provider.
            foreach (ICapabilitiesProvider provider in providers ?? new ICapabilitiesProvider[0])
            {
                foreach (Capability capability in await provider.GetCapabilitiesAsync(settings, cancellationToken) ?? new List<Capability>())
                {
                    capabilities[capability.Name] = capability.Value;
                }
            }

            return capabilities;
        }
        public override void GenerateScripts(AgentSettings settings)
        {
            Trace.Entering();

            CalculateServiceName(settings, _svcNamePattern, _svcDisplayPattern);
            try
            {
                string svcShPath = Path.Combine(IOUtil.GetRootPath(), _svcShName);

                // TODO: encoding?
                // TODO: Loc strings formatted into MSG_xxx vars in shellscript
                string svcShContent = File.ReadAllText(Path.Combine(IOUtil.GetBinPath(), _shTemplate));
                var tokensToReplace = new Dictionary<string, string>
                                          {
                                              { "{{SvcDescription}}", ServiceDisplayName },
                                              { "{{SvcNameVar}}", ServiceName }
                                          };

                svcShContent = tokensToReplace.Aggregate(
                    svcShContent,
                    (current, item) => current.Replace(item.Key, item.Value));

                //TODO: encoding?
                File.WriteAllText(svcShPath, svcShContent);

                var unixUtil = HostContext.CreateService<IUnixUtil>();
                unixUtil.ChmodAsync("755", svcShPath).GetAwaiter().GetResult();
            }
            catch (Exception e)
            {
                Trace.Error(e);
                throw;
            }
        }
        public int Order => 99; // Process last to override prior.

        public Task<List<Capability>> GetCapabilitiesAsync(AgentSettings settings, CancellationToken cancellationToken)
        {
            ArgUtil.NotNull(settings, nameof(settings));
            var capabilities = new List<Capability>();
            Add(capabilities, "Agent.Name", settings.AgentName ?? string.Empty);
            switch (Constants.Agent.Platform)
            {
                case Constants.OSPlatform.Linux:
                    Add(capabilities, "Agent.OS", "linux");
                    break;
                case Constants.OSPlatform.OSX:
                    Add(capabilities, "Agent.OS", "darwin");
                    break;
                case Constants.OSPlatform.Windows:
                    Add(capabilities, "Agent.OS", Environment.GetEnvironmentVariable("OS"));
                    break;
            }

#if OS_WINDOWS
            Add(capabilities, "Agent.OSVersion", GetOSVersionString());
            Add(capabilities, "Cmd", Environment.GetEnvironmentVariable("comspec"));
#endif
            Add(capabilities, "Agent.Version", Constants.Agent.Version);
            Add(capabilities, "Agent.ComputerName", Environment.MachineName ?? string.Empty);
            return Task.FromResult(capabilities);
        }
        public async Task<List<Capability>> GetCapabilitiesAsync(AgentSettings settings, CancellationToken cancellationToken)
        {
            Trace.Entering();

            // Check the cache.
            if (_capabilities != null)
            {
                Trace.Info("Found in cached.");
                return _capabilities;
            }

            // Build the list of capabilities.
            var builder = new CapabilitiesBuilder(HostContext, cancellationToken);
            builder.Check(
                name: "AndroidSDK",
                fileName: "android",
                filePaths: new[]
                {
                    Path.Combine(Environment.GetEnvironmentVariable("ANDROID_STUDIO") ?? string.Empty, "tools/android"),
                    Path.Combine(Environment.GetEnvironmentVariable("HOME") ?? string.Empty, "Library/Developer/Xamarin/android-sdk-macosx/tools/android"),
                });
            builder.Check(name: "ant");
            builder.Check(name: "bundler", fileName: "bundle");
            builder.Check(name: "clang");
            builder.Check(name: "cmake");
            builder.Check(name: "curl");
            builder.Check(name: "git");
            builder.Check(name: "gulp");
            builder.Check(name: "java");
            builder.Check(name: "JDK", fileName: "javac");
            builder.Check(name: "make");
            builder.Check(name: "maven", fileName: "mvn");
            builder.Check(name: "MSBuild", fileName: "xbuild");
            builder.Check(name: "node.js", fileName: "node");
            builder.Check(name: "node.js", fileName: "nodejs");
            builder.Check(name: "npm");
            builder.Check(name: "python");
            builder.Check(name: "python3");
            builder.Check(name: "sh");
            builder.Check(name: "subversion", fileName: "svn");
            builder.Check(name: "ruby");
            builder.Check(name: "rake");
            builder.Check(
                name: "Xamarin.iOS",
                fileName: "mdtool",
                filePaths: new string[] { "/Applications/Xamarin Studio.app/Contents/MacOS/mdtool" });
            builder.Check(
                name: "Xamarin.Android",
                fileName: "mandroid",
                filePaths: new string[] { "/Library/Frameworks/Xamarin.Android.framework/Commands/mandroid" });
            await builder.CheckToolOutputAsync(
                name: "xcode",
                fileName: "xcode-select",
                arguments: "-p");

            // Cache and return the values.
            _capabilities = builder.ToList();
            return _capabilities;
        }
Ejemplo n.º 5
0
        public override bool ConfigureService(
            AgentSettings settings,
            CommandSettings command)
        {
            Trace.Entering();

            throw new NotSupportedException("Systemd Configure Service");
        }
Ejemplo n.º 6
0
 public MessageListenerL0()
 {
     _settings = new AgentSettings { AgentId = 1, AgentName = "myagent", PoolId = 123, PoolName = "default", ServerUrl = "http://myserver", WorkFolder = "_work" };
     _config = new Mock<IConfigurationManager>();
     _config.Setup(x => x.LoadSettings()).Returns(_settings);
     _agentServer = new Mock<IAgentServer>();
     _credMgr = new Mock<ICredentialManager>();
     _capabilitiesManager = new Mock<ICapabilitiesManager>();
 }
        public int Order => 1; // Process first so other providers can override.

        public Task<List<Capability>> GetCapabilitiesAsync(AgentSettings settings, CancellationToken cancellationToken)
        {
            Trace.Entering();
            var capabilities = new List<Capability>();

            // Initialize the ignored hash set.
#if OS_WINDOWS
            var comparer = StringComparer.OrdinalIgnoreCase;
#else
            var comparer = StringComparer.Ordinal;
#endif
            var ignored = new HashSet<string>(s_wellKnownIgnored, comparer);

            // Also ignore env vars specified by the 'VSO_AGENT_IGNORE' env var.
            IDictionary variables = Environment.GetEnvironmentVariables();
            if (variables.Contains(CustomIgnore))
            {
                IEnumerable<string> additionalIgnored =
                    (variables[CustomIgnore] as string ?? string.Empty)
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => x.Trim())
                    .Where(x => !string.IsNullOrEmpty(x));
                foreach (string ignore in additionalIgnored)
                {
                    Trace.Info($"Ignore: '{ignore}'");
                    ignored.Add(ignore); // Handles duplicates gracefully.
                }
            }

            // Get filtered env vars.
            IEnumerable<string> names =
                variables.Keys
                .Cast<string>()
                .Where(x => !string.IsNullOrEmpty(x))
                .OrderBy(x => x.ToUpperInvariant());
            foreach (string name in names)
            {
                string value = variables[name] as string ?? string.Empty;
                if (ignored.Contains(name) || value.Length >= IgnoreValueLength)
                {
                    Trace.Info($"Skipping: '{name}'");
                    continue;
                }

                Trace.Info($"Adding '{name}': '{value}'");
                capabilities.Add(new Capability(name, value));
            }

            return Task.FromResult(capabilities);
        }
 public void ProcessParameterDeserializer_ConvertsExportedAgentSettings_TFS()
 {
     var agentSettings = new AgentSettings
     {
         TagComparison = TagComparison.MatchExactly,
         MaxExecutionTime = TimeSpan.MaxValue,
         MaxWaitTime = TimeSpan.MaxValue,
         Name = "test",
         Tags = new StringList("tagA,tagB")
     };
     var expBuildDef = this.Serialize(new { TfvcAgentSettings = (AgentSettingsBuildParameter)agentSettings });
     var procParam = new[] { "AgentSettings", expBuildDef };
     ExportedProcessParameterTransformer.ProcessParameterDeserializer(procParam).ShouldBeEquivalentTo(agentSettings);
 }
Ejemplo n.º 9
0
        protected void CalculateServiceName(AgentSettings settings, string serviceNamePattern, string serviceDisplayNamePattern)
        {
            Trace.Info(nameof(this.CalculateServiceName));
            var accountName = new Uri(settings.ServerUrl).Host.Split('.').FirstOrDefault();

            if (string.IsNullOrEmpty(accountName))
            {
                // TODO: Localize this error message:
                throw new InvalidOperationException("CannotFindHostName");
            }

            ServiceName = StringUtil.Format(serviceNamePattern, accountName, settings.AgentName);
            ServiceDisplayName = StringUtil.Format(serviceDisplayNamePattern, accountName, settings.AgentName);
        }
        public async void TestGetCapabilities()
        {
            using (var hc = new TestHostContext(this))
            using (var tokenSource = new CancellationTokenSource())
            {
                // Arrange
                var provider = new AgentCapabilitiesProvider();
                provider.Initialize(hc);
                var settings = new AgentSettings() { AgentName = "IAmAgent007" };

                // Act
                List<Capability> capabilities = await provider.GetCapabilitiesAsync(settings, tokenSource.Token);

                // Assert
                Assert.NotNull(capabilities);
                Capability agentNameCapability = capabilities.SingleOrDefault(x => string.Equals(x.Name, "Agent.Name", StringComparison.Ordinal));
                Assert.NotNull(agentNameCapability);
                Assert.Equal("IAmAgent007", agentNameCapability.Value);
            }
        }
 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);
     }
 }
        public async Task<List<Capability>> GetCapabilitiesAsync(AgentSettings settings, CancellationToken cancellationToken)
        {
            Trace.Entering();
            var capabilities = new List<Capability>();
            string powerShellExe = HostContext.GetService<IPowerShellExeUtil>().GetPath();
            string scriptFile = Path.Combine(IOUtil.GetBinPath(), "powershell", "Add-Capabilities.ps1").Replace("'", "''");
            ArgUtil.File(scriptFile, nameof(scriptFile));
            string arguments = $@"-NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "". '{scriptFile}'""";
            using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
            {
                processInvoker.OutputDataReceived +=
                    (object sender, ProcessDataReceivedEventArgs args) =>
                    {
                        Trace.Info($"STDOUT: {args.Data}");
                        Capability capability;
                        if (TryParseCapability(args.Data, out capability))
                        {
                            Trace.Info($"Adding '{capability.Name}': '{capability.Value}'");
                            capabilities.Add(capability);
                        }
                    };
                processInvoker.ErrorDataReceived +=
                    (object sender, ProcessDataReceivedEventArgs args) =>
                    {
                        Trace.Info($"STDERR: {args.Data}");
                    };
                await processInvoker.ExecuteAsync(
                    workingDirectory: Path.GetDirectoryName(scriptFile),
                    fileName: powerShellExe,
                    arguments: arguments,
                    environment: null,
                    cancellationToken: cancellationToken);
            }

            return capabilities;
        }
Ejemplo n.º 13
0
        public async Task<Boolean> CreateSessionAsync(CancellationToken token)
        {
            Trace.Entering();

            // Settings
            var configManager = HostContext.GetService<IConfigurationManager>();
            _settings = configManager.LoadSettings();
            int agentPoolId = _settings.PoolId;
            var serverUrl = _settings.ServerUrl;
            Trace.Info(_settings);

            // Capabilities.
            // TODO: LOC
            _term.WriteLine("Scanning for tool capabilities.");
            Dictionary<string, string> systemCapabilities = await HostContext.GetService<ICapabilitiesManager>().GetCapabilitiesAsync(_settings, token);

            // Create connection.
            Trace.Verbose("Loading Credentials");
            var credMgr = HostContext.GetService<ICredentialManager>();
            VssCredentials creds = credMgr.LoadCredentials();
            Uri uri = new Uri(serverUrl);
            VssConnection conn = ApiUtil.CreateConnection(uri, creds);

            var agent = new TaskAgentReference
            {
                Id = _settings.AgentId,
                Name = _settings.AgentName,
                Version = Constants.Agent.Version,
            };
            string sessionName = $"{Environment.MachineName ?? "AGENT"}";
            var taskAgentSession = new TaskAgentSession(sessionName, agent, systemCapabilities);

            var agentSvr = HostContext.GetService<IAgentServer>();
            string errorMessage = string.Empty;
            bool encounteringError = false;
            // TODO: LOC
            _term.WriteLine("Connecting to the server.");
            while (true)
            {
                token.ThrowIfCancellationRequested();
                Trace.Info($"Attempt to create session.");
                try
                {
                    Trace.Info("Connecting to the Agent Server...");
                    await agentSvr.ConnectAsync(conn);

                    Session = await agentSvr.CreateAgentSessionAsync(
                                                        _settings.PoolId,
                                                        taskAgentSession,
                                                        token);

                    Trace.Info($"Session created.");
                    if (encounteringError)
                    {
                        _term.WriteLine(StringUtil.Loc("QueueConnected", DateTime.UtcNow));
                        _sessionCreationExceptionTracker.Clear();
                        encounteringError = false;
                    }

                    return true;
                }
                catch (OperationCanceledException) when (token.IsCancellationRequested)
                {
                    Trace.Info("Session creation has been cancelled.");
                    throw;
                }
                catch (Exception ex)
                {
                    Trace.Error("Catch exception during create session.");
                    Trace.Error(ex);

                    if (!IsSessionCreationExceptionRetriable(ex))
                    {
                        _term.WriteError(StringUtil.Loc("SessionCreateFailed", ex.Message));
                        return false;
                    }

                    if (!encounteringError) //print the message only on the first error
                    {
                        _term.WriteError(StringUtil.Loc("QueueConError", DateTime.UtcNow, ex.Message, _sessionCreationRetryInterval.TotalSeconds));
                        encounteringError = true;
                    }

                    Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds);
                    await HostContext.Delay(_sessionCreationRetryInterval, token);
                }
            }
        }
Ejemplo n.º 14
0
        private TestHostContext CreateTestContext(CancellationTokenSource _tokenSource, [CallerMemberName] String testName = "")
        {
            var hc = new TestHostContext(this, testName);

            _jobEc             = new Agent.Worker.ExecutionContext();
            _taskManager       = new Mock <ITaskManager>();
            _jobServerQueue    = new Mock <IJobServerQueue>();
            _config            = new Mock <IConfigurationStore>();
            _logger            = new Mock <IPagingLogger>();
            _proxy             = new Mock <IVstsAgentWebProxy>();
            _cert              = new Mock <IAgentCertificateManager>();
            _express           = new Mock <IExpressionManager>();
            _containerProvider = new Mock <IContainerOperationProvider>();
            _logPlugin         = new Mock <IAgentLogPlugin>();

            TaskRunner step1  = new TaskRunner();
            TaskRunner step2  = new TaskRunner();
            TaskRunner step3  = new TaskRunner();
            TaskRunner step4  = new TaskRunner();
            TaskRunner step5  = new TaskRunner();
            TaskRunner step6  = new TaskRunner();
            TaskRunner step7  = new TaskRunner();
            TaskRunner step8  = new TaskRunner();
            TaskRunner step9  = new TaskRunner();
            TaskRunner step10 = new TaskRunner();
            TaskRunner step11 = new TaskRunner();
            TaskRunner step12 = new TaskRunner();

            _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>()));
            var settings = new AgentSettings
            {
                AgentId    = 1,
                AgentName  = "agent1",
                ServerUrl  = "https://test.visualstudio.com",
                WorkFolder = "_work",
            };

            _config.Setup(x => x.GetSettings())
            .Returns(settings);

            _proxy.Setup(x => x.ProxyAddress)
            .Returns(string.Empty);

            TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
            TimelineReference timeline          = new Timeline(Guid.NewGuid());
            JobEnvironment    environment       = new JobEnvironment();

            environment.Variables[Constants.Variables.System.Culture] = "en-US";
            environment.SystemConnection = new ServiceEndpoint()
            {
                Name          = WellKnownServiceEndpointNames.SystemVssConnection,
                Url           = new Uri("https://test.visualstudio.com"),
                Authorization = new EndpointAuthorization()
                {
                    Scheme = "Test",
                }
            };
            environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";

            List <TaskInstance> tasks = new List <TaskInstance>()
            {
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task1",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task2",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task3",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task4",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task5",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task6",
                },
                new TaskInstance()
                {
                    InstanceId  = Guid.NewGuid(),
                    DisplayName = "task7",
                },
            };

            Guid JobId = Guid.NewGuid();

            _message = Pipelines.AgentJobRequestMessageUtil.Convert(new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks));

            _taskManager.Setup(x => x.DownloadAsync(It.IsAny <IExecutionContext>(), It.IsAny <IEnumerable <Pipelines.TaskStep> >()))
            .Returns(Task.CompletedTask);

            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task1")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = new ExecutionData(),
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task2")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = new ExecutionData(),
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task3")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = null,
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task4")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = null,
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task5")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = null,
                    PostJobExecution = new ExecutionData(),
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task6")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = new ExecutionData(),
                    Execution        = new ExecutionData(),
                    PostJobExecution = null,
                },
            });
            _taskManager.Setup(x => x.Load(It.Is <Pipelines.TaskStep>(t => t.DisplayName == "task7")))
            .Returns(new Definition()
            {
                Data = new DefinitionData()
                {
                    PreJobExecution  = null,
                    Execution        = new ExecutionData(),
                    PostJobExecution = new ExecutionData(),
                },
            });

            hc.SetSingleton(_taskManager.Object);
            hc.SetSingleton(_config.Object);
            hc.SetSingleton(_jobServerQueue.Object);
            hc.SetSingleton(_proxy.Object);
            hc.SetSingleton(_cert.Object);
            hc.SetSingleton(_express.Object);
            hc.SetSingleton(_containerProvider.Object);
            hc.SetSingleton(_logPlugin.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // jobcontext logger
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // init step logger
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step 1
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object); // step 12

            hc.EnqueueInstance <ITaskRunner>(step1);
            hc.EnqueueInstance <ITaskRunner>(step2);
            hc.EnqueueInstance <ITaskRunner>(step3);
            hc.EnqueueInstance <ITaskRunner>(step4);
            hc.EnqueueInstance <ITaskRunner>(step5);
            hc.EnqueueInstance <ITaskRunner>(step6);
            hc.EnqueueInstance <ITaskRunner>(step7);
            hc.EnqueueInstance <ITaskRunner>(step8);
            hc.EnqueueInstance <ITaskRunner>(step9);
            hc.EnqueueInstance <ITaskRunner>(step10);
            hc.EnqueueInstance <ITaskRunner>(step11);
            hc.EnqueueInstance <ITaskRunner>(step12);

            _jobEc.Initialize(hc);
            _jobEc.InitializeJob(_message, _tokenSource.Token);
            return(hc);
        }
Ejemplo n.º 15
0
        public ConfigurationManagerL0()
        {
            _agentServer        = new Mock <IAgentServer>();
            _locationServer     = new Mock <ILocationServer>();
            _credMgr            = new Mock <ICredentialManager>();
            _promptManager      = new Mock <IPromptManager>();
            _store              = new Mock <IConfigurationStore>();
            _extnMgr            = new Mock <IExtensionManager>();
            _rsaKeyManager      = new Mock <IRSAKeyManager>();
            _machineGroupServer = new Mock <IDeploymentGroupServer>();
            _vstsAgentWebProxy  = new Mock <IVstsAgentWebProxy>();
            _cert = new Mock <IAgentCertificateManager>();

#if OS_WINDOWS
            _serviceControlManager = new Mock <IWindowsServiceControlManager>();
#endif

#if !OS_WINDOWS
            _serviceControlManager = new Mock <ILinuxServiceControlManager>();
#endif

            _capabilitiesManager = new CapabilitiesManager();

            var expectedAgent = new TaskAgent(_expectedAgentName)
            {
                Id = 1
            };
            var expectedDeploymentMachine = new DeploymentMachine()
            {
                Agent = expectedAgent, Id = _expectedDeploymentMachineId
            };
            expectedAgent.Authorization = new TaskAgentAuthorization
            {
                ClientId         = Guid.NewGuid(),
                AuthorizationUrl = new Uri("http://localhost:8080/tfs"),
            };

            var connectionData = new ConnectionData()
            {
                InstanceId     = Guid.NewGuid(),
                DeploymentType = DeploymentFlags.Hosted,
                DeploymentId   = Guid.NewGuid()
            };
            _agentServer.Setup(x => x.ConnectAsync(It.IsAny <VssConnection>())).Returns(Task.FromResult <object>(null));
            _locationServer.Setup(x => x.ConnectAsync(It.IsAny <VssConnection>())).Returns(Task.FromResult <object>(null));
            _locationServer.Setup(x => x.GetConnectionDataAsync()).Returns(Task.FromResult <ConnectionData>(connectionData));
            _machineGroupServer.Setup(x => x.ConnectAsync(It.IsAny <VssConnection>())).Returns(Task.FromResult <object>(null));
            _machineGroupServer.Setup(x => x.UpdateDeploymentTargetsAsync(It.IsAny <Guid>(), It.IsAny <int>(), It.IsAny <List <DeploymentMachine> >()));
            _machineGroupServer.Setup(x => x.AddDeploymentTargetAsync(It.IsAny <Guid>(), It.IsAny <int>(), It.IsAny <DeploymentMachine>())).Returns(Task.FromResult(expectedDeploymentMachine));
            _machineGroupServer.Setup(x => x.ReplaceDeploymentTargetAsync(It.IsAny <Guid>(), It.IsAny <int>(), It.IsAny <int>(), It.IsAny <DeploymentMachine>())).Returns(Task.FromResult(expectedDeploymentMachine));
            _machineGroupServer.Setup(x => x.GetDeploymentTargetsAsync(It.IsAny <Guid>(), It.IsAny <int>(), It.IsAny <string>())).Returns(Task.FromResult(new List <DeploymentMachine>()
            {
            }));
            _machineGroupServer.Setup(x => x.DeleteDeploymentTargetAsync(It.IsAny <string>(), It.IsAny <int>(), It.IsAny <int>())).Returns(Task.FromResult <object>(null));

            _store.Setup(x => x.IsConfigured()).Returns(false);
            _store.Setup(x => x.HasCredentials()).Returns(false);
            _store.Setup(x => x.GetSettings()).Returns(() => _configMgrAgentSettings);

            _store.Setup(x => x.SaveSettings(It.IsAny <AgentSettings>()))
            .Callback((AgentSettings settings) =>
            {
                _configMgrAgentSettings = settings;
            });

            _credMgr.Setup(x => x.GetCredentialProvider(It.IsAny <string>())).Returns(new TestAgentCredential());

#if !OS_WINDOWS
            _serviceControlManager.Setup(x => x.GenerateScripts(It.IsAny <AgentSettings>()));
#endif

            var expectedPools = new List <TaskAgentPool>()
            {
                new TaskAgentPool(_expectedPoolName)
                {
                    Id = _expectedPoolId
                }
            };
            _agentServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny <string>(), It.IsAny <TaskAgentPoolType>())).Returns(Task.FromResult(expectedPools));

            var expectedAgents = new List <TaskAgent>();
            _agentServer.Setup(x => x.GetAgentsAsync(It.IsAny <int>(), It.IsAny <string>())).Returns(Task.FromResult(expectedAgents));

            _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));

            rsa = new RSACryptoServiceProvider(2048);

            _rsaKeyManager.Setup(x => x.CreateKey()).Returns(rsa);
        }
Ejemplo n.º 16
0
 public abstract void GenerateScripts(AgentSettings settings);
Ejemplo n.º 17
0
        //create worker manager, create message listener and start listening to the queue
        private async Task<int> RunAsync(CancellationToken token, AgentSettings settings, bool runAsService)
        {
            Trace.Info(nameof(RunAsync));

            // Load the settings.
            _poolId = settings.PoolId;

            _listener = HostContext.GetService<IMessageListener>();
            if (!await _listener.CreateSessionAsync(token))
            {
                return Constants.Agent.ReturnCode.TerminatedError;
            }

            _term.WriteLine(StringUtil.Loc("ListenForJobs", DateTime.UtcNow));

            IJobDispatcher jobDispatcher = null;
            CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
            try
            {
                var notification = HostContext.GetService<IJobNotification>();
                notification.StartClient(settings.NotificationPipeName, token);
                bool disableAutoUpdate = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("agent.disableupdate"));
                bool autoUpdateInProgress = false;
                Task<bool> selfUpdateTask = null;
                jobDispatcher = HostContext.CreateService<IJobDispatcher>();

                while (!token.IsCancellationRequested)
                {
                    TaskAgentMessage message = null;
                    bool skipMessageDeletion = false;
                    try
                    {
                        Task<TaskAgentMessage> getNextMessage = _listener.GetNextMessageAsync(messageQueueLoopTokenSource.Token);
                        if (autoUpdateInProgress)
                        {
                            Trace.Verbose("Auto update task running at backend, waiting for getNextMessage or selfUpdateTask to finish.");
                            Task completeTask = await Task.WhenAny(getNextMessage, selfUpdateTask);
                            if (completeTask == selfUpdateTask)
                            {
                                autoUpdateInProgress = false;
                                if (await selfUpdateTask)
                                {
                                    Trace.Info("Auto update task finished at backend, an agent update is ready to apply exit the current agent instance.");
                                    Trace.Info("Stop message queue looping.");
                                    messageQueueLoopTokenSource.Cancel();
                                    try
                                    {
                                        await getNextMessage;
                                    }
                                    catch (Exception ex)
                                    {
                                        Trace.Info($"Ignore any exception after cancel message loop. {ex}");
                                    }

                                    return Constants.Agent.ReturnCode.AgentUpdating;
                                }
                                else
                                {
                                    Trace.Info("Auto update task finished at backend, there is no available agent update needs to apply, continue message queue looping.");
                                }
                            }
                        }

                        message = await getNextMessage; //get next message
                        if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase))
                        {
                            if (disableAutoUpdate)
                            {
                                Trace.Info("Refresh message received, skip autoupdate since environment variable agent.disableupdate is set.");
                            }
                            else
                            {
                                if (autoUpdateInProgress == false)
                                {
                                    autoUpdateInProgress = true;
                                    var selfUpdater = HostContext.GetService<ISelfUpdater>();
                                    selfUpdateTask = selfUpdater.SelfUpdate(jobDispatcher, !runAsService, token);
                                    Trace.Info("Refresh message received, kick-off selfupdate background process.");
                                }
                                else
                                {
                                    Trace.Info("Refresh message received, skip autoupdate since a previous autoupdate is already running.");
                                }
                            }
                        }
                        else if (string.Equals(message.MessageType, JobRequestMessage.MessageType, StringComparison.OrdinalIgnoreCase))
                        {
                            if (autoUpdateInProgress)
                            {
                                skipMessageDeletion = true;
                            }
                            else
                            {
                                var newJobMessage = JsonUtility.FromString<JobRequestMessage>(message.Body);
                                jobDispatcher.Run(newJobMessage);
                            }
                        }
                        else if (string.Equals(message.MessageType, JobCancelMessage.MessageType, StringComparison.OrdinalIgnoreCase))
                        {
                            var cancelJobMessage = JsonUtility.FromString<JobCancelMessage>(message.Body);
                            bool jobCancelled = jobDispatcher.Cancel(cancelJobMessage);
                            skipMessageDeletion = autoUpdateInProgress && !jobCancelled;
                        }
                    }
                    finally
                    {
                        if (!skipMessageDeletion && message != null)
                        {
                            try
                            {
                                await DeleteMessageAsync(message);
                            }
                            catch (Exception ex)
                            {
                                Trace.Error($"Catch exception during delete message from message queue. message id: {message.MessageId}");
                                Trace.Error(ex);
                            }
                            finally
                            {
                                message = null;
                            }
                        }
                    }
                }
            }
            finally
            {
                if (jobDispatcher != null)
                {
                    await jobDispatcher.ShutdownAsync();
                }

                //TODO: make sure we don't mask more important exception
                await _listener.DeleteSessionAsync();
                
                messageQueueLoopTokenSource.Dispose();
            }

            return Constants.Agent.ReturnCode.Success;
        }
Ejemplo n.º 18
0
        //create worker manager, create message listener and start listening to the queue
        private async Task <int> RunAsync(AgentSettings settings)
        {
            Trace.Info(nameof(RunAsync));
            _listener = HostContext.GetService <IMessageListener>();
            if (!await _listener.CreateSessionAsync(HostContext.AgentShutdownToken))
            {
                return(Constants.Agent.ReturnCode.TerminatedError);
            }

            _term.WriteLine(StringUtil.Loc("ListenForJobs", DateTime.UtcNow));

            IJobDispatcher          jobDispatcher = null;
            CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.AgentShutdownToken);

            try
            {
                var notification = HostContext.GetService <IJobNotification>();
                if (!String.IsNullOrEmpty(settings.NotificationSocketAddress))
                {
                    notification.StartClient(settings.NotificationSocketAddress);
                }
                else
                {
                    notification.StartClient(settings.NotificationPipeName, HostContext.AgentShutdownToken);
                }
                // this is not a reliable way to disable auto update.
                // we need server side work to really enable the feature
                // https://github.com/Microsoft/vsts-agent/issues/446 (Feature: Allow agent / pool to opt out of automatic updates)
                bool        disableAutoUpdate    = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("agent.disableupdate"));
                bool        autoUpdateInProgress = false;
                Task <bool> selfUpdateTask       = null;
                jobDispatcher = HostContext.CreateService <IJobDispatcher>();

                while (!HostContext.AgentShutdownToken.IsCancellationRequested)
                {
                    TaskAgentMessage message             = null;
                    bool             skipMessageDeletion = false;
                    try
                    {
                        Task <TaskAgentMessage> getNextMessage = _listener.GetNextMessageAsync(messageQueueLoopTokenSource.Token);
                        if (autoUpdateInProgress)
                        {
                            Trace.Verbose("Auto update task running at backend, waiting for getNextMessage or selfUpdateTask to finish.");
                            Task completeTask = await Task.WhenAny(getNextMessage, selfUpdateTask);

                            if (completeTask == selfUpdateTask)
                            {
                                autoUpdateInProgress = false;
                                if (await selfUpdateTask)
                                {
                                    Trace.Info("Auto update task finished at backend, an agent update is ready to apply exit the current agent instance.");
                                    Trace.Info("Stop message queue looping.");
                                    messageQueueLoopTokenSource.Cancel();
                                    try
                                    {
                                        await getNextMessage;
                                    }
                                    catch (Exception ex)
                                    {
                                        Trace.Info($"Ignore any exception after cancel message loop. {ex}");
                                    }

                                    return(Constants.Agent.ReturnCode.AgentUpdating);
                                }
                                else
                                {
                                    Trace.Info("Auto update task finished at backend, there is no available agent update needs to apply, continue message queue looping.");
                                }
                            }
                        }

                        message = await getNextMessage; //get next message
                        if (string.Equals(message.MessageType, AgentRefreshMessage.MessageType, StringComparison.OrdinalIgnoreCase))
                        {
                            if (disableAutoUpdate)
                            {
                                Trace.Info("Refresh message received, skip autoupdate since environment variable agent.disableupdate is set.");
                            }
                            else
                            {
                                if (autoUpdateInProgress == false)
                                {
                                    autoUpdateInProgress = true;
                                    var agentUpdateMessage = JsonUtility.FromString <AgentRefreshMessage>(message.Body);
                                    var selfUpdater        = HostContext.GetService <ISelfUpdater>();
                                    selfUpdateTask = selfUpdater.SelfUpdate(agentUpdateMessage, jobDispatcher, HostContext.StartupType != StartupType.Service, HostContext.AgentShutdownToken);
                                    Trace.Info("Refresh message received, kick-off selfupdate background process.");
                                }
                                else
                                {
                                    Trace.Info("Refresh message received, skip autoupdate since a previous autoupdate is already running.");
                                }
                            }
                        }
                        else if (string.Equals(message.MessageType, JobRequestMessageTypes.AgentJobRequest, StringComparison.OrdinalIgnoreCase))
                        {
                            if (autoUpdateInProgress)
                            {
                                skipMessageDeletion = true;
                            }
                            else
                            {
                                var newJobMessage = JsonUtility.FromString <AgentJobRequestMessage>(message.Body);
                                jobDispatcher.Run(newJobMessage);
                            }
                        }
                        else if (string.Equals(message.MessageType, JobCancelMessage.MessageType, StringComparison.OrdinalIgnoreCase))
                        {
                            var  cancelJobMessage = JsonUtility.FromString <JobCancelMessage>(message.Body);
                            bool jobCancelled     = jobDispatcher.Cancel(cancelJobMessage);
                            skipMessageDeletion = autoUpdateInProgress && !jobCancelled;
                        }
                        else
                        {
                            Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}.");
                        }
                    }
                    finally
                    {
                        if (!skipMessageDeletion && message != null)
                        {
                            try
                            {
                                await _listener.DeleteMessageAsync(message);
                            }
                            catch (Exception ex)
                            {
                                Trace.Error($"Catch exception during delete message from message queue. message id: {message.MessageId}");
                                Trace.Error(ex);
                            }
                            finally
                            {
                                message = null;
                            }
                        }
                    }
                }
            }
            finally
            {
                if (jobDispatcher != null)
                {
                    await jobDispatcher.ShutdownAsync();
                }

                //TODO: make sure we don't mask more important exception
                await _listener.DeleteSessionAsync();

                messageQueueLoopTokenSource.Dispose();
            }

            return(Constants.Agent.ReturnCode.Success);
        }
Ejemplo n.º 19
0
        private async Task DownloadAsync(IExecutionContext executionContext, Pipelines.TaskStepDefinitionReference task)
        {
            Trace.Entering();
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(task, nameof(task));
            ArgUtil.NotNullOrEmpty(task.Version, nameof(task.Version));
            var taskServer = HostContext.GetService <ITaskServer>();

            // first check to see if we already have the task
            string destDirectory = GetDirectory(task);

            Trace.Info($"Ensuring task exists: ID '{task.Id}', version '{task.Version}', name '{task.Name}', directory '{destDirectory}'.");

            var           configurationStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings           = configurationStore.GetSettings();
            Boolean       signingEnabled     = (settings.SignatureVerification != null && settings.SignatureVerification.Mode != SignatureVerificationMode.None);
            Boolean       alwaysExtractTask  = signingEnabled || settings.AlwaysExtractTask;

            if (File.Exists(destDirectory + ".completed") && !alwaysExtractTask)
            {
                executionContext.Debug($"Task '{task.Name}' already downloaded at '{destDirectory}'.");
                return;
            }

            String taskZipPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.TaskZips), $"{task.Name}_{task.Id}_{task.Version}.zip");

            if (alwaysExtractTask && File.Exists(taskZipPath))
            {
                executionContext.Debug($"Task '{task.Name}' already downloaded at '{taskZipPath}'.");

                // Extract a new zip every time
                IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken);
                ExtractZip(taskZipPath, destDirectory);

                return;
            }

            // delete existing task folder.
            Trace.Verbose("Deleting task destination folder: {0}", destDirectory);
            IOUtil.DeleteDirectory(destDirectory, CancellationToken.None);

            // Inform the user that a download is taking place. The download could take a while if
            // the task zip is large. It would be nice to print the localized name, but it is not
            // available from the reference included in the job message.
            executionContext.Output(StringUtil.Loc("DownloadingTask0", task.Name, task.Version));
            string zipFile = string.Empty;
            var    version = new TaskVersion(task.Version);

            //download and extract task in a temp folder and rename it on success
            string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Tasks), "_temp_" + Guid.NewGuid());

            try
            {
                Directory.CreateDirectory(tempDirectory);
                int retryCount = 0;

                // Allow up to 20 * 60s for any task to be downloaded from service.
                // Base on Kusto, the longest we have on the service today is over 850 seconds.
                // Timeout limit can be overwrite by environment variable
                var timeoutSeconds = AgentKnobs.TaskDownloadTimeout.GetValue(UtilKnobValueContext.Instance()).AsInt();

                while (retryCount < 3)
                {
                    using (var taskDownloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
                        using (var taskDownloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(taskDownloadTimeout.Token, executionContext.CancellationToken))
                        {
                            try
                            {
                                zipFile = Path.Combine(tempDirectory, string.Format("{0}.zip", Guid.NewGuid()));

                                //open zip stream in async mode
                                using (FileStream fs = new FileStream(zipFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: _defaultFileStreamBufferSize, useAsync: true))
                                    using (Stream result = await taskServer.GetTaskContentZipAsync(task.Id, version, taskDownloadCancellation.Token))
                                    {
                                        await result.CopyToAsync(fs, _defaultCopyBufferSize, taskDownloadCancellation.Token);

                                        await fs.FlushAsync(taskDownloadCancellation.Token);

                                        // download succeed, break out the retry loop.
                                        break;
                                    }
                            }
                            catch (OperationCanceledException) when(executionContext.CancellationToken.IsCancellationRequested)
                            {
                                Trace.Info($"Task download has been cancelled.");
                                throw;
                            }
                            catch (Exception ex) when(retryCount < 2)
                            {
                                retryCount++;
                                Trace.Error($"Fail to download task '{task.Id} ({task.Name}/{task.Version})' -- Attempt: {retryCount}");
                                Trace.Error(ex);
                                if (taskDownloadTimeout.Token.IsCancellationRequested)
                                {
                                    // task download didn't finish within timeout
                                    executionContext.Warning(StringUtil.Loc("TaskDownloadTimeout", task.Name, timeoutSeconds));
                                }
                                else
                                {
                                    executionContext.Warning(StringUtil.Loc("TaskDownloadFailed", task.Name, ex.Message));
                                    if (ex.InnerException != null)
                                    {
                                        executionContext.Warning("Inner Exception: {ex.InnerException.Message}");
                                    }
                                }
                            }
                        }

                    if (String.IsNullOrEmpty(Environment.GetEnvironmentVariable("VSTS_TASK_DOWNLOAD_NO_BACKOFF")))
                    {
                        var backOff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30));
                        executionContext.Warning($"Back off {backOff.TotalSeconds} seconds before retry.");
                        await Task.Delay(backOff);
                    }
                }

                if (alwaysExtractTask)
                {
                    Directory.CreateDirectory(HostContext.GetDirectory(WellKnownDirectory.TaskZips));

                    // Copy downloaded zip to the cache on disk for future extraction.
                    executionContext.Debug($"Copying from {zipFile} to {taskZipPath}");
                    File.Copy(zipFile, taskZipPath);
                }

                // We need to extract the zip regardless of whether or not signing is enabled because the task.json metadata for the task is used in JobExtension.InitializeJob.
                // This is fine because we overwrite the contents at task run time.
                Directory.CreateDirectory(destDirectory);
                ExtractZip(zipFile, destDirectory);

                executionContext.Debug($"Task '{task.Name}' has been downloaded into '{(signingEnabled ? taskZipPath : destDirectory)}'.");
                Trace.Info("Finished getting task.");
            }
            finally
            {
                try
                {
                    //if the temp folder wasn't moved -> wipe it
                    if (Directory.Exists(tempDirectory))
                    {
                        Trace.Verbose("Deleting task temp folder: {0}", tempDirectory);
                        IOUtil.DeleteDirectory(tempDirectory, CancellationToken.None); // Don't cancel this cleanup and should be pretty fast.
                    }
                }
                catch (Exception ex)
                {
                    //it is not critical if we fail to delete the temp folder
                    Trace.Warning("Failed to delete temp folder '{0}'. Exception: {1}", tempDirectory, ex);
                    executionContext.Warning(StringUtil.Loc("FailedDeletingTempDirectory0Message1", tempDirectory, ex.Message));
                }
            }
        }
Ejemplo n.º 20
0
        // TODO: Updates legacy config.

        private TestHostContext Setup(
            [CallerMemberName] string name        = "",
            BuildCleanOption?cleanOption          = null,
            ExistingConfigKind existingConfigKind = ExistingConfigKind.None)
        {
            // Setup the host context.
            TestHostContext hc = new TestHostContext(this, name);

            // Create a random work path.
            var configStore = new Mock <IConfigurationStore>();

            _workFolder = Path.Combine(
                Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
                $"_work_{Path.GetRandomFileName()}");
            var settings = new AgentSettings()
            {
                WorkFolder = _workFolder
            };

            configStore.Setup(x => x.GetSettings()).Returns(settings);
            hc.SetSingleton <IConfigurationStore>(configStore.Object);

            // Setup the execution context.
            _ec = new Mock <IExecutionContext>();
            List <string> warnings;

            _variables = new Variables(hc, new Dictionary <string, string>(), new List <MaskHint>(), out warnings);
            _variables.Set(Constants.Variables.System.CollectionId, CollectionId);
            _variables.Set(Constants.Variables.System.DefinitionId, DefinitionId);
            _variables.Set(Constants.Variables.Build.Clean, $"{cleanOption}");
            _ec.Setup(x => x.Variables).Returns(_variables);

            // Store the expected tracking file path.
            _trackingFile = Path.Combine(
                _workFolder,
                Constants.Build.Path.SourceRootMappingDirectory,
                _ec.Object.Variables.System_CollectionId,
                _ec.Object.Variables.System_DefinitionId,
                Constants.Build.Path.TrackingConfigFile);

            // Setup the endpoint.
            _endpoint = new ServiceEndpoint()
            {
                Name = "Some endpoint name",
                Url  = new Uri("http://contoso.visualstudio.com"),
            };

            // Setup the source provider.
            _sourceProvider = new Mock <ISourceProvider>();
            _sourceProvider
            .Setup(x => x.GetBuildDirectoryHashKey(_ec.Object, _endpoint))
            .Returns(HashKey);
            hc.SetSingleton <ISourceProvider>(_sourceProvider.Object);

            // Store the existing config object.
            switch (existingConfigKind)
            {
            case ExistingConfigKind.Matching:
                _existingConfig = new TrackingConfig(_ec.Object, _endpoint, 1, HashKey);
                Assert.Equal("1", _existingConfig.BuildDirectory);
                break;

            case ExistingConfigKind.Nonmatching:
                _existingConfig = new TrackingConfig(_ec.Object, _endpoint, 2, NonmatchingHashKey);
                Assert.Equal("2", _existingConfig.BuildDirectory);
                break;

            case ExistingConfigKind.None:
                break;

            default:
                throw new NotSupportedException();
            }

            // Store the new config object.
            if (existingConfigKind == ExistingConfigKind.Matching)
            {
                _newConfig = _existingConfig;
            }
            else
            {
                _newConfig = new TrackingConfig(_ec.Object, _endpoint, 3, HashKey);
                Assert.Equal("3", _newConfig.BuildDirectory);
            }

            // Setup the tracking manager.
            _trackingManager = new Mock <ITrackingManager>();
            _trackingManager
            .Setup(x => x.LoadIfExists(_ec.Object, _trackingFile))
            .Returns(_existingConfig);
            if (existingConfigKind == ExistingConfigKind.None || existingConfigKind == ExistingConfigKind.Nonmatching)
            {
                _trackingManager
                .Setup(x => x.Create(_ec.Object, _endpoint, HashKey, _trackingFile))
                .Returns(_newConfig);
                if (existingConfigKind == ExistingConfigKind.Nonmatching)
                {
                    _trackingManager
                    .Setup(x => x.MarkForGarbageCollection(_ec.Object, _existingConfig));
                }
            }
            else if (existingConfigKind == ExistingConfigKind.Matching)
            {
                _trackingManager
                .Setup(x => x.UpdateJobRunProperties(_ec.Object, _existingConfig, _trackingFile));
            }
            else
            {
                throw new NotSupportedException();
            }

            hc.SetSingleton <ITrackingManager>(_trackingManager.Object);

            // Setup the build directory manager.
            _buildDirectoryManager = new BuildDirectoryManager();
            _buildDirectoryManager.Initialize(hc);
            return(hc);
        }
Ejemplo n.º 21
0
        public async Task UnconfigureAsync(CommandSettings command)
        {
            string currentAction = StringUtil.Loc("UninstallingService");

            try
            {
                //stop, uninstall service and remove service config file
                _term.WriteLine(currentAction);
                if (_store.IsServiceConfigured())
                {
                    var serviceControlManager = HostContext.GetService <IServiceControlManager>();
                    serviceControlManager.UnconfigureService();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }

                //delete agent from the server
                currentAction = StringUtil.Loc("UnregisteringAgent");
                _term.WriteLine(currentAction);
                bool isConfigured   = _store.IsConfigured();
                bool hasCredentials = _store.HasCredentials();
                if (isConfigured && hasCredentials)
                {
                    AgentSettings settings          = _store.GetSettings();
                    var           credentialManager = HostContext.GetService <ICredentialManager>();

                    // Get the credentials
                    var            credProvider = GetCredentialProvider(command, settings.ServerUrl);
                    VssCredentials creds        = credProvider.GetVssCredentials(HostContext);
                    Trace.Info("cred retrieved");

                    Uri           uri      = new Uri(settings.ServerUrl);
                    VssConnection conn     = ApiUtil.CreateConnection(uri, creds);
                    var           agentSvr = HostContext.GetService <IAgentServer>();
                    await agentSvr.ConnectAsync(conn);

                    Trace.Info("Connect complete.");

                    List <TaskAgent> agents = await agentSvr.GetAgentsAsync(settings.PoolId, settings.AgentName);

                    if (agents.Count == 0)
                    {
                        _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                    }
                    else
                    {
                        await agentSvr.DeleteAgentAsync(settings.PoolId, settings.AgentId);

                        _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                    }
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("MissingConfig"));
                }

                //delete credential config files
                currentAction = StringUtil.Loc("DeletingCredentials");
                _term.WriteLine(currentAction);
                if (hasCredentials)
                {
                    _store.DeleteCredential();
                    var keyManager = HostContext.GetService <IRSAKeyManager>();
                    keyManager.DeleteKey();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                }

                //delete settings config file
                currentAction = StringUtil.Loc("DeletingSettings");
                _term.WriteLine(currentAction);
                if (isConfigured)
                {
                    _store.DeleteSettings();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                }
            }
            catch (Exception)
            {
                _term.WriteLine(StringUtil.Loc("Failed") + currentAction);
                throw;
            }
        }
        public override bool ConfigureService(AgentSettings settings, CommandSettings command)
        {
            Trace.Entering();
            // TODO: add entering with info level. By default the error leve would be info. Config changes can get lost with this as entering is at Verbose level. For config all the logs should be logged.
            // TODO: Fix bug that exists in the legacy Windows agent where configuration using mirrored credentials causes an error, but the agent is still functional (after restarting). Mirrored credentials is a supported scenario and shouldn't manifest any errors.

            string logonPassword = string.Empty;

            NTAccount defaultServiceAccount = _windowsServiceHelper.GetDefaultServiceAccount();
            _logonAccount = command.GetWindowsLogonAccount(defaultValue: defaultServiceAccount.ToString());
            NativeWindowsServiceHelper.GetAccountSegments(_logonAccount, out _domainName, out _userName);
            if ((string.IsNullOrEmpty(_domainName) || _domainName.Equals(".", StringComparison.CurrentCultureIgnoreCase)) && !_logonAccount.Contains('@'))
            {
                _logonAccount = String.Format("{0}\\{1}", Environment.MachineName, _userName);
            }

            Trace.Info("LogonAccount after transforming: {0}, user: {1}, domain: {2}", _logonAccount, _userName, _domainName);
            if (!defaultServiceAccount.Equals(new NTAccount(_logonAccount)) &&
                !NativeWindowsServiceHelper.IsWellKnownIdentity(_logonAccount))
            {
                while (true)
                {
                    logonPassword = command.GetWindowsLogonPassword(_logonAccount);

                    // TODO: Fix this for unattended (should throw if not valid).
                    // TODO: If account is locked there is no point in retrying, translate error to useful message
                    if (_windowsServiceHelper.IsValidCredential(_domainName, _userName, logonPassword) || command.Unattended)
                    {
                        break;
                    }

                    Trace.Info("Invalid credential entered");
                    _term.WriteLine(StringUtil.Loc("InvalidWindowsCredential"));
                }
            }

            CalculateServiceName(settings, ServiceNamePattern, ServiceDisplayNamePattern);

            if (CheckServiceExists(ServiceName))
            {
                _term.WriteLine(StringUtil.Loc("ServiceAleadyExists"));

                StopService();
                UninstallService(ServiceName);
            }

            Trace.Info("Verifying if the account has LogonAsService permission");
            if (!_windowsServiceHelper.CheckUserHasLogonAsServicePrivilege(_domainName, _userName))
            {
                Trace.Info(StringUtil.Format("Account: {0} already has Logon As Service Privilege.", _logonAccount));
            }
            else
            {
                if (!_windowsServiceHelper.GrantUserLogonAsServicePrivilage(_domainName, _userName))
                {
                    throw new InvalidOperationException(StringUtil.Loc("CanNotGrantPermission", _logonAccount));
                }
            }

            _windowsServiceHelper.InstallService(ServiceName, ServiceDisplayName, _logonAccount, logonPassword);

            SaveServiceSettings();

            // TODO: If its service identity add it to appropriate PoolGroup
            // TODO: Add registry key after installation
            return true;
        }
        public override void GenerateScripts(AgentSettings settings)
        {

        }
Ejemplo n.º 24
0
 public abstract bool ConfigureService(AgentSettings settings, CommandSettings command);
Ejemplo n.º 25
0
        public async Task ConfigureAsync(CommandSettings command)
        {
            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

            // TEE EULA
            bool acceptTeeEula = false;
            switch (Constants.Agent.Platform)
            {
                case Constants.OSPlatform.OSX:
                case Constants.OSPlatform.Linux:
                    // Write the section header.
                    WriteSection(StringUtil.Loc("EulasSectionHeader"));

                    // Verify the EULA exists on disk in the expected location.
                    string eulaFile = Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.TeeDirectory, "license.html");
                    ArgUtil.File(eulaFile, nameof(eulaFile));

                    // Write elaborate verbiage about the TEE EULA.
                    _term.WriteLine(StringUtil.Loc("TeeEula", eulaFile));
                    _term.WriteLine();

                    // Prompt to acccept the TEE EULA.
                    acceptTeeEula = command.GetAcceptTeeEula();
                    break;
                case Constants.OSPlatform.Windows:
                    break;
                default:
                    throw new NotSupportedException();
            }

            // TODO: Check if its running with elevated permission and stop early if its not

            // Loop getting url and creds until you can connect
            string serverUrl = null;
            ICredentialProvider credProvider = null;
            WriteSection(StringUtil.Loc("ConnectSectionHeader"));
            while (true)
            {
                // Get the URL
                serverUrl = command.GetUrl();

                // Get the credentials
                credProvider = GetCredentialProvider(command, serverUrl);
                VssCredentials creds = credProvider.GetVssCredentials(HostContext);
                Trace.Info("cred retrieved");
                try
                {
                    // Validate can connect.
                    await TestConnectAsync(serverUrl, creds);
                    Trace.Info("Connect complete.");
                    break;
                }
                catch (Exception e) when (!command.Unattended)
                {
                    _term.WriteError(e);
                    _term.WriteError(StringUtil.Loc("FailedToConnect"));
                    // TODO: If the connection fails, shouldn't the URL/creds be cleared from the command line parser? Otherwise retry may be immediately attempted using the same values without prompting the user for new values. The same general problem applies to every retry loop during configure.
                }
            }

            // We want to use the native CSP of the platform for storage, so we use the RSACSP directly
            RSAParameters publicKey;
            var keyManager = HostContext.GetService<IRSAKeyManager>();
            using (var rsa = keyManager.CreateKey())
            {
                publicKey = rsa.ExportParameters(false);
            }

            // Loop getting agent name and pool
            string poolName = null;
            int poolId = 0;
            string agentName = null;
            WriteSection(StringUtil.Loc("RegisterAgentSectionHeader"));
            while (true)
            {
                poolName = command.GetPool();
                try
                {
                    poolId = await GetPoolId(poolName);
                }
                catch (Exception e) when (!command.Unattended)
                {
                    _term.WriteError(e);
                }

                if (poolId > 0)
                {
                    break;
                }

                _term.WriteError(StringUtil.Loc("FailedToFindPool"));
            }

            TaskAgent agent;
            while (true)
            {
                agentName = command.GetAgent();

                // Get the system capabilities.
                // TODO: Hook up to ctrl+c cancellation token.
                // TODO: LOC
                _term.WriteLine("Scanning for tool capabilities.");
                Dictionary<string, string> systemCapabilities = await HostContext.GetService<ICapabilitiesManager>().GetCapabilitiesAsync(
                    new AgentSettings { AgentName = agentName }, CancellationToken.None);

                // TODO: LOC
                _term.WriteLine("Connecting to the server.");
                agent = await GetAgent(agentName, poolId);
                if (agent != null)
                {
                    if (command.GetReplace())
                    {
                        agent.Authorization = new TaskAgentAuthorization
                        {
                            PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
                        };

                        // update - update instead of delete so we don't lose user capabilities etc...
                        agent.Version = Constants.Agent.Version;

                        foreach (KeyValuePair<string, string> capability in systemCapabilities)
                        {
                            agent.SystemCapabilities[capability.Key] = capability.Value ?? string.Empty;
                        }

                        try
                        {
                            agent = await _agentServer.UpdateAgentAsync(poolId, agent);
                            _term.WriteLine(StringUtil.Loc("AgentReplaced"));
                            break;
                        }
                        catch (Exception e) when (!command.Unattended)
                        {
                            _term.WriteError(e);
                            _term.WriteError(StringUtil.Loc("FailedToReplaceAgent"));
                        }
                    }
                    else
                    {
                        // TODO: ?
                    }
                }
                else
                {
                    agent = new TaskAgent(agentName)
                    {
                        Authorization = new TaskAgentAuthorization
                        {
                            PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
                        },
                        MaxParallelism = 1,
                        Version = Constants.Agent.Version
                    };

                    foreach (KeyValuePair<string, string> capability in systemCapabilities)
                    {
                        agent.SystemCapabilities[capability.Key] = capability.Value ?? string.Empty;
                    }

                    try
                    {
                        agent = await _agentServer.AddAgentAsync(poolId, agent);
                        _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully"));
                        break;
                    }
                    catch (Exception e) when (!command.Unattended)
                    {
                        _term.WriteError(e);
                        _term.WriteError(StringUtil.Loc("AddAgentFailed"));
                    }
                }
            }

            // See if the server supports our OAuth key exchange for credentials
            if (agent.Authorization != null &&
                agent.Authorization.ClientId != Guid.Empty &&
                agent.Authorization.AuthorizationUrl != null)
            {
                var credentialData = new CredentialData
                {
                    Scheme = Constants.Configuration.OAuth,
                    Data =
                    {
                        { "clientId", agent.Authorization.ClientId.ToString("D") },
                        { "authorizationUrl", agent.Authorization.AuthorizationUrl.AbsoluteUri },
                    },
                };

                // Save the negotiated OAuth credential data
                _store.SaveCredential(credentialData);
            }
            else
            {
                // Save the provided admin credential data for compat with existing agent
                _store.SaveCredential(credProvider.CredentialData);
            }

            // We will Combine() what's stored with root.  Defaults to string a relative path
            string workFolder = command.GetWork();
            string notificationPipeName = command.GetNotificationPipeName();

            // Get Agent settings
            var settings = new AgentSettings
            {
                AcceptTeeEula = acceptTeeEula,
                AgentId = agent.Id,
                AgentName = agentName,
                NotificationPipeName = notificationPipeName,
                PoolId = poolId,
                PoolName = poolName,
                ServerUrl = serverUrl,
                WorkFolder = workFolder,
            };

            _store.SaveSettings(settings);
            _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow));

            bool runAsService = false;

            if (Constants.Agent.Platform == Constants.OSPlatform.Windows)
            {
                runAsService = command.GetRunAsService();
            }

            var serviceControlManager = HostContext.GetService<IServiceControlManager>();
            serviceControlManager.GenerateScripts(settings);

            bool successfullyConfigured = false;
            if (runAsService)
            {
                Trace.Info("Configuring to run the agent as service");
                successfullyConfigured = serviceControlManager.ConfigureService(settings, command);
            }

            if (runAsService && successfullyConfigured)
            {
                Trace.Info("Configuration was successful, trying to start the service");
                serviceControlManager.StartService();
            }
        }
Ejemplo n.º 26
0
 public Task <TaskAgent> AddAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command)
 {
     return(_agentServer.AddAgentAsync(agentSettings.PoolId, agent));
 }
Ejemplo n.º 27
0
 public Task DeleteAgentAsync(AgentSettings agentSettings)
 {
     return(_agentServer.DeleteAgentAsync(agentSettings.PoolId, agentSettings.AgentId));
 }
Ejemplo n.º 28
0
        public async Task <TaskResult> RunAsync(AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
        {
            // Validate parameters.
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Environment, nameof(message.Environment));
            ArgUtil.NotNull(message.Environment.Variables, nameof(message.Environment.Variables));
            ArgUtil.NotNull(message.Tasks, nameof(message.Tasks));
            Trace.Info("Job ID {0}", message.JobId);

            if (message.Environment.Variables.ContainsKey(Constants.Variables.System.EnableAccessToken) &&
                StringUtil.ConvertToBoolean(message.Environment.Variables[Constants.Variables.System.EnableAccessToken]))
            {
                // TODO: get access token use Util Method
                message.Environment.Variables[Constants.Variables.System.AccessToken] = message.Environment.SystemConnection.Authorization.Parameters["AccessToken"];
            }

            // Make sure SystemConnection Url and Endpoint Url match Config Url base
            ReplaceConfigUriBaseInJobRequestMessage(message);

            // Setup the job server and job server queue.
            var jobServer           = HostContext.GetService <IJobServer>();
            var jobServerCredential = ApiUtil.GetVssCredential(message.Environment.SystemConnection);
            Uri jobServerUrl        = message.Environment.SystemConnection.Url;

            Trace.Info($"Creating job server with URL: {jobServerUrl}");
            // jobServerQueue is the throttling reporter.
            var jobServerQueue = HostContext.GetService <IJobServerQueue>();
            var jobConnection  = ApiUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(jobServerQueue) });
            await jobServer.ConnectAsync(jobConnection);

            jobServerQueue.Start(message);

            IExecutionContext jobContext = null;

            try
            {
                // Create the job execution context.
                jobContext = HostContext.CreateService <IExecutionContext>();
                jobContext.InitializeJob(message, jobRequestCancellationToken);
                Trace.Info("Starting the job execution context.");
                jobContext.Start();

                // Set agent version into ExecutionContext's variables dictionary.
                jobContext.Variables.Set(Constants.Variables.Agent.Version, Constants.Agent.Version);

                // Print agent version into log for better diagnostic experience
                jobContext.Output(StringUtil.Loc("AgentVersion", Constants.Agent.Version));

                // Print proxy setting information for better diagnostic experience
                var proxyConfig = HostContext.GetService <IProxyConfiguration>();
                if (!string.IsNullOrEmpty(proxyConfig.ProxyUrl))
                {
                    jobContext.Output(StringUtil.Loc("AgentRunningBehindProxy", proxyConfig.ProxyUrl));
                }

                // Validate directory permissions.
                string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
                Trace.Info($"Validating directory permissions for: '{workDirectory}'");
                try
                {
                    Directory.CreateDirectory(workDirectory);
                    IOUtil.ValidateExecutePermission(workDirectory);
                }
                catch (Exception ex)
                {
                    Trace.Error(ex);
                    jobContext.Error(ex);
                    return(jobContext.Complete(TaskResult.Failed));
                }

                // Set agent variables.
                AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings();
                jobContext.Variables.Set(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture));
                jobContext.Variables.Set(Constants.Variables.Agent.HomeDirectory, IOUtil.GetRootPath());
                jobContext.Variables.Set(Constants.Variables.Agent.JobName, message.JobName);
                jobContext.Variables.Set(Constants.Variables.Agent.MachineName, Environment.MachineName);
                jobContext.Variables.Set(Constants.Variables.Agent.Name, settings.AgentName);
                jobContext.Variables.Set(Constants.Variables.Agent.RootDirectory, IOUtil.GetWorkPath(HostContext));
#if OS_WINDOWS
                jobContext.Variables.Set(Constants.Variables.Agent.ServerOMDirectory, Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.ServerOMDirectory));
#endif
                jobContext.Variables.Set(Constants.Variables.Agent.WorkFolder, IOUtil.GetWorkPath(HostContext));
                jobContext.Variables.Set(Constants.Variables.System.WorkFolder, IOUtil.GetWorkPath(HostContext));

                // prefer task definitions url, then TFS collection url, then TFS account url
                var taskServer    = HostContext.GetService <ITaskServer>();
                Uri taskServerUri = null;
                if (!string.IsNullOrEmpty(jobContext.Variables.System_TaskDefinitionsUri))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TaskDefinitionsUri);
                }
                else if (!string.IsNullOrEmpty(jobContext.Variables.System_TFCollectionUrl))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TFCollectionUrl);
                }

                var taskServerCredential = ApiUtil.GetVssCredential(message.Environment.SystemConnection);
                if (taskServerUri != null)
                {
                    Trace.Info($"Creating task server with {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                if (taskServerUri == null || !await taskServer.TaskDefinitionEndpointExist(jobRequestCancellationToken))
                {
                    Trace.Info($"Can't determine task download url from JobMessage or the endpoint doesn't exist.");
                    var configStore = HostContext.GetService <IConfigurationStore>();
                    taskServerUri = new Uri(configStore.GetSettings().ServerUrl);
                    Trace.Info($"Recreate task server with configuration server url: {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                // Expand the endpoint data values.
                foreach (ServiceEndpoint endpoint in jobContext.Endpoints)
                {
                    jobContext.Variables.ExpandValues(target: endpoint.Data);
                    VarUtil.ExpandEnvironmentVariables(HostContext, target: endpoint.Data);
                }

                // Get the job extensions.
                Trace.Info("Getting job extensions.");
                string          hostType         = jobContext.Variables.System_HostType;
                var             extensionManager = HostContext.GetService <IExtensionManager>();
                IJobExtension[] extensions       =
                    (extensionManager.GetExtensions <IJobExtension>() ?? new List <IJobExtension>())
                    .Where(x => string.Equals(x.HostType, hostType, StringComparison.OrdinalIgnoreCase))
                    .ToArray();

                // Add the prepare steps.
                Trace.Info("Adding job prepare extensions.");
                List <IStep> steps = new List <IStep>();
                foreach (IJobExtension extension in extensions)
                {
                    if (extension.PrepareStep != null)
                    {
                        Trace.Verbose($"Adding {extension.GetType().Name}.{nameof(extension.PrepareStep)}.");
                        extension.PrepareStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), extension.PrepareStep.DisplayName);
                        steps.Add(extension.PrepareStep);
                    }
                }

                // Add the task steps.
                Trace.Info("Adding tasks.");
                foreach (TaskInstance taskInstance in message.Tasks)
                {
                    Trace.Verbose($"Adding {taskInstance.DisplayName}.");
                    var taskRunner = HostContext.CreateService <ITaskRunner>();
                    taskRunner.ExecutionContext = jobContext.CreateChild(taskInstance.InstanceId, taskInstance.DisplayName);
                    taskRunner.TaskInstance     = taskInstance;
                    steps.Add(taskRunner);
                }

                // Add the finally steps.
                Trace.Info("Adding job finally extensions.");
                foreach (IJobExtension extension in extensions)
                {
                    if (extension.FinallyStep != null)
                    {
                        Trace.Verbose($"Adding {extension.GetType().Name}.{nameof(extension.FinallyStep)}.");
                        extension.FinallyStep.ExecutionContext = jobContext.CreateChild(Guid.NewGuid(), extension.FinallyStep.DisplayName);
                        steps.Add(extension.FinallyStep);
                    }
                }

                // Download tasks if not already in the cache
                Trace.Info("Downloading task definitions.");
                var taskManager = HostContext.GetService <ITaskManager>();
                try
                {
                    await taskManager.DownloadAsync(jobContext, message.Tasks);
                }
                catch (OperationCanceledException ex)
                {
                    // set the job to canceled
                    Trace.Error($"Caught exception: {ex}");
                    jobContext.Error(ex);
                    return(jobContext.Complete(TaskResult.Canceled));
                }
                catch (Exception ex)
                {
                    // Log the error and fail the job.
                    Trace.Error($"Caught exception from {nameof(TaskManager)}: {ex}");
                    jobContext.Error(ex);
                    return(jobContext.Complete(TaskResult.Failed));
                }

                // Run the steps.
                var stepsRunner = HostContext.GetService <IStepsRunner>();
                try
                {
                    await stepsRunner.RunAsync(jobContext, steps);
                }
                catch (OperationCanceledException ex)
                {
                    // set the job to canceled
                    Trace.Error($"Caught exception: {ex}");
                    jobContext.Error(ex);
                    return(jobContext.Complete(TaskResult.Canceled));
                }
                catch (Exception ex)
                {
                    // Log the error and fail the job.
                    Trace.Error($"Caught exception from {nameof(StepsRunner)}: {ex}");
                    jobContext.Error(ex);
                    return(jobContext.Complete(TaskResult.Failed));
                }

                Trace.Info($"Job result: {jobContext.Result}");

                // Complete the job.
                Trace.Info("Completing the job execution context.");
                return(jobContext.Complete());
            }
            finally
            {
                // Drain the job server queue.
                if (jobServerQueue != null)
                {
                    try
                    {
                        Trace.Info("Shutting down the job server queue.");
                        await jobServerQueue.ShutdownAsync();
                    }
                    catch (Exception ex)
                    {
                        Trace.Error($"Caught exception from {nameof(JobServerQueue)}.{nameof(jobServerQueue.ShutdownAsync)}: {ex}");
                    }
                }
            }
        }
Ejemplo n.º 29
0
        private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
        {
            var hc = new TestHostContext(this, testName);

            _jobEc                = new Agent.Worker.ExecutionContext();
            _config               = new Mock <IConfigurationStore>();
            _extensions           = new Mock <IExtensionManager>();
            _jobExtension         = new Mock <IJobExtension>();
            _jobServer            = new Mock <IJobServer>();
            _jobServerQueue       = new Mock <IJobServerQueue>();
            _proxyConfig          = new Mock <IVstsAgentWebProxy>();
            _cert                 = new Mock <IAgentCertificateManager>();
            _taskServer           = new Mock <ITaskServer>();
            _stepRunner           = new Mock <IStepsRunner>();
            _logger               = new Mock <IPagingLogger>();
            _temp                 = new Mock <ITempDirectoryManager>();
            _diagnosticLogManager = new Mock <IDiagnosticLogManager>();

            if (_tokenSource != null)
            {
                _tokenSource.Dispose();
                _tokenSource = null;
            }

            _tokenSource = new CancellationTokenSource();
            var expressionManager = new ExpressionManager();

            expressionManager.Initialize(hc);
            hc.SetSingleton <IExpressionManager>(expressionManager);

            _jobRunner = new JobRunner();
            _jobRunner.Initialize(hc);

            TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
            TimelineReference timeline          = new Timeline(Guid.NewGuid());
            JobEnvironment    environment       = new JobEnvironment();

            environment.Variables[Constants.Variables.System.Culture] = "en-US";
            environment.SystemConnection = new ServiceEndpoint()
            {
                Url           = new Uri("https://test.visualstudio.com"),
                Authorization = new EndpointAuthorization()
                {
                    Scheme = "Test",
                }
            };
            environment.SystemConnection.Authorization.Parameters["AccessToken"] = "token";

            List <TaskInstance> tasks = new List <TaskInstance>();
            Guid JobId = Guid.NewGuid();

            _message = new AgentJobRequestMessage(plan, timeline, JobId, testName, testName, environment, tasks);

            _extensions.Setup(x => x.GetExtensions <IJobExtension>()).
            Returns(new[] { _jobExtension.Object }.ToList());

            _initResult.PreJobSteps.Clear();
            _initResult.JobSteps.Clear();
            _initResult.PostJobStep.Clear();

            _jobExtension.Setup(x => x.InitializeJob(It.IsAny <IExecutionContext>(), It.IsAny <AgentJobRequestMessage>())).
            Returns(Task.FromResult(_initResult));
            _jobExtension.Setup(x => x.HostType)
            .Returns <string>(null);

            _proxyConfig.Setup(x => x.ProxyAddress)
            .Returns(string.Empty);

            var settings = new AgentSettings
            {
                AgentId    = 1,
                AgentName  = "agent1",
                ServerUrl  = "https://test.visualstudio.com",
                WorkFolder = "_work",
            };

            _config.Setup(x => x.GetSettings())
            .Returns(settings);

            _logger.Setup(x => x.Setup(It.IsAny <Guid>(), It.IsAny <Guid>()));

            hc.SetSingleton(_config.Object);
            hc.SetSingleton(_jobServer.Object);
            hc.SetSingleton(_jobServerQueue.Object);
            hc.SetSingleton(_proxyConfig.Object);
            hc.SetSingleton(_cert.Object);
            hc.SetSingleton(_taskServer.Object);
            hc.SetSingleton(_stepRunner.Object);
            hc.SetSingleton(_extensions.Object);
            hc.SetSingleton(_temp.Object);
            hc.SetSingleton(_diagnosticLogManager.Object);
            hc.EnqueueInstance <IExecutionContext>(_jobEc);
            hc.EnqueueInstance <IPagingLogger>(_logger.Object);
            return(hc);
        }
Ejemplo n.º 30
0
 public void UpdateAgentSetting(AgentSettings settings)
 {
     settings.MachineGroupId = _machineGroupId;
     settings.ProjectName    = _projectName;
 }
Ejemplo n.º 31
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            try
            {
                var agentWebProxy    = HostContext.GetService <IVstsAgentWebProxy>();
                var agentCertManager = HostContext.GetService <IAgentCertificateManager>();
                ApiUtil.InitializeVssClientSettings(agentWebProxy, agentCertManager);

                _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;

                // Local run
                if (command.LocalRun)
                {
                    var localManager = HostContext.GetService <ILocalRunner>();
                    return(await localManager.LocalRunAsync(command, HostContext.AgentShutdownToken));
                }

                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();
            }
        }
Ejemplo n.º 32
0
        public async Task UploadDiagnosticLogsAsync(IExecutionContext executionContext,
                                                    Pipelines.AgentJobRequestMessage message,
                                                    DateTime jobStartTimeUtc)
        {
            executionContext.Debug("Starting diagnostic file upload.");

            // Setup folders
            // \_layout\_work\_temp\[jobname-support]
            executionContext.Debug("Setting up diagnostic log folders.");
            string tempDirectory = HostContext.GetDirectory(WellKnownDirectory.Temp);

            ArgUtil.Directory(tempDirectory, nameof(tempDirectory));

            string supportRootFolder = Path.Combine(tempDirectory, message.JobName + "-support");

            Directory.CreateDirectory(supportRootFolder);

            // \_layout\_work\_temp\[jobname-support]\files
            executionContext.Debug("Creating diagnostic log files folder.");
            string supportFilesFolder = Path.Combine(supportRootFolder, "files");

            Directory.CreateDirectory(supportFilesFolder);

            // Create the environment file
            // \_layout\_work\_temp\[jobname-support]\files\environment.txt
            var           configurationStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings           = configurationStore.GetSettings();
            int           agentId            = settings.AgentId;
            string        agentName          = settings.AgentName;
            int           poolId             = settings.PoolId;

            executionContext.Debug("Creating diagnostic log environment file.");
            string environmentFile = Path.Combine(supportFilesFolder, "environment.txt");
            string content         = await GetEnvironmentContent(agentId, agentName, message.Steps);

            File.WriteAllText(environmentFile, content);

            // Create the capabilities file
            var capabilitiesManager = HostContext.GetService <ICapabilitiesManager>();
            Dictionary <string, string> capabilities = await capabilitiesManager.GetCapabilitiesAsync(configurationStore.GetSettings(), default(CancellationToken));

            executionContext.Debug("Creating capabilities file.");
            string capabilitiesFile    = Path.Combine(supportFilesFolder, "capabilities.txt");
            string capabilitiesContent = GetCapabilitiesContent(capabilities);

            File.WriteAllText(capabilitiesFile, capabilitiesContent);

            // Copy worker diag log files
            List <string> workerDiagLogFiles = GetWorkerDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {workerDiagLogFiles.Count()} worker diag logs.");

            foreach (string workerLogFile in workerDiagLogFiles)
            {
                ArgUtil.File(workerLogFile, nameof(workerLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(workerLogFile));
                File.Copy(workerLogFile, destination);
            }

            // Copy agent diag log files
            List <string> agentDiagLogFiles = GetAgentDiagLogFiles(HostContext.GetDirectory(WellKnownDirectory.Diag), jobStartTimeUtc);

            executionContext.Debug($"Copying {agentDiagLogFiles.Count()} agent diag logs.");

            foreach (string agentLogFile in agentDiagLogFiles)
            {
                ArgUtil.File(agentLogFile, nameof(agentLogFile));

                string destination = Path.Combine(supportFilesFolder, Path.GetFileName(agentLogFile));
                File.Copy(agentLogFile, destination);
            }

            executionContext.Debug("Zipping diagnostic files.");

            string buildNumber = executionContext.Variables.Build_Number ?? "UnknownBuildNumber";
            string buildName   = $"Build {buildNumber}";
            string phaseName   = executionContext.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";

            // zip the files
            string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
            string diagnosticsZipFilePath = Path.Combine(supportRootFolder, diagnosticsZipFileName);

            ZipFile.CreateFromDirectory(supportFilesFolder, diagnosticsZipFilePath);

            // upload the json metadata file
            executionContext.Debug("Uploading diagnostic metadata file.");
            string metadataFileName = $"diagnostics-{buildName}-{phaseName}.json";
            string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
            string phaseResult      = GetTaskResultAsString(executionContext.Result);

            IOUtil.SaveObject(new DiagnosticLogMetadata(agentName, agentId, poolId, phaseName, diagnosticsZipFileName, phaseResult), metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: metadataFileName, filePath: metadataFilePath);

            executionContext.QueueAttachFile(type: CoreAttachmentType.DiagnosticLog, name: diagnosticsZipFileName, filePath: diagnosticsZipFilePath);

            executionContext.Debug("Diagnostic file upload complete.");
        }
Ejemplo n.º 33
0
        public async Task ConfigureAsync(CommandSettings command)
        {
            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

            // TEE EULA
            bool acceptTeeEula = false;

            switch (Constants.Agent.Platform)
            {
            case Constants.OSPlatform.OSX:
            case Constants.OSPlatform.Linux:
                // Write the section header.
                WriteSection(StringUtil.Loc("EulasSectionHeader"));

                // Verify the EULA exists on disk in the expected location.
                string eulaFile = Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.TeeDirectory, "license.html");
                ArgUtil.File(eulaFile, nameof(eulaFile));

                // Write elaborate verbiage about the TEE EULA.
                _term.WriteLine(StringUtil.Loc("TeeEula", eulaFile));
                _term.WriteLine();

                // Prompt to acccept the TEE EULA.
                acceptTeeEula = command.GetAcceptTeeEula();
                break;

            case Constants.OSPlatform.Windows:
                break;

            default:
                throw new NotSupportedException();
            }

            // TODO: Check if its running with elevated permission and stop early if its not

            // Loop getting url and creds until you can connect
            string serverUrl = null;
            ICredentialProvider credProvider = null;

            WriteSection(StringUtil.Loc("ConnectSectionHeader"));
            while (true)
            {
                // Get the URL
                serverUrl = command.GetUrl();

                // Get the credentials
                credProvider = GetCredentialProvider(command, serverUrl);
                VssCredentials creds = credProvider.GetVssCredentials(HostContext);
                Trace.Info("cred retrieved");
                try
                {
                    // Validate can connect.
                    await TestConnectAsync(serverUrl, creds);

                    Trace.Info("Connect complete.");
                    break;
                }
                catch (Exception e) when(!command.Unattended)
                {
                    _term.WriteError(e);
                    _term.WriteError(StringUtil.Loc("FailedToConnect"));
                    // TODO: If the connection fails, shouldn't the URL/creds be cleared from the command line parser? Otherwise retry may be immediately attempted using the same values without prompting the user for new values. The same general problem applies to every retry loop during configure.
                }
            }

            // We want to use the native CSP of the platform for storage, so we use the RSACSP directly
            RSAParameters publicKey;
            var           keyManager = HostContext.GetService <IRSAKeyManager>();

            using (var rsa = keyManager.CreateKey())
            {
                publicKey = rsa.ExportParameters(false);
            }

            // Loop getting agent name and pool
            string poolName  = null;
            int    poolId    = 0;
            string agentName = null;

            WriteSection(StringUtil.Loc("RegisterAgentSectionHeader"));
            while (true)
            {
                poolName = command.GetPool();
                try
                {
                    poolId = await GetPoolId(poolName);
                }
                catch (Exception e) when(!command.Unattended)
                {
                    _term.WriteError(e);
                }

                if (poolId > 0)
                {
                    break;
                }

                _term.WriteError(StringUtil.Loc("FailedToFindPool"));
            }

            TaskAgent agent;

            while (true)
            {
                agentName = command.GetAgentName();

                // Get the system capabilities.
                // TODO: Hook up to ctrl+c cancellation token.
                _term.WriteLine(StringUtil.Loc("ScanToolCapabilities"));
                Dictionary <string, string> systemCapabilities = await HostContext.GetService <ICapabilitiesManager>().GetCapabilitiesAsync(
                    new AgentSettings {
                    AgentName = agentName
                }, CancellationToken.None);

                _term.WriteLine(StringUtil.Loc("ConnectToServer"));
                agent = await GetAgent(agentName, poolId);

                if (agent != null)
                {
                    if (command.GetReplace())
                    {
                        agent.Authorization = new TaskAgentAuthorization
                        {
                            PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
                        };

                        // update - update instead of delete so we don't lose user capabilities etc...
                        agent.Version = Constants.Agent.Version;

                        foreach (KeyValuePair <string, string> capability in systemCapabilities)
                        {
                            agent.SystemCapabilities[capability.Key] = capability.Value ?? string.Empty;
                        }

                        try
                        {
                            agent = await _agentServer.UpdateAgentAsync(poolId, agent);

                            _term.WriteLine(StringUtil.Loc("AgentReplaced"));
                            break;
                        }
                        catch (Exception e) when(!command.Unattended)
                        {
                            _term.WriteError(e);
                            _term.WriteError(StringUtil.Loc("FailedToReplaceAgent"));
                        }
                    }
                    else
                    {
                        // TODO: ?
                    }
                }
                else
                {
                    agent = new TaskAgent(agentName)
                    {
                        Authorization = new TaskAgentAuthorization
                        {
                            PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
                        },
                        MaxParallelism = 1,
                        Version        = Constants.Agent.Version
                    };

                    foreach (KeyValuePair <string, string> capability in systemCapabilities)
                    {
                        agent.SystemCapabilities[capability.Key] = capability.Value ?? string.Empty;
                    }

                    try
                    {
                        agent = await _agentServer.AddAgentAsync(poolId, agent);

                        _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully"));
                        break;
                    }
                    catch (Exception e) when(!command.Unattended)
                    {
                        _term.WriteError(e);
                        _term.WriteError(StringUtil.Loc("AddAgentFailed"));
                    }
                }
            }

            // See if the server supports our OAuth key exchange for credentials
            if (agent.Authorization != null &&
                agent.Authorization.ClientId != Guid.Empty &&
                agent.Authorization.AuthorizationUrl != null)
            {
                var credentialData = new CredentialData
                {
                    Scheme = Constants.Configuration.OAuth,
                    Data   =
                    {
                        { "clientId",         agent.Authorization.ClientId.ToString("D")       },
                        { "authorizationUrl", agent.Authorization.AuthorizationUrl.AbsoluteUri },
                    },
                };

                // Save the negotiated OAuth credential data
                _store.SaveCredential(credentialData);
            }
            else
            {
                // Save the provided admin credential data for compat with existing agent
                _store.SaveCredential(credProvider.CredentialData);
            }

            // Testing agent connection, detect any protential connection issue, like local clock skew that cause OAuth token expired.
            _term.WriteLine(StringUtil.Loc("TestAgentConnection"));
            var            credMgr    = HostContext.GetService <ICredentialManager>();
            VssCredentials credential = credMgr.LoadCredentials();
            VssConnection  conn       = ApiUtil.CreateConnection(new Uri(serverUrl), credential);
            var            agentSvr   = HostContext.GetService <IAgentServer>();

            try
            {
                await agentSvr.ConnectAsync(conn);
            }
            catch (VssOAuthTokenRequestException ex) when(ex.Message.Contains("Current server time is"))
            {
                // there are two exception messages server send that indicate clock skew.
                // 1. The bearer token expired on {jwt.ValidTo}. Current server time is {DateTime.UtcNow}.
                // 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}.
                Trace.Error("Catch exception during test agent connection.");
                Trace.Error(ex);
                throw new Exception(StringUtil.Loc("LocalClockSkewed"));
            }

            // We will Combine() what's stored with root.  Defaults to string a relative path
            string workFolder           = command.GetWork();
            string notificationPipeName = command.GetNotificationPipeName();

            // Get Agent settings
            var settings = new AgentSettings
            {
                AcceptTeeEula        = acceptTeeEula,
                AgentId              = agent.Id,
                AgentName            = agentName,
                NotificationPipeName = notificationPipeName,
                PoolId     = poolId,
                PoolName   = poolName,
                ServerUrl  = serverUrl,
                WorkFolder = workFolder,
            };

            _store.SaveSettings(settings);
            _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow));

            bool runAsService = false;

            if (Constants.Agent.Platform == Constants.OSPlatform.Windows)
            {
                runAsService = command.GetRunAsService();
                if (runAsService)
                {
                    if (!new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator))
                    {
                        Trace.Error("Needs Administrator privileges for configure agent as windows service.");
                        throw new SecurityException(StringUtil.Loc("NeedAdminForConfigAgentWinService"));
                    }
                }
            }

            var serviceControlManager = HostContext.GetService <IServiceControlManager>();

            serviceControlManager.GenerateScripts(settings);

            bool successfullyConfigured = false;

            if (runAsService)
            {
                Trace.Info("Configuring to run the agent as service");
                successfullyConfigured = serviceControlManager.ConfigureService(settings, command);
            }

            if (runAsService && successfullyConfigured)
            {
                Trace.Info("Configuration was successful, trying to start the service");
                serviceControlManager.StartService();
            }
        }
Ejemplo n.º 34
0
        public TrackingConfig Create(
            IExecutionContext executionContext,
            ServiceEndpoint endpoint,
            string hashKey,
            string file,
            bool overrideBuildDirectory)
        {
            Trace.Entering();

            // Get or create the top-level tracking config.
            TopLevelTrackingConfig topLevelConfig;
            string topLevelFile = Path.Combine(
                IOUtil.GetWorkPath(HostContext),
                Constants.Build.Path.SourceRootMappingDirectory,
                Constants.Build.Path.TopLevelTrackingConfigFile);

            Trace.Verbose($"Loading top-level tracking config if exists: {topLevelFile}");
            if (!File.Exists(topLevelFile))
            {
                topLevelConfig = new TopLevelTrackingConfig();
            }
            else
            {
                topLevelConfig = JsonConvert.DeserializeObject <TopLevelTrackingConfig>(File.ReadAllText(topLevelFile));
                if (topLevelConfig == null)
                {
                    executionContext.Warning($"Rebuild corruptted top-level tracking configure file {topLevelFile}.");
                    // save the corruptted file in case we need to investigate more.
                    File.Copy(topLevelFile, $"{topLevelFile}.corruptted", true);

                    topLevelConfig = new TopLevelTrackingConfig();
                    DirectoryInfo workDir = new DirectoryInfo(HostContext.GetDirectory(WellKnownDirectory.Work));

                    foreach (var dir in workDir.EnumerateDirectories())
                    {
                        // we scan the entire _work directory and find the directory with the highest integer number.
                        if (int.TryParse(dir.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out int lastBuildNumber) &&
                            lastBuildNumber > topLevelConfig.LastBuildDirectoryNumber)
                        {
                            topLevelConfig.LastBuildDirectoryNumber = lastBuildNumber;
                        }
                    }
                }
            }

            // Determine the build directory.
            if (overrideBuildDirectory)
            {
                // This should only occur during hosted builds. This was added due to TFVC.
                // TFVC does not allow a local path for a single machine to be mapped in multiple
                // workspaces. The machine name for a hosted images is not unique.
                //
                // So if a customer is running two hosted builds at the same time, they could run
                // into the local mapping conflict.
                //
                // The workaround is to force the build directory to be different across all concurrent
                // hosted builds (for TFVC). The agent ID will be unique across all concurrent hosted
                // builds so that can safely be used as the build directory.
                ArgUtil.Equal(default(int), topLevelConfig.LastBuildDirectoryNumber, nameof(topLevelConfig.LastBuildDirectoryNumber));
                var           configurationStore = HostContext.GetService <IConfigurationStore>();
                AgentSettings settings           = configurationStore.GetSettings();
                topLevelConfig.LastBuildDirectoryNumber = settings.AgentId;
            }
            else
            {
                topLevelConfig.LastBuildDirectoryNumber++;
            }

            // Update the top-level tracking config.
            topLevelConfig.LastBuildDirectoryCreatedOn = DateTimeOffset.Now;
            WriteToFile(topLevelFile, topLevelConfig);

            // Create the new tracking config.
            TrackingConfig config = new TrackingConfig(
                executionContext,
                endpoint,
                topLevelConfig.LastBuildDirectoryNumber,
                hashKey);

            WriteToFile(file, config);
            return(config);
        }
        public void ConfigureService(AgentSettings settings, CommandSettings command)
        {
            Trace.Entering();

            if (!_windowsServiceHelper.IsRunningInElevatedMode())
            {
                Trace.Error("Needs Administrator privileges for configure agent as windows service.");
                throw new SecurityException(StringUtil.Loc("NeedAdminForConfigAgentWinService"));
            }

            // TODO: Fix bug that exists in the legacy Windows agent where configuration using mirrored credentials causes an error, but the agent is still functional (after restarting). Mirrored credentials is a supported scenario and shouldn't manifest any errors.

            // We use NetworkService as default account for build and release agent
            // We use Local System as default account for deployment agent, deployment pool agent
            bool      isDeploymentGroupScenario = command.DeploymentGroup || command.DeploymentPool;
            NTAccount defaultServiceAccount     = isDeploymentGroupScenario ? _windowsServiceHelper.GetDefaultAdminServiceAccount() : _windowsServiceHelper.GetDefaultServiceAccount();
            string    logonAccount = command.GetWindowsLogonAccount(defaultValue: defaultServiceAccount.ToString(), descriptionMsg: StringUtil.Loc("WindowsLogonAccountNameDescription"));

            string domainName;
            string userName;

            GetAccountSegments(logonAccount, out domainName, out userName);

            if ((string.IsNullOrEmpty(domainName) || domainName.Equals(".", StringComparison.CurrentCultureIgnoreCase)) && !logonAccount.Contains('@'))
            {
                logonAccount = String.Format("{0}\\{1}", Environment.MachineName, userName);
                domainName   = Environment.MachineName;
            }

            Trace.Info("LogonAccount after transforming: {0}, user: {1}, domain: {2}", logonAccount, userName, domainName);

            string logonPassword = string.Empty;

            if (!defaultServiceAccount.Equals(new NTAccount(logonAccount)) && !NativeWindowsServiceHelper.IsWellKnownIdentity(logonAccount))
            {
                while (true)
                {
                    logonPassword = command.GetWindowsLogonPassword(logonAccount);
                    if (_windowsServiceHelper.IsValidCredential(domainName, userName, logonPassword))
                    {
                        Trace.Info("Credential validation succeed");
                        break;
                    }
                    else
                    {
                        if (!command.Unattended)
                        {
                            Trace.Info("Invalid credential entered");
                            _term.WriteLine(StringUtil.Loc("InvalidWindowsCredential"));
                        }
                        else
                        {
                            throw new SecurityException(StringUtil.Loc("InvalidWindowsCredential"));
                        }
                    }
                }
            }

            string serviceName;
            string serviceDisplayName;

            CalculateServiceName(settings, ServiceNamePattern, ServiceDisplayNamePattern, out serviceName, out serviceDisplayName);
            if (_windowsServiceHelper.IsServiceExists(serviceName))
            {
                _term.WriteLine(StringUtil.Loc("ServiceAlreadyExists", serviceName));
                _windowsServiceHelper.UninstallService(serviceName);
            }

            Trace.Info("Verifying if the account has LogonAsService permission");
            if (_windowsServiceHelper.IsUserHasLogonAsServicePrivilege(domainName, userName))
            {
                Trace.Info($"Account: {logonAccount} already has Logon As Service Privilege.");
            }
            else
            {
                if (!_windowsServiceHelper.GrantUserLogonAsServicePrivilage(domainName, userName))
                {
                    throw new InvalidOperationException(StringUtil.Loc("CanNotGrantPermission", logonAccount));
                }
            }

            Trace.Info("Create local group and grant folder permission to service logon account.");
            GrantDirectoryPermissionForAccount(logonAccount);

            // install service.
            _windowsServiceHelper.InstallService(serviceName, serviceDisplayName, logonAccount, logonPassword);

            // create .service file with service name.
            SaveServiceSettings(serviceName);

            // Add registry key after installation
            _windowsServiceHelper.CreateVstsAgentRegistryKey();

            Trace.Info("Configuration was successful, trying to start the service");
            _windowsServiceHelper.StartService(serviceName);
        }
 private bool ShouldOverrideBuildDirectory(IList <RepositoryResource> repositories, AgentSettings settings)
 {
     if (repositories?.Count == 1 && repositories[0].Type == RepositoryTypes.Tfvc)
     {
         return(settings.IsHosted);
     }
     else
     {
         return(false);
     }
 }
Ejemplo n.º 37
0
        public async Task <TaskResult> RunAsync(Pipelines.AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
        {
            // Validate parameters.
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Resources, nameof(message.Resources));
            ArgUtil.NotNull(message.Variables, nameof(message.Variables));
            ArgUtil.NotNull(message.Steps, nameof(message.Steps));
            Trace.Info("Job ID {0}", message.JobId);

            DateTime jobStartTimeUtc = DateTime.UtcNow;

            // Agent.RunMode
            RunMode runMode;

            if (message.Variables.ContainsKey(Constants.Variables.Agent.RunMode) &&
                Enum.TryParse(message.Variables[Constants.Variables.Agent.RunMode].Value, ignoreCase: true, result: out runMode) &&
                runMode == RunMode.Local)
            {
                HostContext.RunMode = runMode;
            }

            ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));

            // System.AccessToken
            if (message.Variables.ContainsKey(Constants.Variables.System.EnableAccessToken) &&
                StringUtil.ConvertToBoolean(message.Variables[Constants.Variables.System.EnableAccessToken].Value))
            {
                message.Variables[Constants.Variables.System.AccessToken] = new VariableValue(systemConnection.Authorization.Parameters["AccessToken"], false);
            }

            // back compat TfsServerUrl
            message.Variables[Constants.Variables.System.TFServerUrl] = systemConnection.Url.AbsoluteUri;

            // Make sure SystemConnection Url and Endpoint Url match Config Url base for OnPremises server
            if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType))
            {
                if (!UrlUtil.IsHosted(systemConnection.Url.AbsoluteUri)) // TODO: remove this after TFS/RM move to M133
                {
                    ReplaceConfigUriBaseInJobRequestMessage(message);
                }
            }
            else if (string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase))
            {
                ReplaceConfigUriBaseInJobRequestMessage(message);
            }

            // Setup the job server and job server queue.
            var            jobServer           = HostContext.GetService <IJobServer>();
            VssCredentials jobServerCredential = ApiUtil.GetVssCredential(systemConnection);
            Uri            jobServerUrl        = systemConnection.Url;

            Trace.Info($"Creating job server with URL: {jobServerUrl}");
            // jobServerQueue is the throttling reporter.
            _jobServerQueue = HostContext.GetService <IJobServerQueue>();
            VssConnection jobConnection = ApiUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) });
            await jobServer.ConnectAsync(jobConnection);

            _jobServerQueue.Start(message);

            IExecutionContext             jobContext = null;
            CancellationTokenRegistration?agentShutdownRegistration = null;

            try
            {
                // Create the job execution context.
                jobContext = HostContext.CreateService <IExecutionContext>();
                jobContext.InitializeJob(message, jobRequestCancellationToken);
                Trace.Info("Starting the job execution context.");
                jobContext.Start();
                jobContext.Section(StringUtil.Loc("StepStarting", message.JobDisplayName));

                agentShutdownRegistration = HostContext.AgentShutdownToken.Register(() =>
                {
                    // log an issue, then agent get shutdown by Ctrl-C or Ctrl-Break.
                    // the server will use Ctrl-Break to tells the agent that operating system is shutting down.
                    string errorMessage;
                    switch (HostContext.AgentShutdownReason)
                    {
                    case ShutdownReason.UserCancelled:
                        errorMessage = StringUtil.Loc("UserShutdownAgent");
                        break;

                    case ShutdownReason.OperatingSystemShutdown:
                        errorMessage = StringUtil.Loc("OperatingSystemShutdown", Environment.MachineName);
                        break;

                    default:
                        throw new ArgumentException(HostContext.AgentShutdownReason.ToString(), nameof(HostContext.AgentShutdownReason));
                    }
                    jobContext.AddIssue(new Issue()
                    {
                        Type = IssueType.Error, Message = errorMessage
                    });
                });

                // Set agent version variable.
                jobContext.Variables.Set(Constants.Variables.Agent.Version, Constants.Agent.Version);
                jobContext.Output(StringUtil.Loc("AgentVersion", Constants.Agent.Version));

                // Print proxy setting information for better diagnostic experience
                var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();
                if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
                {
                    jobContext.Output(StringUtil.Loc("AgentRunningBehindProxy", agentWebProxy.ProxyAddress));
                }

                // Validate directory permissions.
                string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
                Trace.Info($"Validating directory permissions for: '{workDirectory}'");
                try
                {
                    Directory.CreateDirectory(workDirectory);
                    IOUtil.ValidateExecutePermission(workDirectory);
                }
                catch (Exception ex)
                {
                    Trace.Error(ex);
                    jobContext.Error(ex);
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                // Set agent variables.
                AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings();
                jobContext.Variables.Set(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture));
                jobContext.Variables.Set(Constants.Variables.Agent.HomeDirectory, HostContext.GetDirectory(WellKnownDirectory.Root));
                jobContext.Variables.Set(Constants.Variables.Agent.JobName, message.JobDisplayName);
                jobContext.Variables.Set(Constants.Variables.Agent.MachineName, Environment.MachineName);
                jobContext.Variables.Set(Constants.Variables.Agent.Name, settings.AgentName);
                jobContext.Variables.Set(Constants.Variables.Agent.OS, VarUtil.OS);
                jobContext.Variables.Set(Constants.Variables.Agent.RootDirectory, IOUtil.GetWorkPath(HostContext));
#if OS_WINDOWS
                jobContext.Variables.Set(Constants.Variables.Agent.ServerOMDirectory, Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.ServerOMDirectory));
#endif
                jobContext.Variables.Set(Constants.Variables.Agent.WorkFolder, IOUtil.GetWorkPath(HostContext));
                jobContext.Variables.Set(Constants.Variables.System.WorkFolder, IOUtil.GetWorkPath(HostContext));

                string toolsDirectory = Environment.GetEnvironmentVariable("AGENT_TOOLSDIRECTORY") ?? Environment.GetEnvironmentVariable(Constants.Variables.Agent.ToolsDirectory);
                if (string.IsNullOrEmpty(toolsDirectory))
                {
                    toolsDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.ToolDirectory);
                    Directory.CreateDirectory(toolsDirectory);
                }
                else
                {
                    Trace.Info($"Set tool cache directory base on environment: '{toolsDirectory}'");
                    Directory.CreateDirectory(toolsDirectory);
                }
                jobContext.Variables.Set(Constants.Variables.Agent.ToolsDirectory, toolsDirectory);

                // Setup TEMP directories
                _tempDirectoryManager = HostContext.GetService <ITempDirectoryManager>();
                _tempDirectoryManager.InitializeTempDirectory(jobContext);

                // todo: task server can throw. try/catch and fail job gracefully.
                // prefer task definitions url, then TFS collection url, then TFS account url
                var taskServer    = HostContext.GetService <ITaskServer>();
                Uri taskServerUri = null;
                if (!string.IsNullOrEmpty(jobContext.Variables.System_TaskDefinitionsUri))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TaskDefinitionsUri);
                }
                else if (!string.IsNullOrEmpty(jobContext.Variables.System_TFCollectionUrl))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TFCollectionUrl);
                }

                var taskServerCredential = ApiUtil.GetVssCredential(systemConnection);
                if (taskServerUri != null)
                {
                    Trace.Info($"Creating task server with {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                if (taskServerUri == null || !await taskServer.TaskDefinitionEndpointExist())
                {
                    Trace.Info($"Can't determine task download url from JobMessage or the endpoint doesn't exist.");
                    var configStore = HostContext.GetService <IConfigurationStore>();
                    taskServerUri = new Uri(configStore.GetSettings().ServerUrl);
                    Trace.Info($"Recreate task server with configuration server url: {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                // Expand the endpoint data values.
                foreach (ServiceEndpoint endpoint in jobContext.Endpoints)
                {
                    jobContext.Variables.ExpandValues(target: endpoint.Data);
                    VarUtil.ExpandEnvironmentVariables(HostContext, target: endpoint.Data);
                }

                // Get the job extension.
                Trace.Info("Getting job extension.");
                var hostType         = jobContext.Variables.System_HostType;
                var extensionManager = HostContext.GetService <IExtensionManager>();
                // We should always have one job extension
                IJobExtension jobExtension =
                    (extensionManager.GetExtensions <IJobExtension>() ?? new List <IJobExtension>())
                    .Where(x => x.HostType.HasFlag(hostType))
                    .FirstOrDefault();
                ArgUtil.NotNull(jobExtension, nameof(jobExtension));

                List <IStep> jobSteps = new List <IStep>();
                try
                {
                    Trace.Info("Initialize job. Getting all job steps.");
                    var initializeResult = await jobExtension.InitializeJob(jobContext, message);

                    jobSteps.AddRange(initializeResult.PreJobSteps);
                    jobSteps.AddRange(initializeResult.JobSteps);
                    jobSteps.AddRange(initializeResult.PostJobStep);
                }
                catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested)
                {
                    // set the job to canceled
                    // don't log error issue to job ExecutionContext, since server owns the job level issue
                    Trace.Error($"Job is canceled during initialize.");
                    Trace.Error($"Caught exception: {ex}");
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Canceled));
                }
                catch (Exception ex)
                {
                    // set the job to failed.
                    // don't log error issue to job ExecutionContext, since server owns the job level issue
                    Trace.Error($"Job initialize failed.");
                    Trace.Error($"Caught exception from {nameof(jobExtension.InitializeJob)}: {ex}");
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                // trace out all steps
                Trace.Info($"Total job steps: {jobSteps.Count}.");
                Trace.Verbose($"Job steps: '{string.Join(", ", jobSteps.Select(x => x.DisplayName))}'");

                bool             processCleanup    = jobContext.Variables.GetBoolean("process.clean") ?? true;
                HashSet <string> existingProcesses = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                string           processLookupId   = null;
                if (processCleanup)
                {
                    processLookupId = $"vsts_{Guid.NewGuid()}";

                    // Set the VSTS_PROCESS_LOOKUP_ID env variable.
                    jobContext.SetVariable(Constants.ProcessLookupId, processLookupId, false, false);

                    // Take a snapshot of current running processes
                    Dictionary <int, Process> processes = SnapshotProcesses();
                    foreach (var proc in processes)
                    {
                        // Pid_ProcessName
                        existingProcesses.Add($"{proc.Key}_{proc.Value.ProcessName}");
                    }
                }

                // Run all job steps
                Trace.Info("Run all job steps.");
                var stepsRunner = HostContext.GetService <IStepsRunner>();
                try
                {
                    await stepsRunner.RunAsync(jobContext, jobSteps);
                }
                catch (Exception ex)
                {
                    // StepRunner should never throw exception out.
                    // End up here mean there is a bug in StepRunner
                    // Log the error and fail the job.
                    Trace.Error($"Caught exception from job steps {nameof(StepsRunner)}: {ex}");
                    jobContext.Error(ex);
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }
                finally
                {
                    if (processCleanup)
                    {
                        // Only check environment variable for any process that doesn't run before we invoke our process.
                        Dictionary <int, Process> currentProcesses = SnapshotProcesses();
                        foreach (var proc in currentProcesses)
                        {
                            if (existingProcesses.Contains($"{proc.Key}_{proc.Value.ProcessName}"))
                            {
                                Trace.Verbose($"Skip existing process. PID: {proc.Key} ({proc.Value.ProcessName})");
                            }
                            else
                            {
                                Trace.Info($"Inspecting process environment variables. PID: {proc.Key} ({proc.Value.ProcessName})");

                                Dictionary <string, string> env = new Dictionary <string, string>();
                                try
                                {
                                    env = proc.Value.GetEnvironmentVariables();
                                    foreach (var e in env)
                                    {
                                        Trace.Verbose($"PID:{proc.Key} ({e.Key}={e.Value})");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Trace.Verbose("Ignore any exception during read process environment variables.");
                                    Trace.Verbose(ex.ToString());
                                }

                                if (env.TryGetValue(Constants.ProcessLookupId, out string lookupId) &&
                                    lookupId.Equals(processLookupId, StringComparison.OrdinalIgnoreCase))
                                {
                                    Trace.Info($"Terminate orphan process: pid ({proc.Key}) ({proc.Value.ProcessName})");
                                    try
                                    {
                                        proc.Value.Kill();
                                    }
                                    catch (Exception ex)
                                    {
                                        Trace.Error("Catch exception during orphan process cleanup.");
                                        Trace.Error(ex);
                                    }
                                }
                            }
                        }
                    }
                }

                Trace.Info($"Job result after all job steps finish: {jobContext.Result ?? TaskResult.Succeeded}");

                if (jobContext.Variables.GetBoolean(Constants.Variables.Agent.Diagnostic) ?? false)
                {
                    Trace.Info("Support log upload starting.");

                    IDiagnosticLogManager diagnosticLogManager = HostContext.GetService <IDiagnosticLogManager>();

                    try
                    {
                        await diagnosticLogManager.UploadDiagnosticLogsAsync(executionContext : jobContext, message : message, jobStartTimeUtc : jobStartTimeUtc);

                        Trace.Info("Support log upload complete.");
                    }
                    catch (Exception ex)
                    {
                        // Log the error but make sure we continue gracefully.
                        Trace.Info("Error uploading support logs.");
                        Trace.Error(ex);
                    }
                }

                Trace.Info("Completing the job execution context.");
                return(await CompleteJobAsync(jobServer, jobContext, message));
            }
            finally
            {
                if (agentShutdownRegistration != null)
                {
                    agentShutdownRegistration.Value.Dispose();
                    agentShutdownRegistration = null;
                }

                await ShutdownQueue(throwOnFailure : false);
            }
        }
Ejemplo n.º 38
0
        public async Task <int> ExecuteCommand(CommandSettings command)
        {
            ArgUtil.NotNull(command, nameof(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();
            }
        }
Ejemplo n.º 39
0
 public void UpdateAgentSetting(AgentSettings settings)
 {
     // No implementation required
 }
Ejemplo n.º 40
0
        public async Task ConfigureAsync(CommandSettings command)
        {
            ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

            // Populate proxy setting from commandline args
            var    vstsProxy        = HostContext.GetService <IVstsAgentWebProxy>();
            bool   saveProxySetting = false;
            string proxyUrl         = command.GetProxyUrl();

            if (!string.IsNullOrEmpty(proxyUrl))
            {
                if (!Uri.IsWellFormedUriString(proxyUrl, UriKind.Absolute))
                {
                    throw new ArgumentOutOfRangeException(nameof(proxyUrl));
                }

                Trace.Info("Reset proxy base on commandline args.");
                string proxyUserName = command.GetProxyUserName();
                string proxyPassword = command.GetProxyPassword();
                (vstsProxy as VstsAgentWebProxy).SetupProxy(proxyUrl, proxyUserName, proxyPassword);
                saveProxySetting = true;
            }

            // Populate cert setting from commandline args
            var    agentCertManager   = HostContext.GetService <IAgentCertificateManager>();
            bool   saveCertSetting    = false;
            bool   skipCertValidation = command.GetSkipCertificateValidation();
            string caCert             = command.GetCACertificate();
            string clientCert         = command.GetClientCertificate();
            string clientCertKey      = command.GetClientCertificatePrivateKey();
            string clientCertArchive  = command.GetClientCertificateArchrive();
            string clientCertPassword = command.GetClientCertificatePassword();

            // We require all Certificate files are under agent root.
            // So we can set ACL correctly when configure as service
            if (!string.IsNullOrEmpty(caCert))
            {
                caCert = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), caCert);
                ArgUtil.File(caCert, nameof(caCert));
            }

            if (!string.IsNullOrEmpty(clientCert) &&
                !string.IsNullOrEmpty(clientCertKey) &&
                !string.IsNullOrEmpty(clientCertArchive))
            {
                // Ensure all client cert pieces are there.
                clientCert        = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCert);
                clientCertKey     = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCertKey);
                clientCertArchive = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), clientCertArchive);

                ArgUtil.File(clientCert, nameof(clientCert));
                ArgUtil.File(clientCertKey, nameof(clientCertKey));
                ArgUtil.File(clientCertArchive, nameof(clientCertArchive));
            }
            else if (!string.IsNullOrEmpty(clientCert) ||
                     !string.IsNullOrEmpty(clientCertKey) ||
                     !string.IsNullOrEmpty(clientCertArchive))
            {
                // Print out which args are missing.
                ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCert, Constants.Agent.CommandLine.Args.SslClientCert);
                ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCertKey, Constants.Agent.CommandLine.Args.SslClientCertKey);
                ArgUtil.NotNullOrEmpty(Constants.Agent.CommandLine.Args.SslClientCertArchive, Constants.Agent.CommandLine.Args.SslClientCertArchive);
            }

            if (skipCertValidation || !string.IsNullOrEmpty(caCert) || !string.IsNullOrEmpty(clientCert))
            {
                Trace.Info("Reset agent cert setting base on commandline args.");
                (agentCertManager as AgentCertificateManager).SetupCertificate(skipCertValidation, caCert, clientCert, clientCertKey, clientCertArchive, clientCertPassword);
                saveCertSetting = true;
            }

            AgentSettings agentSettings = new AgentSettings();

            // TEE EULA
            agentSettings.AcceptTeeEula = false;
            switch (Constants.Agent.Platform)
            {
            case Constants.OSPlatform.OSX:
            case Constants.OSPlatform.Linux:
                // Write the section header.
                WriteSection(StringUtil.Loc("EulasSectionHeader"));

                // Verify the EULA exists on disk in the expected location.
                string eulaFile = Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.TeeDirectory, "license.html");
                ArgUtil.File(eulaFile, nameof(eulaFile));

                // Write elaborate verbiage about the TEE EULA.
                _term.WriteLine(StringUtil.Loc("TeeEula", eulaFile));
                _term.WriteLine();

                // Prompt to acccept the TEE EULA.
                agentSettings.AcceptTeeEula = command.GetAcceptTeeEula();
                break;

            case Constants.OSPlatform.Windows:
                // Warn and continue if .NET 4.6 is not installed.
                var netFrameworkUtil = HostContext.GetService <INetFrameworkUtil>();
                if (!netFrameworkUtil.Test(new Version(4, 6)))
                {
                    WriteSection(StringUtil.Loc("PrerequisitesSectionHeader"));     // Section header.
                    _term.WriteLine(StringUtil.Loc("MinimumNetFrameworkTfvc"));     // Warning.
                }

                break;

            default:
                throw new NotSupportedException();
            }

            // Create the configuration provider as per agent type.
            string agentType;

            if (command.DeploymentGroup)
            {
                agentType = Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration;
            }
            else if (command.DeploymentPool)
            {
                agentType = Constants.Agent.AgentConfigurationProvider.SharedDeploymentAgentConfiguration;
            }
            else
            {
                agentType = Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration;
            }

            var extensionManager = HostContext.GetService <IExtensionManager>();
            IConfigurationProvider agentProvider =
                (extensionManager.GetExtensions <IConfigurationProvider>())
                .FirstOrDefault(x => x.ConfigurationProviderType == agentType);

            ArgUtil.NotNull(agentProvider, agentType);

            bool isHostedServer = false;
            // Loop getting url and creds until you can connect
            ICredentialProvider credProvider = null;
            VssCredentials      creds        = null;

            WriteSection(StringUtil.Loc("ConnectSectionHeader"));
            while (true)
            {
                // Get the URL
                agentProvider.GetServerUrl(agentSettings, command);

                // Get the credentials
                credProvider = GetCredentialProvider(command, agentSettings.ServerUrl);
                creds        = credProvider.GetVssCredentials(HostContext);
                Trace.Info("cred retrieved");
                try
                {
                    // Determine the service deployment type based on connection data. (Hosted/OnPremises)
                    isHostedServer = await IsHostedServer(agentSettings.ServerUrl, creds);

                    // Get the collection name for deployment group
                    agentProvider.GetCollectionName(agentSettings, command, isHostedServer);

                    // Validate can connect.
                    await agentProvider.TestConnectionAsync(agentSettings, creds, isHostedServer);

                    Trace.Info("Test Connection complete.");
                    break;
                }
                catch (Exception e) when(!command.Unattended)
                {
                    _term.WriteError(e);
                    _term.WriteError(StringUtil.Loc("FailedToConnect"));
                }
            }

            _agentServer = HostContext.GetService <IAgentServer>();
            // We want to use the native CSP of the platform for storage, so we use the RSACSP directly
            RSAParameters publicKey;
            var           keyManager = HostContext.GetService <IRSAKeyManager>();

            using (var rsa = keyManager.CreateKey())
            {
                publicKey = rsa.ExportParameters(false);
            }

            // Loop getting agent name and pool name
            WriteSection(StringUtil.Loc("RegisterAgentSectionHeader"));

            while (true)
            {
                try
                {
                    await agentProvider.GetPoolId(agentSettings, command);

                    break;
                }
                catch (Exception e) when(!command.Unattended)
                {
                    _term.WriteError(e);
                    _term.WriteError(agentProvider.GetFailedToFindPoolErrorString());
                }
            }

            TaskAgent agent;

            while (true)
            {
                agentSettings.AgentName = command.GetAgentName();

                // Get the system capabilities.
                // TODO: Hook up to ctrl+c cancellation token.
                _term.WriteLine(StringUtil.Loc("ScanToolCapabilities"));
                Dictionary <string, string> systemCapabilities = await HostContext.GetService <ICapabilitiesManager>().GetCapabilitiesAsync(agentSettings, CancellationToken.None);

                _term.WriteLine(StringUtil.Loc("ConnectToServer"));
                agent = await agentProvider.GetAgentAsync(agentSettings);

                if (agent != null)
                {
                    if (command.GetReplace())
                    {
                        // Update existing agent with new PublicKey, agent version and SystemCapabilities.
                        agent = UpdateExistingAgent(agent, publicKey, systemCapabilities);

                        try
                        {
                            agent = await agentProvider.UpdateAgentAsync(agentSettings, agent, command);

                            _term.WriteLine(StringUtil.Loc("AgentReplaced"));
                            break;
                        }
                        catch (Exception e) when(!command.Unattended)
                        {
                            _term.WriteError(e);
                            _term.WriteError(StringUtil.Loc("FailedToReplaceAgent"));
                        }
                    }
                    else if (command.Unattended)
                    {
                        // if not replace and it is unattended config.
                        agentProvider.ThrowTaskAgentExistException(agentSettings);
                    }
                }
                else
                {
                    // Create a new agent.
                    agent = CreateNewAgent(agentSettings.AgentName, publicKey, systemCapabilities);

                    try
                    {
                        agent = await agentProvider.AddAgentAsync(agentSettings, agent, command);

                        _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully"));
                        break;
                    }
                    catch (Exception e) when(!command.Unattended)
                    {
                        _term.WriteError(e);
                        _term.WriteError(StringUtil.Loc("AddAgentFailed"));
                    }
                }
            }
            // Add Agent Id to settings
            agentSettings.AgentId = agent.Id;

            // respect the serverUrl resolve by server.
            // in case of agent configured using collection url instead of account url.
            string agentServerUrl;

            if (agent.Properties.TryGetValidatedValue <string>("ServerUrl", out agentServerUrl) &&
                !string.IsNullOrEmpty(agentServerUrl))
            {
                Trace.Info($"Agent server url resolve by server: '{agentServerUrl}'.");

                // we need make sure the Schema/Host/Port component of the url remain the same.
                UriBuilder inputServerUrl          = new UriBuilder(agentSettings.ServerUrl);
                UriBuilder serverReturnedServerUrl = new UriBuilder(agentServerUrl);
                if (Uri.Compare(inputServerUrl.Uri, serverReturnedServerUrl.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    inputServerUrl.Path = serverReturnedServerUrl.Path;
                    Trace.Info($"Replace server returned url's scheme://host:port component with user input server url's scheme://host:port: '{inputServerUrl.Uri.AbsoluteUri}'.");
                    agentSettings.ServerUrl = inputServerUrl.Uri.AbsoluteUri;
                }
                else
                {
                    agentSettings.ServerUrl = agentServerUrl;
                }
            }

            // See if the server supports our OAuth key exchange for credentials
            if (agent.Authorization != null &&
                agent.Authorization.ClientId != Guid.Empty &&
                agent.Authorization.AuthorizationUrl != null)
            {
                // We use authorizationUrl as the oauth endpoint url by default.
                // For TFS, we need make sure the Schema/Host/Port component of the oauth endpoint url also match configuration url. (Incase of customer's agent configure URL and TFS server public URL are different)
                // Which means, we will keep use the original authorizationUrl in the VssOAuthJwtBearerClientCredential (authorizationUrl is the audience),
                // But might have different Url in VssOAuthCredential (connection url)
                // We can't do this for VSTS, since its SPS/TFS urls are different.
                UriBuilder configServerUrl         = new UriBuilder(agentSettings.ServerUrl);
                UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl);
                if (!isHostedServer && Uri.Compare(configServerUrl.Uri, oauthEndpointUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    oauthEndpointUrlBuilder.Scheme = configServerUrl.Scheme;
                    oauthEndpointUrlBuilder.Host   = configServerUrl.Host;
                    oauthEndpointUrlBuilder.Port   = configServerUrl.Port;
                    Trace.Info($"Set oauth endpoint url's scheme://host:port component to match agent configure url's scheme://host:port: '{oauthEndpointUrlBuilder.Uri.AbsoluteUri}'.");
                }

                var credentialData = new CredentialData
                {
                    Scheme = Constants.Configuration.OAuth,
                    Data   =
                    {
                        { "clientId",         agent.Authorization.ClientId.ToString("D")       },
                        { "authorizationUrl", agent.Authorization.AuthorizationUrl.AbsoluteUri },
                        { "oauthEndpointUrl", oauthEndpointUrlBuilder.Uri.AbsoluteUri          },
                    },
                };

                // Save the negotiated OAuth credential data
                _store.SaveCredential(credentialData);
            }
            else
            {
                switch (Constants.Agent.Platform)
                {
                case Constants.OSPlatform.OSX:
                case Constants.OSPlatform.Linux:
                    // Save the provided admin cred for compat with previous agent.
                    _store.SaveCredential(credProvider.CredentialData);
                    break;

                case Constants.OSPlatform.Windows:
                    // Not supported against TFS 2015.
                    _term.WriteError(StringUtil.Loc("Tfs2015NotSupported"));
                    return;

                default:
                    throw new NotSupportedException();
                }
            }

            // Testing agent connection, detect any protential connection issue, like local clock skew that cause OAuth token expired.
            _term.WriteLine(StringUtil.Loc("TestAgentConnection"));
            var            credMgr    = HostContext.GetService <ICredentialManager>();
            VssCredentials credential = credMgr.LoadCredentials();
            VssConnection  conn       = ApiUtil.CreateConnection(new Uri(agentSettings.ServerUrl), credential);
            var            agentSvr   = HostContext.GetService <IAgentServer>();

            try
            {
                await agentSvr.ConnectAsync(conn);
            }
            catch (VssOAuthTokenRequestException ex) when(ex.Message.Contains("Current server time is"))
            {
                // there are two exception messages server send that indicate clock skew.
                // 1. The bearer token expired on {jwt.ValidTo}. Current server time is {DateTime.UtcNow}.
                // 2. The bearer token is not valid until {jwt.ValidFrom}. Current server time is {DateTime.UtcNow}.
                Trace.Error("Catch exception during test agent connection.");
                Trace.Error(ex);
                throw new Exception(StringUtil.Loc("LocalClockSkewed"));
            }

            // We will Combine() what's stored with root.  Defaults to string a relative path
            agentSettings.WorkFolder = command.GetWork();

            // notificationPipeName for Hosted agent provisioner.
            agentSettings.NotificationPipeName = command.GetNotificationPipeName();

            agentSettings.NotificationSocketAddress = command.GetNotificationSocketAddress();

            _store.SaveSettings(agentSettings);

            if (saveProxySetting)
            {
                Trace.Info("Save proxy setting to disk.");
                (vstsProxy as VstsAgentWebProxy).SaveProxySetting();
            }

            if (saveCertSetting)
            {
                Trace.Info("Save agent cert setting to disk.");
                (agentCertManager as AgentCertificateManager).SaveCertificateSetting();
            }

            _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow));

            bool saveRuntimeOptions = false;
            var  runtimeOptions     = new AgentRuntimeOptions();

#if OS_WINDOWS
            if (command.GitUseSChannel)
            {
                saveRuntimeOptions = true;
                runtimeOptions.GitUseSecureChannel = true;
            }
#endif
            if (saveRuntimeOptions)
            {
                Trace.Info("Save agent runtime options to disk.");
                _store.SaveAgentRuntimeOptions(runtimeOptions);
            }

#if OS_WINDOWS
            // config windows service
            bool runAsService = command.GetRunAsService();
            if (runAsService)
            {
                Trace.Info("Configuring to run the agent as service");
                var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>();
                serviceControlManager.ConfigureService(agentSettings, command);
            }
            // config auto logon
            else if (command.GetRunAsAutoLogon())
            {
                Trace.Info("Agent is going to run as process setting up the 'AutoLogon' capability for the agent.");
                var autoLogonConfigManager = HostContext.GetService <IAutoLogonManager>();
                await autoLogonConfigManager.ConfigureAsync(command);

                //Important: The machine may restart if the autologon user is not same as the current user
                //if you are adding code after this, keep that in mind
            }
#elif OS_LINUX || OS_OSX
            // generate service config script for OSX and Linux, GenerateScripts() will no-opt on windows.
            var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>();
            serviceControlManager.GenerateScripts(agentSettings);
#endif
        }
Ejemplo n.º 41
0
        public async Task UnconfigureAsync(CommandSettings command)
        {
            ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
            string currentAction = string.Empty;

            try
            {
                //stop, uninstall service and remove service config file
                if (_store.IsServiceConfigured())
                {
                    currentAction = StringUtil.Loc("UninstallingService");
                    _term.WriteLine(currentAction);
#if OS_WINDOWS
                    var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>();
                    serviceControlManager.UnconfigureService();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
#elif OS_LINUX
                    // unconfig system D service first
                    throw new Exception(StringUtil.Loc("UnconfigureServiceDService"));
#elif OS_OSX
                    // unconfig osx service first
                    throw new Exception(StringUtil.Loc("UnconfigureOSXService"));
#endif
                }
                else
                {
#if OS_WINDOWS
                    //running as process, unconfigure autologon if it was configured
                    if (_store.IsAutoLogonConfigured())
                    {
                        currentAction = StringUtil.Loc("UnconfigAutologon");
                        _term.WriteLine(currentAction);
                        var autoLogonConfigManager = HostContext.GetService <IAutoLogonManager>();
                        autoLogonConfigManager.Unconfigure();
                        _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                    }
                    else
                    {
                        Trace.Info("AutoLogon was not configured on the agent.");
                    }
#endif
                }

                //delete agent from the server
                currentAction = StringUtil.Loc("UnregisteringAgent");
                _term.WriteLine(currentAction);
                bool isConfigured   = _store.IsConfigured();
                bool hasCredentials = _store.HasCredentials();
                if (isConfigured && hasCredentials)
                {
                    AgentSettings settings          = _store.GetSettings();
                    var           credentialManager = HostContext.GetService <ICredentialManager>();

                    // Get the credentials
                    var            credProvider = GetCredentialProvider(command, settings.ServerUrl);
                    VssCredentials creds        = credProvider.GetVssCredentials(HostContext);
                    Trace.Info("cred retrieved");

                    bool isDeploymentGroup = (settings.MachineGroupId > 0) || (settings.DeploymentGroupId > 0);

                    Trace.Info("Agent configured for deploymentGroup : {0}", isDeploymentGroup.ToString());

                    string agentType = isDeploymentGroup
                   ? Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration
                   : Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration;

                    var extensionManager = HostContext.GetService <IExtensionManager>();
                    IConfigurationProvider agentProvider = (extensionManager.GetExtensions <IConfigurationProvider>()).FirstOrDefault(x => x.ConfigurationProviderType == agentType);
                    ArgUtil.NotNull(agentProvider, agentType);

                    // Determine the service deployment type based on connection data. (Hosted/OnPremises)
                    bool isHostedServer = await IsHostedServer(settings.ServerUrl, creds);

                    await agentProvider.TestConnectionAsync(settings, creds, isHostedServer);

                    TaskAgent agent = await agentProvider.GetAgentAsync(settings);

                    if (agent == null)
                    {
                        _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                    }
                    else
                    {
                        await agentProvider.DeleteAgentAsync(settings);

                        _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                    }
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("MissingConfig"));
                }

                //delete credential config files
                currentAction = StringUtil.Loc("DeletingCredentials");
                _term.WriteLine(currentAction);
                if (hasCredentials)
                {
                    _store.DeleteCredential();
                    var keyManager = HostContext.GetService <IRSAKeyManager>();
                    keyManager.DeleteKey();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                }

                //delete settings config file
                currentAction = StringUtil.Loc("DeletingSettings");
                _term.WriteLine(currentAction);
                if (isConfigured)
                {
                    // delete proxy setting
                    (HostContext.GetService <IVstsAgentWebProxy>() as VstsAgentWebProxy).DeleteProxySetting();

                    // delete agent cert setting
                    (HostContext.GetService <IAgentCertificateManager>() as AgentCertificateManager).DeleteCertificateSetting();

                    // delete agent runtime option
                    _store.DeleteAgentRuntimeOptions();

                    _store.DeleteSettings();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                }
            }
            catch (Exception)
            {
                _term.WriteLine(StringUtil.Loc("Failed") + currentAction);
                throw;
            }
        }
Ejemplo n.º 42
0
        public async Task CanEnsureMachineGroupAgentConfigureOnPremScenario()
        {
            using (TestHostContext tc = CreateTestContext())
            {
                Tracing trace = tc.GetTrace();

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

                var onPremTfsUrl = "http://localtfs:8080/tfs";

                trace.Info("Preparing command line arguments for vsts scenario");
                var command = new CommandSettings(
                    tc,
                    new[]
                {
                    "configure",
#if !OS_WINDOWS
                    "--acceptteeeula",
#endif
                    "--deploymentgroup",
                    "--url", onPremTfsUrl,
                    "--agent", _expectedAgentName,
                    "--collectionname", _expectedCollectionName,
                    "--projectname", _expectedProjectName,
                    "--deploymentgroupname", _expectedMachineGroupName,
                    "--work", _expectedWorkFolder,
                    "--auth", _expectedAuthType,
                    "--token", _expectedToken
                });
                trace.Info("Constructed.");

                _store.Setup(x => x.IsConfigured()).Returns(false);
                _configMgrAgentSettings = null;

                _extnMgr.Setup(x => x.GetExtensions <IConfigurationProvider>()).Returns(GetConfigurationProviderList(tc));

                _machineGroupServer.Setup(x => x.GetDeploymentGroupsAsync(It.IsAny <string>(), It.IsAny <string>())).Returns(Task.FromResult(GetDeploymentGroups(3, 7)));

                trace.Info("Ensuring all the required parameters are available in the command line parameter");
                await configManager.ConfigureAsync(command);

                _store.Setup(x => x.IsConfigured()).Returns(true);

                trace.Info("Configured, verifying all the parameter value");
                var s = configManager.LoadSettings();
                Assert.NotNull(s);
                Assert.True(s.ServerUrl.Equals(onPremTfsUrl));
                Assert.True(s.AgentName.Equals(_expectedAgentName));
                Assert.True(s.PoolId.Equals(7));
                Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
                Assert.True(s.MachineGroupId.Equals(0));
                Assert.True(s.DeploymentGroupId.Equals(3));
                Assert.Null(s.ProjectName);
                Assert.True(s.ProjectId.Equals(_expectedProjectId));

                // Tags logic should not get trigger
                _machineGroupServer.Verify(x =>
                                           x.UpdateDeploymentTargetsAsync(It.IsAny <Guid>(), It.IsAny <int>(), It.IsAny <List <DeploymentMachine> >()), Times.Never);
            }
        }
Ejemplo n.º 43
0
        public async Task GetSourceAsync(
            IExecutionContext executionContext,
            ServiceEndpoint endpoint,
            CancellationToken cancellationToken)
        {
            Trace.Entering();
            // Validate args.
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(endpoint, nameof(endpoint));

#if OS_WINDOWS
            // Validate .NET Framework 4.6 or higher is installed.
            var netFrameworkUtil = HostContext.GetService <INetFrameworkUtil>();
            if (!netFrameworkUtil.Test(new Version(4, 6)))
            {
                throw new Exception(StringUtil.Loc("MinimumNetFramework46"));
            }
#endif

            // Create the tf command manager.
            var tf = HostContext.CreateService <ITfsVCCommandManager>();
            tf.CancellationToken = cancellationToken;
            tf.Endpoint          = endpoint;
            tf.ExecutionContext  = executionContext;

            // Setup proxy.
            var agentProxy = HostContext.GetService <IVstsAgentWebProxy>();
            if (!string.IsNullOrEmpty(executionContext.Variables.Agent_ProxyUrl) && !agentProxy.IsBypassed(endpoint.Url))
            {
                executionContext.Debug($"Configure '{tf.FilePath}' to work through proxy server '{executionContext.Variables.Agent_ProxyUrl}'.");
                tf.SetupProxy(executionContext.Variables.Agent_ProxyUrl, executionContext.Variables.Agent_ProxyUsername, executionContext.Variables.Agent_ProxyPassword);
            }

            // Add TF to the PATH.
            string tfPath = tf.FilePath;
            ArgUtil.File(tfPath, nameof(tfPath));
            var varUtil = HostContext.GetService <IVarUtil>();
            executionContext.Output(StringUtil.Loc("Prepending0WithDirectoryContaining1", Constants.PathVariable, Path.GetFileName(tfPath)));
            varUtil.PrependPath(Path.GetDirectoryName(tfPath));
            executionContext.Debug($"{Constants.PathVariable}: '{Environment.GetEnvironmentVariable(Constants.PathVariable)}'");

#if OS_WINDOWS
            // Set TFVC_BUILDAGENT_POLICYPATH
            string policyDllPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.ServerOM), "Microsoft.TeamFoundation.VersionControl.Controls.dll");
            ArgUtil.File(policyDllPath, nameof(policyDllPath));
            const string policyPathEnvKey = "TFVC_BUILDAGENT_POLICYPATH";
            executionContext.Output(StringUtil.Loc("SetEnvVar", policyPathEnvKey));
            Environment.SetEnvironmentVariable(policyPathEnvKey, policyDllPath);
#endif

            // Check if the administrator accepted the license terms of the TEE EULA when configuring the agent.
            AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings();
            if (tf.Features.HasFlag(TfsVCFeatures.Eula) && settings.AcceptTeeEula)
            {
                // Check if the "tf eula -accept" command needs to be run for the current user.
                bool skipEula = false;
                try
                {
                    skipEula = tf.TestEulaAccepted();
                }
                catch (Exception ex)
                {
                    executionContext.Debug("Unexpected exception while testing whether the TEE EULA has been accepted for the current user.");
                    executionContext.Debug(ex.ToString());
                }

                if (!skipEula)
                {
                    // Run the command "tf eula -accept".
                    try
                    {
                        await tf.EulaAsync();
                    }
                    catch (Exception ex)
                    {
                        executionContext.Debug(ex.ToString());
                        executionContext.Warning(ex.Message);
                    }
                }
            }

            // Get the workspaces.
            executionContext.Output(StringUtil.Loc("QueryingWorkspaceInfo"));
            ITfsVCWorkspace[] tfWorkspaces = await tf.WorkspacesAsync();

            // Determine the workspace name.
            string buildDirectory = executionContext.Variables.Agent_BuildDirectory;
            ArgUtil.NotNullOrEmpty(buildDirectory, nameof(buildDirectory));
            string workspaceName = $"ws_{Path.GetFileName(buildDirectory)}_{settings.AgentId}";
            executionContext.Variables.Set(Constants.Variables.Build.RepoTfvcWorkspace, workspaceName);

            // Get the definition mappings.
            DefinitionWorkspaceMapping[] definitionMappings =
                JsonConvert.DeserializeObject <DefinitionWorkspaceMappings>(endpoint.Data[WellKnownEndpointData.TfvcWorkspaceMapping])?.Mappings;

            // Determine the sources directory.
            string sourcesDirectory = GetEndpointData(endpoint, Constants.EndpointData.SourcesDirectory);
            ArgUtil.NotNullOrEmpty(sourcesDirectory, nameof(sourcesDirectory));

            // Attempt to re-use an existing workspace if the command manager supports scorch
            // or if clean is not specified.
            ITfsVCWorkspace existingTFWorkspace = null;
            bool            clean = endpoint.Data.ContainsKey(WellKnownEndpointData.Clean) &&
                                    StringUtil.ConvertToBoolean(endpoint.Data[WellKnownEndpointData.Clean], defaultValue: false);
            if (tf.Features.HasFlag(TfsVCFeatures.Scorch) || !clean)
            {
                existingTFWorkspace = WorkspaceUtil.MatchExactWorkspace(
                    executionContext: executionContext,
                    tfWorkspaces: tfWorkspaces,
                    name: workspaceName,
                    definitionMappings: definitionMappings,
                    sourcesDirectory: sourcesDirectory);
                if (existingTFWorkspace != null)
                {
                    if (tf.Features.HasFlag(TfsVCFeatures.GetFromUnmappedRoot))
                    {
                        // Undo pending changes.
                        ITfsVCStatus tfStatus = await tf.StatusAsync(localPath : sourcesDirectory);

                        if (tfStatus?.HasPendingChanges ?? false)
                        {
                            await tf.UndoAsync(localPath : sourcesDirectory);

                            // Cleanup remaining files/directories from pend adds.
                            tfStatus.AllAdds
                            .OrderByDescending(x => x.LocalItem)     // Sort descending so nested items are deleted before their parent is deleted.
                            .ToList()
                            .ForEach(x =>
                            {
                                executionContext.Output(StringUtil.Loc("Deleting", x.LocalItem));
                                IOUtil.Delete(x.LocalItem, cancellationToken);
                            });
                        }
                    }
                    else
                    {
                        // Perform "undo" for each map.
                        foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings ?? new DefinitionWorkspaceMapping[0])
                        {
                            if (definitionMapping.MappingType == DefinitionMappingType.Map)
                            {
                                // Check the status.
                                string       localPath = definitionMapping.GetRootedLocalPath(sourcesDirectory);
                                ITfsVCStatus tfStatus  = await tf.StatusAsync(localPath : localPath);

                                if (tfStatus?.HasPendingChanges ?? false)
                                {
                                    // Undo.
                                    await tf.UndoAsync(localPath : localPath);

                                    // Cleanup remaining files/directories from pend adds.
                                    tfStatus.AllAdds
                                    .OrderByDescending(x => x.LocalItem)     // Sort descending so nested items are deleted before their parent is deleted.
                                    .ToList()
                                    .ForEach(x =>
                                    {
                                        executionContext.Output(StringUtil.Loc("Deleting", x.LocalItem));
                                        IOUtil.Delete(x.LocalItem, cancellationToken);
                                    });
                                }
                            }
                        }
                    }

                    // Scorch.
                    if (clean)
                    {
                        // Try to scorch.
                        try
                        {
                            await tf.ScorchAsync();
                        }
                        catch (ProcessExitCodeException ex)
                        {
                            // Scorch failed.
                            // Warn, drop the folder, and re-clone.
                            executionContext.Warning(ex.Message);
                            existingTFWorkspace = null;
                        }
                    }
                }
            }

            // Create a new workspace.
            if (existingTFWorkspace == null)
            {
                // Remove any conflicting workspaces.
                await RemoveConflictingWorkspacesAsync(
                    tf : tf,
                    tfWorkspaces : tfWorkspaces,
                    name : workspaceName,
                    directory : sourcesDirectory);

                // Remove any conflicting workspace from a different computer.
                // This is primarily a hosted scenario where a registered hosted
                // agent can land on a different computer each time.
                tfWorkspaces = await tf.WorkspacesAsync(matchWorkspaceNameOnAnyComputer : true);

                foreach (ITfsVCWorkspace tfWorkspace in tfWorkspaces ?? new ITfsVCWorkspace[0])
                {
                    await tf.WorkspaceDeleteAsync(tfWorkspace);
                }

                // Recreate the sources directory.
                executionContext.Debug($"Deleting: '{sourcesDirectory}'.");
                IOUtil.DeleteDirectory(sourcesDirectory, cancellationToken);
                Directory.CreateDirectory(sourcesDirectory);

                // Create the workspace.
                await tf.WorkspaceNewAsync();

                // Remove the default mapping.
                if (tf.Features.HasFlag(TfsVCFeatures.DefaultWorkfoldMap))
                {
                    await tf.WorkfoldUnmapAsync("$/");
                }

                // Sort the definition mappings.
                definitionMappings =
                    (definitionMappings ?? new DefinitionWorkspaceMapping[0])
                    .OrderBy(x => x.NormalizedServerPath?.Length ?? 0) // By server path length.
                    .ToArray() ?? new DefinitionWorkspaceMapping[0];

                // Add the definition mappings to the workspace.
                foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings)
                {
                    switch (definitionMapping.MappingType)
                    {
                    case DefinitionMappingType.Cloak:
                        // Add the cloak.
                        await tf.WorkfoldCloakAsync(serverPath : definitionMapping.ServerPath);

                        break;

                    case DefinitionMappingType.Map:
                        // Add the mapping.
                        await tf.WorkfoldMapAsync(
                            serverPath : definitionMapping.ServerPath,
                            localPath : definitionMapping.GetRootedLocalPath(sourcesDirectory));

                        break;

                    default:
                        throw new NotSupportedException();
                    }
                }
            }

            if (tf.Features.HasFlag(TfsVCFeatures.GetFromUnmappedRoot))
            {
                // Get.
                await tf.GetAsync(localPath : sourcesDirectory);
            }
            else
            {
                // Perform "get" for each map.
                foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings ?? new DefinitionWorkspaceMapping[0])
                {
                    if (definitionMapping.MappingType == DefinitionMappingType.Map)
                    {
                        await tf.GetAsync(localPath : definitionMapping.GetRootedLocalPath(sourcesDirectory));
                    }
                }
            }

            // Steps for shelveset/gated.
            string shelvesetName = GetEndpointData(endpoint, Constants.EndpointData.SourceTfvcShelveset);
            if (!string.IsNullOrEmpty(shelvesetName))
            {
                // Steps for gated.
                ITfsVCShelveset tfShelveset        = null;
                string          gatedShelvesetName = GetEndpointData(endpoint, Constants.EndpointData.GatedShelvesetName);
                if (!string.IsNullOrEmpty(gatedShelvesetName))
                {
                    // Clean the last-saved-checkin-metadata for existing workspaces.
                    //
                    // A better long term fix is to add a switch to "tf unshelve" that completely overwrites
                    // the last-saved-checkin-metadata, instead of merging associated work items.
                    //
                    // The targeted workaround for now is to create a trivial change and "tf shelve /move",
                    // which will delete the last-saved-checkin-metadata.
                    if (existingTFWorkspace != null)
                    {
                        executionContext.Output("Cleaning last saved checkin metadata.");

                        // Find a local mapped directory.
                        string firstLocalDirectory =
                            (definitionMappings ?? new DefinitionWorkspaceMapping[0])
                            .Where(x => x.MappingType == DefinitionMappingType.Map)
                            .Select(x => x.GetRootedLocalPath(sourcesDirectory))
                            .FirstOrDefault(x => Directory.Exists(x));
                        if (firstLocalDirectory == null)
                        {
                            executionContext.Warning("No mapped folder found. Unable to clean last-saved-checkin-metadata.");
                        }
                        else
                        {
                            // Create a trival change and "tf shelve /move" to clear the
                            // last-saved-checkin-metadata.
                            string cleanName     = "__tf_clean_wksp_metadata";
                            string tempCleanFile = Path.Combine(firstLocalDirectory, cleanName);
                            try
                            {
                                File.WriteAllText(path: tempCleanFile, contents: "clean last-saved-checkin-metadata", encoding: Encoding.UTF8);
                                await tf.AddAsync(tempCleanFile);

                                await tf.ShelveAsync(shelveset : cleanName, commentFile : tempCleanFile, move : true);
                            }
                            catch (Exception ex)
                            {
                                executionContext.Warning($"Unable to clean last-saved-checkin-metadata. {ex.Message}");
                                try
                                {
                                    await tf.UndoAsync(tempCleanFile);
                                }
                                catch (Exception ex2)
                                {
                                    executionContext.Warning($"Unable to undo '{tempCleanFile}'. {ex2.Message}");
                                }
                            }
                            finally
                            {
                                IOUtil.DeleteFile(tempCleanFile);
                            }
                        }
                    }

                    // Get the shelveset metadata.
                    tfShelveset = await tf.ShelvesetsAsync(shelveset : shelvesetName);

                    // The above command throws if the shelveset is not found,
                    // so the following assertion should never fail.
                    ArgUtil.NotNull(tfShelveset, nameof(tfShelveset));
                }

                // Unshelve.
                await tf.UnshelveAsync(shelveset : shelvesetName);

                // Ensure we undo pending changes for shelveset build at the end.
                _undoShelvesetPendingChanges = true;

                if (!string.IsNullOrEmpty(gatedShelvesetName))
                {
                    // Create the comment file for reshelve.
                    StringBuilder comment    = new StringBuilder(tfShelveset.Comment ?? string.Empty);
                    string        runCi      = GetEndpointData(endpoint, Constants.EndpointData.GatedRunCI);
                    bool          gatedRunCi = StringUtil.ConvertToBoolean(runCi, true);
                    if (!gatedRunCi)
                    {
                        if (comment.Length > 0)
                        {
                            comment.AppendLine();
                        }

                        comment.Append(Constants.Build.NoCICheckInComment);
                    }

                    string commentFile = null;
                    try
                    {
                        commentFile = Path.GetTempFileName();
                        File.WriteAllText(path: commentFile, contents: comment.ToString(), encoding: Encoding.UTF8);

                        // Reshelve.
                        await tf.ShelveAsync(shelveset : gatedShelvesetName, commentFile : commentFile, move : false);
                    }
                    finally
                    {
                        // Cleanup the comment file.
                        if (File.Exists(commentFile))
                        {
                            File.Delete(commentFile);
                        }
                    }
                }
            }

            // Cleanup proxy settings.
            if (!string.IsNullOrEmpty(executionContext.Variables.Agent_ProxyUrl) && !agentProxy.IsBypassed(endpoint.Url))
            {
                executionContext.Debug($"Remove proxy setting for '{tf.FilePath}' to work through proxy server '{executionContext.Variables.Agent_ProxyUrl}'.");
                tf.CleanupProxySetting();
            }
        }
Ejemplo n.º 44
0
        public async Task GetSourceAsync(
            IExecutionContext executionContext,
            ServiceEndpoint endpoint,
            CancellationToken cancellationToken)
        {
            Trace.Entering();
            // Validate args.
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(endpoint, nameof(endpoint));

            // Create the tf command manager.
            var tf = HostContext.CreateService <ITfsVCCommandManager>();

            tf.CancellationToken = cancellationToken;
            tf.Endpoint          = endpoint;
            tf.ExecutionContext  = executionContext;

            // Add TF to the PATH.
            string tfPath = tf.FilePath;

            ArgUtil.File(tfPath, nameof(tfPath));
            var varUtil = HostContext.GetService <IVarUtil>();

            executionContext.Output(StringUtil.Loc("Prepending0WithDirectoryContaining1", Constants.PathVariable, Path.GetFileName(tfPath)));
            varUtil.PrependPath(Path.GetDirectoryName(tfPath));
            executionContext.Debug($"{Constants.PathVariable}: '{Environment.GetEnvironmentVariable(Constants.PathVariable)}'");

            // Check if the administrator accepted the license terms of the TEE EULA when configuring the agent.
            AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings();

            if (tf.Features.HasFlag(TfsVCFeatures.Eula) && settings.AcceptTeeEula)
            {
                // Check if the "tf eula -accept" command needs to be run for the current user.
                bool skipEula = false;
                try
                {
                    skipEula = tf.TestEulaAccepted();
                }
                catch (Exception ex)
                {
                    executionContext.Debug("Unexpected exception while testing whether the TEE EULA has been accepted for the current user.");
                    executionContext.Debug(ex.ToString());
                }

                if (!skipEula)
                {
                    // Run the command "tf eula -accept".
                    try
                    {
                        await tf.EulaAsync();
                    }
                    catch (Exception ex)
                    {
                        executionContext.Debug(ex.ToString());
                        executionContext.Warning(ex.Message);
                    }
                }
            }

            // Get the workspaces.
            executionContext.Output(StringUtil.Loc("QueryingWorkspaceInfo"));
            ITfsVCWorkspace[] tfWorkspaces = await tf.WorkspacesAsync();

            // Determine the workspace name.
            string buildDirectory = executionContext.Variables.Agent_BuildDirectory;

            ArgUtil.NotNullOrEmpty(buildDirectory, nameof(buildDirectory));
            string workspaceName = $"ws_{Path.GetFileName(buildDirectory)}_{settings.AgentId}";

            executionContext.Variables.Set(Constants.Variables.Build.RepoTfvcWorkspace, workspaceName);

            // Get the definition mappings.
            DefinitionWorkspaceMapping[] definitionMappings =
                JsonConvert.DeserializeObject <DefinitionWorkspaceMappings>(endpoint.Data[WellKnownEndpointData.TfvcWorkspaceMapping])?.Mappings;

            // Determine the sources directory.
            string sourcesDirectory = GetEndpointData(endpoint, Constants.EndpointData.SourcesDirectory);

            ArgUtil.NotNullOrEmpty(sourcesDirectory, nameof(sourcesDirectory));

            // Attempt to re-use an existing workspace if the command manager supports scorch
            // or if clean is not specified.
            ITfsVCWorkspace existingTFWorkspace = null;
            bool            clean = endpoint.Data.ContainsKey(WellKnownEndpointData.Clean) &&
                                    StringUtil.ConvertToBoolean(endpoint.Data[WellKnownEndpointData.Clean], defaultValue: false);

            if (tf.Features.HasFlag(TfsVCFeatures.Scorch) || !clean)
            {
                existingTFWorkspace = WorkspaceUtil.MatchExactWorkspace(
                    executionContext: executionContext,
                    tfWorkspaces: tfWorkspaces,
                    name: workspaceName,
                    definitionMappings: definitionMappings,
                    sourcesDirectory: sourcesDirectory);
                if (existingTFWorkspace != null)
                {
                    if (tf.Features.HasFlag(TfsVCFeatures.GetFromUnmappedRoot))
                    {
                        // Undo pending changes.
                        ITfsVCStatus tfStatus = await tf.StatusAsync(localPath : sourcesDirectory);

                        if (tfStatus?.HasPendingChanges ?? false)
                        {
                            await tf.UndoAsync(localPath : sourcesDirectory);

                            // Cleanup remaining files/directories from pend adds.
                            tfStatus.AllAdds
                            .OrderByDescending(x => x.LocalItem)     // Sort descending so nested items are deleted before their parent is deleted.
                            .ToList()
                            .ForEach(x =>
                            {
                                executionContext.Output(StringUtil.Loc("Deleting", x.LocalItem));
                                IOUtil.Delete(x.LocalItem, cancellationToken);
                            });
                        }
                    }
                    else
                    {
                        // Perform "undo" for each map.
                        foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings ?? new DefinitionWorkspaceMapping[0])
                        {
                            if (definitionMapping.MappingType == DefinitionMappingType.Map)
                            {
                                // Check the status.
                                string       localPath = definitionMapping.GetRootedLocalPath(sourcesDirectory);
                                ITfsVCStatus tfStatus  = await tf.StatusAsync(localPath : localPath);

                                if (tfStatus?.HasPendingChanges ?? false)
                                {
                                    // Undo.
                                    await tf.UndoAsync(localPath : localPath);

                                    // Cleanup remaining files/directories from pend adds.
                                    tfStatus.AllAdds
                                    .OrderByDescending(x => x.LocalItem)     // Sort descending so nested items are deleted before their parent is deleted.
                                    .ToList()
                                    .ForEach(x =>
                                    {
                                        executionContext.Output(StringUtil.Loc("Deleting", x.LocalItem));
                                        IOUtil.Delete(x.LocalItem, cancellationToken);
                                    });
                                }
                            }
                        }
                    }

                    // Scorch.
                    if (clean)
                    {
                        // Try to scorch.
                        try
                        {
                            await tf.ScorchAsync();
                        }
                        catch (ProcessExitCodeException ex)
                        {
                            // Scorch failed.
                            // Warn, drop the folder, and re-clone.
                            executionContext.Warning(ex.Message);
                            existingTFWorkspace = null;
                        }
                    }
                }
            }

            // Create a new workspace.
            if (existingTFWorkspace == null)
            {
                // Remove any conflicting workspaces.
                await RemoveConflictingWorkspacesAsync(
                    tf : tf,
                    tfWorkspaces : tfWorkspaces,
                    name : workspaceName,
                    directory : sourcesDirectory);

                // Remove any conflicting workspace from a different computer.
                // This is primarily a hosted scenario where a registered hosted
                // agent can land on a different computer each time.
                tfWorkspaces = await tf.WorkspacesAsync(matchWorkspaceNameOnAnyComputer : true);

                foreach (ITfsVCWorkspace tfWorkspace in tfWorkspaces ?? new ITfsVCWorkspace[0])
                {
                    await tf.WorkspaceDeleteAsync(tfWorkspace);
                }

                // Recreate the sources directory.
                executionContext.Debug($"Deleting: '{sourcesDirectory}'.");
                IOUtil.DeleteDirectory(sourcesDirectory, cancellationToken);
                Directory.CreateDirectory(sourcesDirectory);

                // Create the workspace.
                await tf.WorkspaceNewAsync();

                // Remove the default mapping.
                if (tf.Features.HasFlag(TfsVCFeatures.DefaultWorkfoldMap))
                {
                    await tf.WorkfoldUnmapAsync("$/");
                }

                // Sort the definition mappings.
                definitionMappings =
                    (definitionMappings ?? new DefinitionWorkspaceMapping[0])
                    .OrderBy(x => x.NormalizedServerPath?.Length ?? 0) // By server path length.
                    .ToArray() ?? new DefinitionWorkspaceMapping[0];

                // Add the definition mappings to the workspace.
                foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings)
                {
                    switch (definitionMapping.MappingType)
                    {
                    case DefinitionMappingType.Cloak:
                        // Add the cloak.
                        await tf.WorkfoldCloakAsync(serverPath : definitionMapping.ServerPath);

                        break;

                    case DefinitionMappingType.Map:
                        // Add the mapping.
                        await tf.WorkfoldMapAsync(
                            serverPath : definitionMapping.ServerPath,
                            localPath : definitionMapping.GetRootedLocalPath(sourcesDirectory));

                        break;

                    default:
                        throw new NotSupportedException();
                    }
                }
            }

            if (tf.Features.HasFlag(TfsVCFeatures.GetFromUnmappedRoot))
            {
                // Get.
                await tf.GetAsync(localPath : sourcesDirectory);
            }
            else
            {
                // Perform "get" for each map.
                foreach (DefinitionWorkspaceMapping definitionMapping in definitionMappings ?? new DefinitionWorkspaceMapping[0])
                {
                    if (definitionMapping.MappingType == DefinitionMappingType.Map)
                    {
                        await tf.GetAsync(localPath : definitionMapping.GetRootedLocalPath(sourcesDirectory));
                    }
                }
            }

            string shelvesetName = GetEndpointData(endpoint, Constants.EndpointData.SourceTfvcShelveset);

            if (!string.IsNullOrEmpty(shelvesetName))
            {
                // Get the shelveset details.
                ITfsVCShelveset tfShelveset        = null;
                string          gatedShelvesetName = GetEndpointData(endpoint, Constants.EndpointData.GatedShelvesetName);
                if (!string.IsNullOrEmpty(gatedShelvesetName))
                {
                    tfShelveset = await tf.ShelvesetsAsync(shelveset : shelvesetName);

                    // The command throws if the shelveset is not found.
                    // This assertion should never fail.
                    ArgUtil.NotNull(tfShelveset, nameof(tfShelveset));
                }

                // Unshelve.
                await tf.UnshelveAsync(shelveset : shelvesetName);

                if (!string.IsNullOrEmpty(gatedShelvesetName))
                {
                    // Create the comment file for reshelve.
                    StringBuilder comment    = new StringBuilder(tfShelveset.Comment ?? string.Empty);
                    string        runCi      = GetEndpointData(endpoint, Constants.EndpointData.GatedRunCI);
                    bool          gatedRunCi = StringUtil.ConvertToBoolean(runCi, true);
                    if (!gatedRunCi)
                    {
                        if (comment.Length > 0)
                        {
                            comment.AppendLine();
                        }

                        comment.Append(Constants.Build.NoCICheckInComment);
                    }

                    string commentFile = null;
                    try
                    {
                        commentFile = Path.GetTempFileName();
                        File.WriteAllText(path: commentFile, contents: comment.ToString(), encoding: Encoding.UTF8);

                        // Reshelve.
                        await tf.ShelveAsync(shelveset : gatedShelvesetName, commentFile : commentFile);
                    }
                    finally
                    {
                        // Cleanup the comment file.
                        if (File.Exists(commentFile))
                        {
                            File.Delete(commentFile);
                        }
                    }
                }
            }
        }
Ejemplo n.º 45
0
        public async Task CanEnsureMachineGroupAgentConfigureVSTSScenarioWithTags()
        {
            Guid   receivedProjectId     = Guid.Empty;
            string expectedProcessedTags = string.Empty;
            string tags                      = "Tag3, ,, Tag4  , , ,  Tag1,  , tag3 ";
            string expectedTags              = "Tag3,Tag4,Tag1";
            int    receivedMachineId         = -1;
            int    expectedDeploymentGroupId = 7;
            int    receivedDeploymentGroupId = -1;

            _machineGroupServer.Setup(x =>
                                      x.UpdateDeploymentTargetsAsync(It.IsAny <Guid>(), It.IsAny <int>(),
                                                                     It.IsAny <List <DeploymentMachine> >())).Callback((Guid project, int deploymentGroupId, List <DeploymentMachine> deploymentMachine) =>
            {
                receivedProjectId         = project;
                expectedProcessedTags     = string.Join(",", deploymentMachine.FirstOrDefault().Tags.ToArray());
                receivedMachineId         = deploymentMachine.FirstOrDefault().Id;
                receivedDeploymentGroupId = deploymentGroupId;
            }
                                                                                                                       );

            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 for vsts scenario");
                var command = new CommandSettings(
                    tc,
                    new[]
                {
                    "configure",
#if !OS_WINDOWS
                    "--acceptteeeula",
#endif
                    "--machinegroup",
                    "--adddeploymentgrouptags",
                    "--url", _expectedVSTSServerUrl,
                    "--agent", _expectedAgentName,
                    "--projectname", _expectedProjectName,
                    "--deploymentgroupname", _expectedMachineGroupName,
                    "--work", _expectedWorkFolder,
                    "--auth", _expectedAuthType,
                    "--token", _expectedToken,
                    "--deploymentgrouptags", tags
                });
                trace.Info("Constructed.");

                _store.Setup(x => x.IsConfigured()).Returns(false);
                _configMgrAgentSettings = null;

                _extnMgr.Setup(x => x.GetExtensions <IConfigurationProvider>()).Returns(GetConfigurationProviderList(tc));


                _machineGroupServer.Setup(x => x.GetDeploymentGroupsAsync(It.IsAny <string>(), It.IsAny <string>())).Returns(Task.FromResult(GetDeploymentGroups(expectedDeploymentGroupId, 3)));

                trace.Info("Ensuring all the required parameters are available in the command line parameter");
                await configManager.ConfigureAsync(command);

                _store.Setup(x => x.IsConfigured()).Returns(true);

                trace.Info("Configured, verifying all the parameter value");
                var s = configManager.LoadSettings();
                Assert.NotNull(s);
                Assert.True(s.ServerUrl.Equals(_expectedVSTSServerUrl, StringComparison.CurrentCultureIgnoreCase));
                Assert.True(s.AgentName.Equals(_expectedAgentName));
                Assert.True(s.PoolId.Equals(3));
                Assert.True(s.DeploymentGroupId.Equals(7));
                Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
                Assert.True(s.MachineGroupId.Equals(0));
                Assert.Null(s.ProjectName);
                Assert.True(s.ProjectId.Equals(_expectedProjectId));

                Assert.True(receivedProjectId.Equals(new Guid(_expectedProjectId)), "UpdateDeploymentMachinesGroupAsync should get call with correct project name");
                Assert.True(expectedTags.Equals(expectedProcessedTags), "Before applying the tags, should get processed ( Trim, Remove duplicate)");
                Assert.True(receivedMachineId.Equals(_expectedDeploymentMachineId), "UpdateDeploymentMachinesGroupAsync should get call with correct machine id");
                Assert.True(receivedDeploymentGroupId.Equals(expectedDeploymentGroupId), "UpdateDeploymentMachinesGroupAsync should get call with correct deployment group id");
                // Tags logic should get trigger
                _machineGroupServer.Verify(x =>
                                           x.UpdateDeploymentTargetsAsync(It.IsAny <Guid>(), It.IsAny <int>(),
                                                                          It.IsAny <List <DeploymentMachine> >()), Times.Once);
            }
        }
Ejemplo n.º 46
0
 public void ThrowTaskAgentExistException(AgentSettings agentSettings)
 {
     throw new TaskAgentExistsException(StringUtil.Loc("DeploymentMachineWithSameNameAlreadyExistInDeploymentGroup", agentSettings.DeploymentGroupId, agentSettings.AgentName));
 }
Ejemplo n.º 47
0
        public async Task <TaskResult> RunAsync(AgentJobRequestMessage message, CancellationToken jobRequestCancellationToken)
        {
            // Validate parameters.
            Trace.Entering();
            ArgUtil.NotNull(message, nameof(message));
            ArgUtil.NotNull(message.Environment, nameof(message.Environment));
            ArgUtil.NotNull(message.Environment.Variables, nameof(message.Environment.Variables));
            ArgUtil.NotNull(message.Tasks, nameof(message.Tasks));
            Trace.Info("Job ID {0}", message.JobId);

            // Agent.RunMode
            RunMode runMode;

            if (message.Environment.Variables.ContainsKey(Constants.Variables.Agent.RunMode) &&
                Enum.TryParse(message.Environment.Variables[Constants.Variables.Agent.RunMode], ignoreCase: true, result: out runMode) &&
                runMode == RunMode.Local)
            {
                HostContext.RunMode = runMode;
            }

            // System.AccessToken
            if (message.Environment.Variables.ContainsKey(Constants.Variables.System.EnableAccessToken) &&
                StringUtil.ConvertToBoolean(message.Environment.Variables[Constants.Variables.System.EnableAccessToken]))
            {
                // TODO: get access token use Util Method
                message.Environment.Variables[Constants.Variables.System.AccessToken] = message.Environment.SystemConnection.Authorization.Parameters["AccessToken"];
            }

            // Make sure SystemConnection Url and Endpoint Url match Config Url base
            ReplaceConfigUriBaseInJobRequestMessage(message);

            // Setup the job server and job server queue.
            var            jobServer           = HostContext.GetService <IJobServer>();
            VssCredentials jobServerCredential = ApiUtil.GetVssCredential(message.Environment.SystemConnection);
            Uri            jobServerUrl        = message.Environment.SystemConnection.Url;

            Trace.Info($"Creating job server with URL: {jobServerUrl}");
            // jobServerQueue is the throttling reporter.
            _jobServerQueue = HostContext.GetService <IJobServerQueue>();
            VssConnection jobConnection = ApiUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) });
            await jobServer.ConnectAsync(jobConnection);

            _jobServerQueue.Start(message);

            IExecutionContext             jobContext = null;
            CancellationTokenRegistration?agentShutdownRegistration = null;

            try
            {
                // Create the job execution context.
                jobContext = HostContext.CreateService <IExecutionContext>();
                jobContext.InitializeJob(message, jobRequestCancellationToken);
                Trace.Info("Starting the job execution context.");
                jobContext.Start();
                jobContext.Section(StringUtil.Loc("StepStarting", message.JobName));

                agentShutdownRegistration = HostContext.AgentShutdownToken.Register(() =>
                {
                    // log an issue, then agent get shutdown by Ctrl-C or Ctrl-Break.
                    // the server will use Ctrl-Break to tells the agent that operating system is shutting down.
                    string errorMessage;
                    switch (HostContext.AgentShutdownReason)
                    {
                    case ShutdownReason.UserCancelled:
                        errorMessage = StringUtil.Loc("UserShutdownAgent");
                        break;

                    case ShutdownReason.OperatingSystemShutdown:
                        errorMessage = StringUtil.Loc("OperatingSystemShutdown", Environment.MachineName);
                        break;

                    default:
                        throw new ArgumentException(HostContext.AgentShutdownReason.ToString(), nameof(HostContext.AgentShutdownReason));
                    }
                    jobContext.AddIssue(new Issue()
                    {
                        Type = IssueType.Error, Message = errorMessage
                    });
                });

                // Set agent version variable.
                jobContext.Variables.Set(Constants.Variables.Agent.Version, Constants.Agent.Version);
                jobContext.Output(StringUtil.Loc("AgentVersion", Constants.Agent.Version));

                // Print proxy setting information for better diagnostic experience
                var agentWebProxy = HostContext.GetService <IVstsAgentWebProxy>();
                if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
                {
                    jobContext.Output(StringUtil.Loc("AgentRunningBehindProxy", agentWebProxy.ProxyAddress));
                }

                // Validate directory permissions.
                string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
                Trace.Info($"Validating directory permissions for: '{workDirectory}'");
                try
                {
                    Directory.CreateDirectory(workDirectory);
                    IOUtil.ValidateExecutePermission(workDirectory);
                }
                catch (Exception ex)
                {
                    Trace.Error(ex);
                    jobContext.Error(ex);
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                // Set agent variables.
                AgentSettings settings = HostContext.GetService <IConfigurationStore>().GetSettings();
                jobContext.Variables.Set(Constants.Variables.Agent.Id, settings.AgentId.ToString(CultureInfo.InvariantCulture));
                jobContext.Variables.Set(Constants.Variables.Agent.HomeDirectory, IOUtil.GetRootPath());
                jobContext.Variables.Set(Constants.Variables.Agent.JobName, message.JobName);
                jobContext.Variables.Set(Constants.Variables.Agent.MachineName, Environment.MachineName);
                jobContext.Variables.Set(Constants.Variables.Agent.Name, settings.AgentName);
                jobContext.Variables.Set(Constants.Variables.Agent.OS, VarUtil.OS);
                jobContext.Variables.Set(Constants.Variables.Agent.RootDirectory, IOUtil.GetWorkPath(HostContext));
#if OS_WINDOWS
                jobContext.Variables.Set(Constants.Variables.Agent.ServerOMDirectory, Path.Combine(IOUtil.GetExternalsPath(), Constants.Path.ServerOMDirectory));
#endif
                jobContext.Variables.Set(Constants.Variables.Agent.WorkFolder, IOUtil.GetWorkPath(HostContext));
                jobContext.Variables.Set(Constants.Variables.System.WorkFolder, IOUtil.GetWorkPath(HostContext));

                if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_TOOLSDIRECTORY")))
                {
                    string toolsDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.ToolDirectory);
                    Directory.CreateDirectory(toolsDirectory);
                    jobContext.Variables.Set(Constants.Variables.Agent.ToolsDirectory, toolsDirectory);
                }

                // Setup TEMP directories
                _tempDirectoryManager = HostContext.GetService <ITempDirectoryManager>();
                _tempDirectoryManager.InitializeTempDirectory(jobContext);

                // todo: task server can throw. try/catch and fail job gracefully.
                // prefer task definitions url, then TFS collection url, then TFS account url
                var taskServer    = HostContext.GetService <ITaskServer>();
                Uri taskServerUri = null;
                if (!string.IsNullOrEmpty(jobContext.Variables.System_TaskDefinitionsUri))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TaskDefinitionsUri);
                }
                else if (!string.IsNullOrEmpty(jobContext.Variables.System_TFCollectionUrl))
                {
                    taskServerUri = new Uri(jobContext.Variables.System_TFCollectionUrl);
                }

                var taskServerCredential = ApiUtil.GetVssCredential(message.Environment.SystemConnection);
                if (taskServerUri != null)
                {
                    Trace.Info($"Creating task server with {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                if (taskServerUri == null || !await taskServer.TaskDefinitionEndpointExist())
                {
                    Trace.Info($"Can't determine task download url from JobMessage or the endpoint doesn't exist.");
                    var configStore = HostContext.GetService <IConfigurationStore>();
                    taskServerUri = new Uri(configStore.GetSettings().ServerUrl);
                    Trace.Info($"Recreate task server with configuration server url: {taskServerUri}");
                    await taskServer.ConnectAsync(ApiUtil.CreateConnection(taskServerUri, taskServerCredential));
                }

                // Expand the endpoint data values.
                foreach (ServiceEndpoint endpoint in jobContext.Endpoints)
                {
                    jobContext.Variables.ExpandValues(target: endpoint.Data);
                    VarUtil.ExpandEnvironmentVariables(HostContext, target: endpoint.Data);
                }

                // Get the job extension.
                Trace.Info("Getting job extension.");
                var hostType         = jobContext.Variables.System_HostType;
                var extensionManager = HostContext.GetService <IExtensionManager>();
                // We should always have one job extension
                IJobExtension jobExtension =
                    (extensionManager.GetExtensions <IJobExtension>() ?? new List <IJobExtension>())
                    .Where(x => x.HostType.HasFlag(hostType))
                    .FirstOrDefault();
                ArgUtil.NotNull(jobExtension, nameof(jobExtension));

                List <IStep> preJobSteps  = new List <IStep>();
                List <IStep> jobSteps     = new List <IStep>();
                List <IStep> postJobSteps = new List <IStep>();

                try
                {
                    Trace.Info("Initialize job. Getting all job steps.");
                    var initializeResult = await jobExtension.InitializeJob(jobContext, message);

                    preJobSteps  = initializeResult.PreJobSteps;
                    jobSteps     = initializeResult.JobSteps;
                    postJobSteps = initializeResult.PostJobStep;
                }
                catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested)
                {
                    // set the job to canceled
                    // don't log error issue to job ExecutionContext, since server owns the job level issue
                    Trace.Error($"Job is canceled during initialize.");
                    Trace.Error($"Caught exception: {ex}");
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Canceled));
                }
                catch (Exception ex)
                {
                    // set the job to failed.
                    // don't log error issue to job ExecutionContext, since server owns the job level issue
                    Trace.Error($"Job initialize failed.");
                    Trace.Error($"Caught exception from {nameof(jobExtension.InitializeJob)}: {ex}");
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                // trace out all steps
                Trace.Info($"Total pre-job steps: {preJobSteps.Count}.");
                Trace.Verbose($"Pre-job steps: '{string.Join(", ", preJobSteps.Select(x => x.DisplayName))}'");

                Trace.Info($"Total job steps: {jobSteps.Count}.");
                Trace.Verbose($"Job steps: '{string.Join(", ", jobSteps.Select(x => x.DisplayName))}'");

                Trace.Info($"Total post-job steps: {postJobSteps.Count}.");
                Trace.Verbose($"Post-job steps: '{string.Join(", ", postJobSteps.Select(x => x.DisplayName))}'");

                // Run all pre job steps
                // All pre job steps are critical to the job
                // Stop execution on any step failure or cancelled
                Trace.Info("Run all pre-job steps.");
                var stepsRunner = HostContext.GetService <IStepsRunner>();
                try
                {
                    await stepsRunner.RunAsync(jobContext, preJobSteps, JobRunStage.PreJob);
                }
                catch (Exception ex)
                {
                    // StepRunner should never throw exception out.
                    // End up here mean there is a bug in StepRunner
                    // Log the error and fail the job.
                    Trace.Error($"Caught exception from pre-job steps {nameof(StepsRunner)}: {ex}");
                    jobContext.Error(ex);
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                Trace.Info($"Job result after all pre-job steps finish: {jobContext.Result}");

                // Base on the Job result after all pre-job steps finish.
                // Run all job steps only if the job result is still Succeeded or SucceededWithIssues
                if (jobContext.Result == null ||
                    jobContext.Result == TaskResult.Succeeded ||
                    jobContext.Result == TaskResult.SucceededWithIssues)
                {
                    Trace.Info("Run all job steps.");
                    try
                    {
                        await stepsRunner.RunAsync(jobContext, jobSteps, JobRunStage.Main);
                    }
                    catch (Exception ex)
                    {
                        // StepRunner should never throw exception out.
                        // End up here mean there is a bug in StepRunner
                        // Log the error and fail the job.
                        Trace.Error($"Caught exception from job steps {nameof(StepsRunner)}: {ex}");
                        jobContext.Error(ex);
                        return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                    }
                }
                else
                {
                    Trace.Info("Skip all job steps due to pre-job step failure.");
                    foreach (var step in jobSteps)
                    {
                        step.ExecutionContext.Start();
                        step.ExecutionContext.Complete(TaskResult.Skipped);
                    }
                }

                Trace.Info($"Job result after all job steps finish: {jobContext.Result}");

                // Always run all post job steps
                // step might not run base on it's own condition.
                Trace.Info("Run all post-job steps.");
                try
                {
                    await stepsRunner.RunAsync(jobContext, postJobSteps, JobRunStage.PostJob);
                }
                catch (Exception ex)
                {
                    // StepRunner should never throw exception out.
                    // End up here mean there is a bug in StepRunner
                    // Log the error and fail the job.
                    Trace.Error($"Caught exception from post-job steps {nameof(StepsRunner)}: {ex}");
                    jobContext.Error(ex);
                    return(await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed));
                }

                Trace.Info($"Job result after all post-job steps finish: {jobContext.Result}");

                // Complete the job.
                Trace.Info("Completing the job execution context.");
                return(await CompleteJobAsync(jobServer, jobContext, message));
            }
            finally
            {
                if (agentShutdownRegistration != null)
                {
                    agentShutdownRegistration.Value.Dispose();
                    agentShutdownRegistration = null;
                }

                await ShutdownQueue(throwOnFailure : false);
            }
        }
Ejemplo n.º 48
0
 public void GetServerUrl(AgentSettings agentSettings, CommandSettings command)
 {
     agentSettings.ServerUrl = command.GetUrl();
 }
Ejemplo n.º 49
0
 public void ThrowTaskAgentExistException(AgentSettings agentSettings)
 {
     throw new TaskAgentExistsException(StringUtil.Loc("AgentWithSameNameAlreadyExistInPool", agentSettings.PoolId, agentSettings.AgentName));
 }
Ejemplo n.º 50
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());
                }
            }
        }