public override void EnsureCredential( IHostContext context, CommandSettings command, String serverUrl) { // Nothing to verify here }
public override bool ConfigureService( AgentSettings settings, CommandSettings command) { Trace.Entering(); throw new NotSupportedException("Systemd Configure Service"); }
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl) { ArgUtil.NotNull(context, nameof(context)); Tracing trace = context.GetTrace(nameof(PersonalAccessToken)); trace.Info(nameof(EnsureCredential)); ArgUtil.NotNull(command, nameof(command)); CredentialData.Data[Constants.Agent.CommandLine.Args.Token] = command.GetToken(); }
public async Task EnsureConfiguredAsync(CommandSettings command) { Trace.Info(nameof(EnsureConfiguredAsync)); if (!IsConfigured()) { await ConfigureAsync(command); } }
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl) { ArgUtil.NotNull(context, nameof(context)); Tracing trace = context.GetTrace(nameof(PersonalAccessToken)); trace.Info(nameof(EnsureCredential)); ArgUtil.NotNull(command, nameof(command)); ArgUtil.NotNullOrEmpty(serverUrl, nameof(serverUrl)); //TODO: use Validators.NTAccountValidator when it works on Linux CredentialData.Data[Constants.Agent.CommandLine.Args.UserName] = command.GetUserName(); CredentialData.Data[Constants.Agent.CommandLine.Args.Password] = command.GetPassword(); CredentialData.Data[Constants.Agent.CommandLine.Args.Url] = serverUrl; }
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl) { //Integrated credentials do not require any configuration parameters }
public async Task <TaskAgent> UpdateAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { var deploymentMachine = (await this.GetDeploymentMachinesAsync(agentSettings)).FirstOrDefault(); deploymentMachine.Agent = agent; deploymentMachine = await _deploymentGroupServer.ReplaceDeploymentMachineAsync(new Guid(agentSettings.ProjectId), agentSettings.DeploymentGroupId, deploymentMachine.Id, deploymentMachine); await GetAndAddTags(deploymentMachine, agentSettings, command); return(deploymentMachine.Agent); }
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl) { CredentialData.Data[Constants.Agent.CommandLine.Args.UserName] = command.GetUserName(); CredentialData.Data[Constants.Agent.CommandLine.Args.Password] = command.GetPassword(); }
public void Assign(CommandSettings settings, ITypeResolver resolver, object?value) { // Is the property pair deconstructable? // TODO: This needs to be better defined if (Property.PropertyType.IsPairDeconstructable() && WantRawValue) { var genericTypes = Property.PropertyType.GetGenericArguments(); var multimap = (IMultiMap?)Property.GetValue(settings); if (multimap == null) { multimap = Activator.CreateInstance(typeof(MultiMap <,>).MakeGenericType(genericTypes[0], genericTypes[1])) as IMultiMap; if (multimap == null) { throw new InvalidOperationException("Could not create multimap"); } } // Create deconstructor. var deconstructorType = PairDeconstructor?.Type ?? typeof(DefaultPairDeconstructor); if (!(resolver.Resolve(deconstructorType) is IPairDeconstructor deconstructor)) { if (!(Activator.CreateInstance(deconstructorType) is IPairDeconstructor activatedDeconstructor)) { throw new InvalidOperationException($"Could not create pair deconstructor."); } deconstructor = activatedDeconstructor; } // Deconstruct and add to multimap. var pair = deconstructor.Deconstruct(resolver, genericTypes[0], genericTypes[1], value as string); if (pair.Key != null) { multimap.Add(pair); } value = multimap; } else if (Property.PropertyType.IsArray) { // Add a new item to the array var array = (Array?)Property.GetValue(settings); Array newArray; var elementType = Property.PropertyType.GetElementType(); if (elementType == null) { throw new InvalidOperationException("Could not get property type."); } if (array == null) { newArray = Array.CreateInstance(elementType, 1); } else { newArray = Array.CreateInstance(elementType, array.Length + 1); array.CopyTo(newArray, 0); } newArray.SetValue(value, newArray.Length - 1); value = newArray; } else if (IsFlagValue()) { var flagValue = (IFlagValue?)Property.GetValue(settings); if (flagValue == null) { flagValue = (IFlagValue?)Activator.CreateInstance(ParameterType); if (flagValue == null) { throw new InvalidOperationException("Could not create flag value."); } } if (value != null) { // Null means set, but not with a valid value. flagValue.Value = value; } // If the parameter was mapped, then it's set. flagValue.IsSet = true; value = flagValue; } Property.SetValue(settings, value); }
private ICredentialProvider GetCredentialProvider(CommandSettings command, string serverUrl) { Trace.Info(nameof(GetCredentialProvider)); var credentialManager = HostContext.GetService<ICredentialManager>(); // Get the auth type. On premise defaults to negotiate (Kerberos with fallback to NTLM). // Hosted defaults to PAT authentication. bool isHosted = serverUrl.IndexOf("visualstudio.com", StringComparison.OrdinalIgnoreCase) != -1 || serverUrl.IndexOf("tfsallin.net", StringComparison.OrdinalIgnoreCase) != -1; string defaultAuth = isHosted ? Constants.Configuration.PAT : (Constants.Agent.Platform == Constants.OSPlatform.Windows ? Constants.Configuration.Integrated : Constants.Configuration.Negotiate); string authType = command.GetAuth(defaultValue: defaultAuth); // Create the credential. Trace.Info("Creating credential for auth: {0}", authType); var provider = credentialManager.GetCredentialProvider(authType); provider.EnsureCredential(HostContext, command, serverUrl); return provider; }
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(); } // Create the configuration provider as per agent type..... string agentType = command.MachineGroup ? 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); // 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; VssCredentials creds = null; WriteSection(StringUtil.Loc("ConnectSectionHeader")); while (true) { // Get the URL serverUrl = agentProvider.GetServerUrl(command); // Get the credentials credProvider = GetCredentialProvider(command, serverUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("cred retrieved"); try { // Validate can connect. await agentProvider.TestConnectionAsync(serverUrl, creds); 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 string poolName = null; int poolId = 0; string agentName = null; WriteSection(StringUtil.Loc("RegisterAgentSectionHeader")); while (true) { try { poolId = await agentProvider.GetPoolId(command); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError(agentProvider.GetFailedToFindPoolErrorString()); } } 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()) { // Update existing agent with new PublicKey, agent version and SystemCapabilities. agent = UpdateExistingAgent(agent, publicKey, systemCapabilities); try { agent = await agentProvider.UpdateAgentAsync(poolId, agent); _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. throw new TaskAgentExistsException(StringUtil.Loc("AgentWithSameNameAlreadyExistInPool", poolId, agentName)); } } else { // Create a new agent. agent = CreateNewAgent(agentName, publicKey, systemCapabilities); try { agent = await agentProvider.AddAgentAsync(poolId, agent); _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully")); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("AddAgentFailed")); } } } // 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 Host component of the url remain the same. UriBuilder inputServerUrl = new UriBuilder(serverUrl); UriBuilder serverReturnedServerUrl = new UriBuilder(agentServerUrl); if (Uri.Compare(inputServerUrl.Uri, serverReturnedServerUrl.Uri, UriComponents.Host, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0) { inputServerUrl.Path = serverReturnedServerUrl.Path; Trace.Info($"Replace server returned url's host component with user input server url's host: '{inputServerUrl.Uri.AbsoluteUri}'."); serverUrl = inputServerUrl.Uri.AbsoluteUri; } else { 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) { 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(); // notificationPipeName for Hosted agent provisioner. 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 }; // This is required in case agent is configured as DeploymentAgent. It will make entry for projectName and MachineGroup agentProvider.UpdateAgentSetting(settings); _store.SaveSettings(settings); _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow)); #if OS_WINDOWS // config windows service as part of configuration bool runAsService = command.GetRunAsService(); if (!runAsService) { return; } else { 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")); } Trace.Info("Configuring to run the agent as service"); var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.ConfigureService(settings, command); } #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(settings); #endif }
//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 = JobRequestMessageTypes.AgentJobRequest }; 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 <AgentJobRequestMessage>())) .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 <AgentJobRequestMessage>()), 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()); } } }
public async Task ConfigureAsync(CommandSettings command) { _term.WriteLine(); _term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); _term.WriteLine("| ____ _ _ _ _ _ _ _ _ |", ConsoleColor.White); _term.WriteLine("| / ___(_) |_| | | |_ _| |__ / \\ ___| |_(_) ___ _ __ ___ |", ConsoleColor.White); _term.WriteLine("| | | _| | __| |_| | | | | '_ \\ / _ \\ / __| __| |/ _ \\| '_ \\/ __| |", ConsoleColor.White); _term.WriteLine("| | |_| | | |_| _ | |_| | |_) | / ___ \\ (__| |_| | (_) | | | \\__ \\ |", ConsoleColor.White); _term.WriteLine("| \\____|_|\\__|_| |_|\\__,_|_.__/ /_/ \\_\\___|\\__|_|\\___/|_| |_|___/ |", ConsoleColor.White); _term.WriteLine("| |", ConsoleColor.White); _term.Write("| ", ConsoleColor.White); _term.Write("Self-hosted runner registration", ConsoleColor.Cyan); _term.WriteLine(" |", ConsoleColor.White); _term.WriteLine("| |", ConsoleColor.White); _term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); Trace.Info(nameof(ConfigureAsync)); if (IsConfigured()) { throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first."); } RunnerSettings runnerSettings = new RunnerSettings(); // Loop getting url and creds until you can connect ICredentialProvider credProvider = null; VssCredentials creds = null; _term.WriteSection("Authentication"); while (true) { // When testing against a dev deployment of Actions Service, set this environment variable var useDevActionsServiceUrl = Environment.GetEnvironmentVariable("USE_DEV_ACTIONS_SERVICE_URL"); var inputUrl = command.GetUrl(); if (inputUrl.Contains("codedev.ms", StringComparison.OrdinalIgnoreCase) || useDevActionsServiceUrl != null) { runnerSettings.ServerUrl = inputUrl; // Get the credentials credProvider = GetCredentialProvider(command, runnerSettings.ServerUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("legacy vss cred retrieved"); } else { runnerSettings.GitHubUrl = inputUrl; var githubToken = command.GetRunnerRegisterToken(); GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register); runnerSettings.ServerUrl = authResult.TenantUrl; creds = authResult.ToVssCredentials(); Trace.Info("cred retrieved via GitHub auth"); } try { // Determine the service deployment type based on connection data. (Hosted/OnPremises) runnerSettings.IsHostedServer = runnerSettings.GitHubUrl == null || IsHostedServer(new UriBuilder(runnerSettings.GitHubUrl)); // Validate can connect. await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), creds); _term.WriteLine(); _term.WriteSuccessMessage("Connected to GitHub"); Trace.Info("Test Connection complete."); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to connect. Try again or ctrl-c to quit"); _term.WriteLine(); } } // 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); } _term.WriteSection("Runner Registration"); //Get all the agent pools, and select the first private pool List <TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync(); TaskAgentPool agentPool = agentPools?.Where(x => x.IsHosted == false).FirstOrDefault(); if (agentPool == null) { throw new TaskAgentPoolNotFoundException($"Could not find any private pool. Contact support."); } else { Trace.Info("Found a private pool with id {1} and name {2}", agentPool.Id, agentPool.Name); runnerSettings.PoolId = agentPool.Id; runnerSettings.PoolName = agentPool.Name; } TaskAgent agent; while (true) { runnerSettings.AgentName = command.GetRunnerName(); _term.WriteLine(); var userLabels = command.GetLabels(); _term.WriteLine(); var agents = await _runnerServer.GetAgentsAsync(runnerSettings.PoolId, runnerSettings.AgentName); Trace.Verbose("Returns {0} agents", agents.Count); agent = agents.FirstOrDefault(); if (agent != null) { _term.WriteLine("A runner exists with the same name", ConsoleColor.Yellow); if (command.GetReplace()) { // Update existing agent with new PublicKey, agent version. agent = UpdateExistingAgent(agent, publicKey, userLabels); try { agent = await _runnerServer.ReplaceAgentAsync(runnerSettings.PoolId, agent); _term.WriteSuccessMessage("Successfully replaced the runner"); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to replace the runner. Try again or ctrl-c to quit"); } } else if (command.Unattended) { // if not replace and it is unattended config. throw new TaskAgentExistsException($"Pool {runnerSettings.PoolId} already contains a runner with name {runnerSettings.AgentName}."); } } else { // Create a new agent. agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels); try { agent = await _runnerServer.AddAgentAsync(runnerSettings.PoolId, agent); _term.WriteSuccessMessage("Runner successfully added"); break; } catch (Exception e) when(!command.Unattended) { _term.WriteError(e); _term.WriteError("Failed to add the runner. Try again or ctrl-c to quit"); } } } // Add Agent Id to settings runnerSettings.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(runnerSettings.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}'."); runnerSettings.ServerUrl = inputServerUrl.Uri.AbsoluteUri; } else { runnerSettings.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) { UriBuilder configServerUrl = new UriBuilder(runnerSettings.ServerUrl); UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl); 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 { throw new NotSupportedException("Message queue listen OAuth token."); } // Testing agent connection, detect any potential connection issue, like local clock skew that cause OAuth token expired. var credMgr = HostContext.GetService <ICredentialManager>(); VssCredentials credential = credMgr.LoadCredentials(); try { await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), credential); // ConnectAsync() hits _apis/connectionData which is an anonymous endpoint // Need to hit an authenticate endpoint to trigger OAuth token exchange. await _runnerServer.GetAgentPoolsAsync(); _term.WriteSuccessMessage("Runner connection is good"); } 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("The local machine's clock may be out of sync with the server time by more than five minutes. Please sync your clock with your domain or internet time and try again."); } _term.WriteSection("Runner settings"); // We will Combine() what's stored with root. Defaults to string a relative path runnerSettings.WorkFolder = command.GetWork(); runnerSettings.MonitorSocketAddress = command.GetMonitorSocketAddress(); _store.SaveSettings(runnerSettings); _term.WriteLine(); _term.WriteSuccessMessage("Settings Saved."); _term.WriteLine(); #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(runnerSettings, command); } #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(runnerSettings); #endif }
public virtual async Task <TaskAgent> UpdateAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); ArgUtil.NotNull(command, nameof(command)); var deploymentMachine = (await this.GetDeploymentTargetsAsync(agentSettings)).FirstOrDefault(); deploymentMachine.Agent = agent; deploymentMachine = await _deploymentGroupServer.ReplaceDeploymentTargetAsync(new Guid(agentSettings.ProjectId), agentSettings.DeploymentGroupId, deploymentMachine.Id, deploymentMachine); await GetAndAddTags(deploymentMachine, agentSettings, command); return(deploymentMachine.Agent); }
public async Task ConfigureAsync(CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); if (!_windowsServiceHelper.IsRunningInElevatedMode()) { Trace.Error("Needs Administrator privileges to configure agent with AutoLogon capability."); throw new SecurityException(StringUtil.Loc("NeedAdminForAutologonCapability")); } string domainName; string userName; string logonAccount; string logonPassword; while (true) { logonAccount = command.GetWindowsLogonAccount(defaultValue: string.Empty, descriptionMsg: StringUtil.Loc("AutoLogonAccountNameDescription")); 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); logonPassword = command.GetWindowsLogonPassword(logonAccount); if (_windowsServiceHelper.IsValidAutoLogonCredential(domainName, userName, logonPassword)) { Trace.Info("Credential validation succeeded"); break; } if (command.Unattended()) { throw new SecurityException(StringUtil.Loc("InvalidAutoLogonCredential")); } Trace.Error("Invalid credential entered."); _terminal.WriteError(StringUtil.Loc("InvalidAutoLogonCredential")); } _autoLogonRegManager.GetAutoLogonUserDetails(out string currentAutoLogonUserDomainName, out string currentAutoLogonUserName); if (currentAutoLogonUserName != null && !userName.Equals(currentAutoLogonUserName, StringComparison.CurrentCultureIgnoreCase) && !domainName.Equals(currentAutoLogonUserDomainName, StringComparison.CurrentCultureIgnoreCase)) { string currentAutoLogonAccount = String.Format("{0}\\{1}", currentAutoLogonUserDomainName, currentAutoLogonUserName); if (string.IsNullOrEmpty(currentAutoLogonUserDomainName) || currentAutoLogonUserDomainName.Equals(".", StringComparison.CurrentCultureIgnoreCase)) { currentAutoLogonAccount = String.Format("{0}\\{1}", Environment.MachineName, currentAutoLogonUserName); } Trace.Warning($"AutoLogon already enabled for {currentAutoLogonAccount}."); if (!command.GetOverwriteAutoLogon(currentAutoLogonAccount)) { Trace.Error("Marking the agent configuration as failed due to the denial of autologon setting overwriting by the user."); throw new Exception(StringUtil.Loc("AutoLogonOverwriteDeniedError", currentAutoLogonAccount)); } Trace.Info($"Continuing with the autologon configuration."); } // grant permission for agent root folder and work folder Trace.Info("Create local group and grant folder permission to logon account."); string agentRoot = HostContext.GetDirectory(WellKnownDirectory.Root); string workFolder = HostContext.GetDirectory(WellKnownDirectory.Work); Directory.CreateDirectory(workFolder); _windowsServiceHelper.GrantDirectoryPermissionForAccount(logonAccount, new[] { agentRoot, workFolder }); _autoLogonRegManager.UpdateRegistrySettings(command, domainName, userName, logonPassword); _windowsServiceHelper.SetAutoLogonPassword(logonPassword); await ConfigurePowerOptions(); SaveAutoLogonSettings(domainName, userName); RestartBasedOnUserInput(command); }
public async Task UnconfigureAsync(CommandSettings command) { string currentAction = string.Empty; _term.WriteSection("Runner removal"); try { //stop, uninstall service and remove service config file if (_store.IsServiceConfigured()) { currentAction = "Removing service"; _term.WriteLine(currentAction); #if OS_WINDOWS var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.UnconfigureService(); _term.WriteLine(); _term.WriteSuccessMessage("Runner service removed"); #elif OS_LINUX // unconfig system D service first throw new Exception("Unconfigure service first"); #elif OS_OSX // unconfig osx service first throw new Exception("Unconfigure service first"); #endif } //delete agent from the server currentAction = "Removing runner from the server"; bool isConfigured = _store.IsConfigured(); bool hasCredentials = _store.HasCredentials(); if (isConfigured && hasCredentials) { RunnerSettings settings = _store.GetSettings(); var credentialManager = HostContext.GetService <ICredentialManager>(); // Get the credentials VssCredentials creds = null; if (string.IsNullOrEmpty(settings.GitHubUrl)) { var credProvider = GetCredentialProvider(command, settings.ServerUrl); creds = credProvider.GetVssCredentials(HostContext); Trace.Info("legacy vss cred retrieved"); } else { var githubToken = command.GetRunnerDeletionToken(); GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove); creds = authResult.ToVssCredentials(); Trace.Info("cred retrieved via GitHub auth"); } // Determine the service deployment type based on connection data. (Hosted/OnPremises) await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds); var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName); Trace.Verbose("Returns {0} agents", agents.Count); TaskAgent agent = agents.FirstOrDefault(); if (agent == null) { _term.WriteLine("Does not exist. Skipping " + currentAction); } else { await _runnerServer.DeleteAgentAsync(settings.PoolId, settings.AgentId); _term.WriteLine(); _term.WriteSuccessMessage("Runner removed successfully"); } } else { _term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server."); } //delete credential config files currentAction = "Removing .credentials"; if (hasCredentials) { _store.DeleteCredential(); var keyManager = HostContext.GetService <IRSAKeyManager>(); keyManager.DeleteKey(); _term.WriteSuccessMessage("Removed .credentials"); } else { _term.WriteLine("Does not exist. Skipping " + currentAction); } //delete settings config file currentAction = "Removing .runner"; if (isConfigured) { _store.DeleteSettings(); _term.WriteSuccessMessage("Removed .runner"); } else { _term.WriteLine("Does not exist. Skipping " + currentAction); } } catch (Exception) { _term.WriteError("Failed: " + currentAction); throw; } _term.WriteLine(); }
public Task <TaskAgent> AddAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { return(_agentServer.AddAgentAsync(agentSettings.PoolId, agent)); }
public void GetServerUrl(AgentSettings agentSettings, CommandSettings command) { agentSettings.ServerUrl = command.GetUrl(); }
public async Task <TaskAgent> AddAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { var deploymentMachine = new DeploymentMachine() { Agent = agent }; deploymentMachine = await _deploymentGroupServer.AddDeploymentMachineAsync(new Guid(agentSettings.ProjectId), agentSettings.DeploymentGroupId, deploymentMachine); await GetAndAddTags(deploymentMachine, agentSettings, command); return(deploymentMachine.Agent); }
public abstract bool ConfigureService(AgentSettings settings, CommandSettings command);
public virtual async Task <TaskAgent> AddAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); ArgUtil.NotNull(command, nameof(command)); var deploymentMachine = new DeploymentMachine() { Agent = agent }; var azureSubscriptionId = await GetAzureSubscriptionIdAsync(); if (!String.IsNullOrEmpty(azureSubscriptionId)) { deploymentMachine.Properties.Add("AzureSubscriptionId", azureSubscriptionId); } deploymentMachine = await _deploymentGroupServer.AddDeploymentTargetAsync(new Guid(agentSettings.ProjectId), agentSettings.DeploymentGroupId, deploymentMachine); await GetAndAddTags(deploymentMachine, agentSettings, command); return(deploymentMachine.Agent); }
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()) { #if OS_WINDOWS if (!new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)) { Trace.Error("Needs Administrator privileges for unconfigure windows service agent."); throw new SecurityException(StringUtil.Loc("NeedAdminForUnconfigWinServiceAgent")); } 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 } //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."); Trace.Info("Agent configured for machineGroup : {0}", settings.MachineGroup.ToString()); string agentType = settings.MachineGroup ? 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); List <TaskAgent> agents = await agentSvr.GetAgentsAsync(settings.PoolId, settings.AgentName); if (agents.Count == 0) { _term.WriteLine(StringUtil.Loc("Skipping") + currentAction); } else { await agentProvider.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 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(); } }
private void btnSettings_Click(object sender, RoutedEventArgs e) { this.ReloadList(CommandSettings.ShowBox(this)); }
//process 2 new job messages, and one cancel message public async void TestRunAsync() { using (var hc = new TestHostContext(this)) { //Arrange var agent = new Agent.Listener.Agent(); 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); hc.SetSingleton <IVstsAgentWebProxy>(_proxy.Object); hc.SetSingleton <IAgentCertificateManager>(_cert.Object); hc.SetSingleton <IConfigurationStore>(_configStore.Object); agent.Initialize(hc); var settings = new AgentSettings { PoolId = 43242 }; var message = new TaskAgentMessage() { Body = JsonUtility.ToString(CreateJobRequestMessage("job1")), MessageId = 4234, MessageType = JobRequestMessageTypes.AgentJobRequest }; 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); _messageListener.Setup(x => x.CreateSessionAsync(It.IsAny <CancellationToken>())) .Returns(Task.FromResult <bool>(true)); _messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny <CancellationToken>())) .Returns(async() => { if (0 == messages.Count) { signalWorkerComplete.Release(); await Task.Delay(2000, hc.AgentShutdownToken); } return(messages.Dequeue()); }); _messageListener.Setup(x => x.DeleteSessionAsync()) .Returns(Task.CompletedTask); _messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>())) .Returns(Task.CompletedTask); _jobDispatcher.Setup(x => x.Run(It.IsAny <Pipelines.AgentJobRequestMessage>(), It.IsAny <bool>())) .Callback(() => { }); _jobNotification.Setup(x => x.StartClient(It.IsAny <String>(), It.IsAny <String>(), It.IsAny <CancellationToken>())) .Callback(() => { }); _jobNotification.Setup(x => x.StartClient(It.IsAny <String>(), It.IsAny <String>())) .Callback(() => { }); hc.EnqueueInstance <IJobDispatcher>(_jobDispatcher.Object); _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act var command = new CommandSettings(hc, new string[] { "run" }); 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 hc.ShutdownAgent(ShutdownReason.UserCancelled); //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 <Pipelines.AgentJobRequestMessage>(), It.IsAny <bool>()), 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()); _messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>()), Times.AtLeastOnce()); } } }
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 object Get(CommandSettings settings) { return(Property.GetValue(settings)); }
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 async Task UnconfigureAsync(CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); string currentAction = string.Empty; try { //stop, uninstall service and remove service config file if (_store.IsServiceConfigured()) { currentAction = StringUtil.Loc("UninstallingService"); _term.WriteLine(currentAction); if (PlatformUtil.RunningOnWindows) { var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>(); serviceControlManager.UnconfigureService(); _term.WriteLine(StringUtil.Loc("Success") + currentAction); } else if (PlatformUtil.RunningOnLinux) { // unconfig systemd service first throw new Exception(StringUtil.Loc("UnconfigureServiceDService")); } else if (PlatformUtil.RunningOnMacOS) { // unconfig macOS service first throw new Exception(StringUtil.Loc("UnconfigureOSXService")); } } else { if (PlatformUtil.RunningOnWindows) { //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."); } } } //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 isEnvironmentVMResource = false; bool isDeploymentGroup = (settings.MachineGroupId > 0) || (settings.DeploymentGroupId > 0); if (!isDeploymentGroup) { isEnvironmentVMResource = settings.EnvironmentId > 0; } Trace.Info("Agent configured for deploymentGroup : {0}", isDeploymentGroup.ToString()); string agentType = isDeploymentGroup ? Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration : isEnvironmentVMResource ? Constants.Agent.AgentConfigurationProvider.EnvironmentVMResourceConfiguration : 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; } }
private async Task GetAndAddTags(DeploymentMachine deploymentMachine, AgentSettings agentSettings, CommandSettings command) { // Get and apply Tags in case agent is configured against Deployment Group if (command.GetDeploymentGroupTagsRequired()) { try { string tagString = command.GetDeploymentGroupTags(); Trace.Info("Given tags - {0} will be processed and added", tagString); if (!string.IsNullOrWhiteSpace(tagString)) { var tagsList = tagString.Split(',').Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()) .Distinct(StringComparer.CurrentCultureIgnoreCase).ToList(); if (tagsList.Any()) { Trace.Info("Adding tags - {0}", string.Join(",", tagsList.ToArray())); deploymentMachine.Tags = tagsList; await _deploymentGroupServer.UpdateDeploymentTargetsAsync(new Guid(agentSettings.ProjectId), agentSettings.DeploymentGroupId, new List <DeploymentMachine>() { deploymentMachine }); _term.WriteLine(StringUtil.Loc("DeploymentGroupTagsAddedMsg")); } } } catch (Exception e) when(!command.Unattended()) { _term.WriteError(e); _term.WriteError(StringUtil.Loc("FailedToAddTags")); } } }
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); await agentProvider.TestConnectionAsync(settings, creds); 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(); _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 async void TestRunOnceHandleUpdateMessage() { using (var hc = new TestHostContext(this)) { //Arrange var agent = new Agent.Listener.Agent(); 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); hc.SetSingleton <IVstsAgentWebProxy>(_proxy.Object); hc.SetSingleton <IAgentCertificateManager>(_cert.Object); hc.SetSingleton <IConfigurationStore>(_configStore.Object); hc.SetSingleton <ISelfUpdater>(_updater.Object); agent.Initialize(hc); var settings = new AgentSettings { PoolId = 43242, AgentId = 5678 }; var message1 = new TaskAgentMessage() { Body = JsonUtility.ToString(new AgentRefreshMessage(settings.AgentId, "2.123.0")), MessageId = 4234, MessageType = AgentRefreshMessage.MessageType }; var messages = new Queue <TaskAgentMessage>(); messages.Enqueue(message1); _updater.Setup(x => x.SelfUpdate(It.IsAny <AgentRefreshMessage>(), It.IsAny <IJobDispatcher>(), It.IsAny <bool>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(true)); _configurationManager.Setup(x => x.LoadSettings()) .Returns(settings); _configurationManager.Setup(x => x.IsConfigured()) .Returns(true); _messageListener.Setup(x => x.CreateSessionAsync(It.IsAny <CancellationToken>())) .Returns(Task.FromResult <bool>(true)); _messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny <CancellationToken>())) .Returns(async() => { if (0 == messages.Count) { await Task.Delay(2000); } return(messages.Dequeue()); }); _messageListener.Setup(x => x.DeleteSessionAsync()) .Returns(Task.CompletedTask); _messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>())) .Returns(Task.CompletedTask); _jobNotification.Setup(x => x.StartClient(It.IsAny <String>(), It.IsAny <String>(), It.IsAny <CancellationToken>())) .Callback(() => { }); _jobNotification.Setup(x => x.StartClient(It.IsAny <String>(), It.IsAny <String>())) .Callback(() => { }); hc.EnqueueInstance <IJobDispatcher>(_jobDispatcher.Object); _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act var command = new CommandSettings(hc, new string[] { "run", "--once" }); Task <int> agentTask = agent.ExecuteCommand(command); //Assert //wait for the agent to exit with right return code await Task.WhenAny(agentTask, Task.Delay(30000)); Assert.True(agentTask.IsCompleted, $"{nameof(agent.ExecuteCommand)} timed out."); Assert.True(!agentTask.IsFaulted, agentTask.Exception?.ToString()); Assert.True(agentTask.Result == Constants.Agent.ReturnCode.RunOnceAgentUpdating); _updater.Verify(x => x.SelfUpdate(It.IsAny <AgentRefreshMessage>(), It.IsAny <IJobDispatcher>(), false, It.IsAny <CancellationToken>()), Times.Once); _jobDispatcher.Verify(x => x.Run(It.IsAny <Pipelines.AgentJobRequestMessage>(), true), Times.Never()); _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()); _messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>()), Times.Once()); } }
public override async Task <TaskAgent> AddAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); ArgUtil.NotNull(agent, nameof(agent)); ArgUtil.NotNull(command, nameof(command)); var virtualMachine = new VirtualMachineResource() { Name = agent.Name, Agent = agent }; var tags = GetVirtualMachineResourceTags(command); virtualMachine.Tags = tags; virtualMachine = await _environmentsServer.AddEnvironmentVMAsync(new Guid(agentSettings.ProjectId), agentSettings.EnvironmentId, virtualMachine); Trace.Info("Environment virtual machine resource with name: '{0}', id: '{1}' has been added successfully.", virtualMachine.Name, virtualMachine.Id); var pool = await _environmentsServer.GetEnvironmentPoolAsync(new Guid(agentSettings.ProjectId), agentSettings.EnvironmentId); Trace.Info("environment pool id: '{0}'", pool.Id); agentSettings.PoolId = pool.Id; agentSettings.AgentName = virtualMachine.Name; agentSettings.EnvironmentVMResourceId = virtualMachine.Id; return(virtualMachine.Agent); }
public void Assign(CommandSettings settings, object value) { Property.SetValue(settings, value); }
public override async Task <TaskAgent> UpdateAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); ArgUtil.NotNull(command, nameof(command)); var tags = GetVirtualMachineResourceTags(command); var vmResource = (await GetEnvironmentVMsAsync(agentSettings)).FirstOrDefault(); vmResource.Agent = agent; vmResource.Tags = tags; Trace.Info("Replacing environment virtual machine resource with id: '{0}'", vmResource.Id); vmResource = await _environmentsServer.ReplaceEnvironmentVMAsync(new Guid(agentSettings.ProjectId), agentSettings.EnvironmentId, vmResource); Trace.Info("environment virtual machine resource with id: '{0}' has been replaced successfully", vmResource.Id); var pool = await _environmentsServer.GetEnvironmentPoolAsync(new Guid(agentSettings.ProjectId), agentSettings.EnvironmentId); agentSettings.AgentName = vmResource.Name; agentSettings.EnvironmentVMResourceId = vmResource.Id; agentSettings.PoolId = pool.Id; return(vmResource.Agent); }
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, environment vm agent bool isDeploymentGroupScenario = command.GetDeploymentOrMachineGroup() || command.GetDeploymentPool() || command.GetEnvironmentVMResource(); 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)); } } // grant permission for agent root folder and work folder Trace.Info("Create local group and grant folder permission to service logon account."); string agentRoot = HostContext.GetDirectory(WellKnownDirectory.Root); string workFolder = HostContext.GetDirectory(WellKnownDirectory.Work); Directory.CreateDirectory(workFolder); _windowsServiceHelper.GrantDirectoryPermissionForAccount(logonAccount, new[] { agentRoot, workFolder }); _term.WriteLine(StringUtil.Loc("GrantingFilePermissions", 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); }
public void GetServerUrl(AgentSettings agentSettings, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); ArgUtil.NotNull(command, nameof(command)); agentSettings.ServerUrl = command.GetUrl(); }
public async Task ConfigureAsync(CommandSettings command) { ArgUtil.NotNull(command, nameof(command)); 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 (PlatformUtil.HostOS) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: // Write the section header. WriteSection(StringUtil.Loc("EulasSectionHeader")); // Verify the EULA exists on disk in the expected location. string eulaFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), 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 PlatformUtil.OS.Windows: // Warn and continue if .NET 4.6 is not installed. if (!NetFrameworkUtil.Test(new Version(4, 6), Trace)) { 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.GetDeploymentOrMachineGroup()) { agentType = Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration; } else if (command.GetDeploymentPool()) { agentType = Constants.Agent.AgentConfigurationProvider.SharedDeploymentAgentConfiguration; } else if (command.GetEnvironmentVMResource()) { agentType = Constants.Agent.AgentConfigurationProvider.EnvironmentVMResourceConfiguration; } 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.GetPoolIdAndName(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 (PlatformUtil.HostOS) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: // Save the provided admin cred for compat with previous agent. _store.SaveCredential(credProvider.CredentialData); break; case PlatformUtil.OS.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(); var agentSvr = HostContext.GetService <IAgentServer>(); try { await agentSvr.ConnectAsync(new Uri(agentSettings.ServerUrl), credential); } 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.MonitorSocketAddress = command.GetMonitorSocketAddress(); 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 (PlatformUtil.RunningOnWindows && command.GetGitUseSChannel()) { saveRuntimeOptions = true; runtimeOptions.GitUseSecureChannel = true; } if (saveRuntimeOptions) { Trace.Info("Save agent runtime options to disk."); _store.SaveAgentRuntimeOptions(runtimeOptions); } if (PlatformUtil.RunningOnWindows) { // 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 } } else if (PlatformUtil.RunningOnLinux) { // generate service config script for Linux var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>(); serviceControlManager.GenerateScripts(agentSettings); } else if (PlatformUtil.RunningOnMacOS) { // generate service config script for macOS var serviceControlManager = HostContext.GetService <IMacOSServiceControlManager>(); serviceControlManager.GenerateScripts(agentSettings); } }
public void GetCollectionName(AgentSettings agentSettings, CommandSettings command, bool isHosted) { // Collection name is not required for Build/Release agent }
public abstract void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl);
public Task <TaskAgent> UpdateAgentAsync(AgentSettings agentSettings, TaskAgent agent, CommandSettings command) { ArgUtil.NotNull(agentSettings, nameof(agentSettings)); return(_agentServer.UpdateAgentAsync(agentSettings.PoolId, agent)); }
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; }