예제 #1
0
        public VssCredentials LoadCredentials(bool preferMigrated = true)
        {
            IConfigurationStore store = HostContext.GetService <IConfigurationStore>();

            if (!store.HasCredentials())
            {
                throw new InvalidOperationException("Credentials not stored.  Must reconfigure.");
            }

            CredentialData credData = store.GetCredentials();

            if (preferMigrated)
            {
                var migratedCred = store.GetMigratedCredentials();
                if (migratedCred != null)
                {
                    credData = migratedCred;
                }
            }

            ICredentialProvider credProv = GetCredentialProvider(credData.Scheme);

            credProv.CredentialData = credData;

            VssCredentials creds = credProv.GetVssCredentials(HostContext);

            return(creds);
        }
        public VssCredentials LoadCredentials()
        {
            IConfigurationStore store = HostContext.GetService <IConfigurationStore>();

            if (!store.HasCredentials())
            {
                throw new InvalidOperationException("Credentials not stored.  Must reconfigure.");
            }

            CredentialData credData     = store.GetCredentials();
            var            migratedCred = store.GetMigratedCredentials();

            if (migratedCred != null)
            {
                credData = migratedCred;

                // Re-write .credentials with Token URL
                store.SaveCredential(credData);

                // Delete .credentials_migrated
                store.DeleteMigratedCredential();
            }

            ICredentialProvider credProv = GetCredentialProvider(credData.Scheme);

            credProv.CredentialData = credData;

            VssCredentials creds = credProv.GetVssCredentials(HostContext);

            return(creds);
        }
예제 #3
0
        public VssCredentials LoadCredentials()
        {
            IConfigurationStore store = HostContext.GetService <IConfigurationStore>();

            if (!store.HasCredentials())
            {
                throw new InvalidOperationException("Credentials not stored.  Must reconfigure.");
            }

            CredentialData      credData = store.GetCredentials();
            ICredentialProvider credProv = GetCredentialProvider(credData.Scheme);

            credProv.CredentialData = credData;

            VssCredentials creds = credProv.GetVssCredentials(HostContext);

            return(creds);
        }
예제 #4
0
        public async Task <int> LocalRunAsync(CommandSettings command, CancellationToken token)
        {
            Trace.Info(nameof(LocalRunAsync));

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

            HostContext.RunMode = RunMode.Local;

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

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

                ymlFile = ymlFiles.FirstOrDefault();
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                // Get the auth type. On premise defaults to negotiate (Kerberos with fallback to NTLM).
                // Hosted defaults to PAT authentication.
                string defaultAuthType = UrlUtil.IsHosted(url) ? Constants.Configuration.PAT :
                                         (Constants.Agent.Platform == Constants.OSPlatform.Windows ? Constants.Configuration.Integrated : Constants.Configuration.Negotiate);
                string authType = command.GetAuth(defaultValue: defaultAuthType);
                ICredentialProvider provider = credentialManager.GetCredentialProvider(authType);
                provider.EnsureCredential(HostContext, command, url);
                _taskStore.HttpClient = new TaskAgentHttpClient(new Uri(url), provider.GetVssCredentials(HostContext));
            }

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

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

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

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

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

                default:
                    return(Constants.Agent.ReturnCode.TerminatedError);
                }
            }
            finally
            {
                if (jobDispatcher != null)
                {
                    await jobDispatcher.ShutdownAsync();
                }
            }
        }
예제 #5
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(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 Constants.OSPlatform.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.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();
            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.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
        }
예제 #6
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();
            }
        }
예제 #7
0
        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();

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

            _term.WriteSection("Authentication");
            while (true)
            {
                // Get the URL
                var inputUrl = command.GetUrl();
                if (!inputUrl.Contains("github.com", StringComparison.OrdinalIgnoreCase) &&
                    !inputUrl.Contains("github.localhost", StringComparison.OrdinalIgnoreCase))
                {
                    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)
                    isHostedServer = await IsHostedServer(runnerSettings.ServerUrl, creds);

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

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

                    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);
                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 runner 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
            {
                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 async Task ConfigureAsync(CommandSettings command)
        {
            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

            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 = command.DeploymentGroup
                ? 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
            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
                {
                    // Validate can connect.
                    await agentProvider.TestConnectionAsync(agentSettings, 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
            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 Host 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.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}'.");
                    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)
            {
                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
            {
                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);
            _term.WriteLine(StringUtil.Loc("SavedSettings", DateTime.UtcNow));

#if OS_WINDOWS
            // config windows service as part of configuration
            bool runAsService = command.GetRunAsService();
            if (runAsService)
            {
                Trace.Info("Configuring to run the agent as service");
                var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>();
                serviceControlManager.ConfigureService(agentSettings, command);
            }
            //This will be enabled with AutoLogon code changes are tested
            else if (command.GetEnableAutoLogon())
            {
                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
        }
예제 #9
0
        public async Task <int> LocalRunAsync(CommandSettings command, CancellationToken token)
        {
            Trace.Info(nameof(LocalRunAsync));

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

            HostContext.RunMode = RunMode.Local;

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

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

                ymlFile = ymlFiles.FirstOrDefault();
            }

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

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

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

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

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

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

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

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

                // Get the auth type. On premise defaults to negotiate (Kerberos with fallback to NTLM).
                // Hosted defaults to PAT authentication.
                string defaultAuthType = UrlUtil.IsHosted(url) ? Constants.Configuration.PAT :
                                         (Constants.Agent.Platform == Constants.OSPlatform.Windows ? Constants.Configuration.Integrated : Constants.Configuration.Negotiate);
                string authType = command.GetAuth(defaultValue: defaultAuthType);
                ICredentialProvider provider = credentialManager.GetCredentialProvider(authType);
                provider.EnsureCredential(HostContext, command, url);
                _taskStore.HttpClient = new TaskAgentHttpClient(new Uri(url), provider.GetVssCredentials(HostContext));
            }

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

            // Create job message.
            IJobDispatcher jobDispatcher = null;

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

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

            return(Constants.Agent.ReturnCode.Success);
        }
        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"));
                }
            }

            // 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)
            {
                poolName = command.GetPool();
                try
                {
                    poolId = await GetPoolId(poolName);

                    Trace.Info($"PoolId for agent pool '{poolName}' is '{poolId}'.");
                    break;
                }
                catch (Exception e) when(!command.Unattended)
                {
                    _term.WriteError(e);
                    _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())
                    {
                        // Update existing agent with new PublicKey, agent version and SystemCapabilities.
                        agent = UpdateExistingAgent(agent, publicKey, systemCapabilities);

                        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 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 _agentServer.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,
            };

            _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
        }
예제 #11
0
        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 registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");

                    GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, 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 || UrlUtil.IsHostedServer(new UriBuilder(runnerSettings.GitHubUrl));

                    // Warn if the Actions server url and GHES server url has different Host
                    if (!runnerSettings.IsHostedServer)
                    {
                        // Example actionsServerUrl is https://my-ghes/_services/pipelines/[...]
                        // Example githubServerUrl is https://my-ghes
                        var actionsServerUrl = new Uri(runnerSettings.ServerUrl);
                        var githubServerUrl  = new Uri(runnerSettings.GitHubUrl);
                        if (!string.Equals(actionsServerUrl.Authority, githubServerUrl.Authority, StringComparison.OrdinalIgnoreCase))
                        {
                            throw new InvalidOperationException($"GitHub Actions is not properly configured in GHES. GHES url: {runnerSettings.GitHubUrl}, Actions url: {runnerSettings.ServerUrl}.");
                        }
                    }

                    // 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");

            // If we have more than one runner group available, allow the user to specify which one to be added into
            string               poolName   = null;
            TaskAgentPool        agentPool  = null;
            List <TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync();

            TaskAgentPool defaultPool = agentPools?.Where(x => x.IsInternal).FirstOrDefault();

            if (agentPools?.Where(x => !x.IsHosted).Count() > 1)
            {
                poolName = command.GetRunnerGroupName(defaultPool?.Name);
                _term.WriteLine();
                agentPool = agentPools.Where(x => string.Equals(poolName, x.Name, StringComparison.OrdinalIgnoreCase) && !x.IsHosted).FirstOrDefault();
            }
            else
            {
                agentPool = defaultPool;
            }

            if (agentPool == null && poolName == null)
            {
                throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner groups. Contact support.");
            }
            else if (agentPool == null && poolName != null)
            {
                throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner group named \"{poolName}\".");
            }
            else
            {
                Trace.Info("Found a self-hosted runner group 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($"A runner exists with the same 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;

            // 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 },
                        { "requireFipsCryptography", agent.Properties.GetValue("RequireFipsCryptography", false).ToString()}
                    },
                };

                // 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
        }
예제 #12
0
        public async Task ConfigureAsync(Dictionary <string, string> args, HashSet <string> flags, bool enforceSupplied)
        {
            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

            Trace.Info("Read agent settings");
            var consoleWizard = HostContext.GetService <IConsoleWizard>();

            // 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 credProv = null;

            while (true)
            {
                WriteSection("Connect");
                serverUrl = consoleWizard.ReadValue(CliArgs.Url,
                                                    StringUtil.Loc("ServerUrl"),
                                                    false,
                                                    String.Empty,
                                                    Validators.ServerUrlValidator,
                                                    args,
                                                    enforceSupplied);
                Trace.Info("serverUrl: {0}", serverUrl);

                credProv = AcquireCredentials(args, enforceSupplied);
                VssCredentials creds = credProv.GetVssCredentials(HostContext);

                Trace.Info("cred retrieved");

                bool connected = true;
                try
                {
                    await TestConnectAsync(serverUrl, creds);
                }
                catch (Exception e)
                {
                    Trace.Error(e);
                    _term.WriteLine(StringUtil.Loc("FailedToConnect"));
                    connected = false;
                }

                // we don't want to loop on unattend
                if (enforceSupplied || connected)
                {
                    break;
                }
            }

            // TODO: Create console agent service so we can hide in testing etc... and trace
            _term.WriteLine(StringUtil.Loc("SavingCredential"));
            Trace.Verbose("Saving credential");
            _store.SaveCredential(credProv.CredentialData);

            Trace.Info("Connect Complete.");

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

            WriteSection("Register Agent");

            while (true)
            {
                poolName = consoleWizard.ReadValue(CliArgs.Pool,
                                                   "Pool Name", // Not localized as pool name is a technical term
                                                   false,
                                                   "default",
                                                   // can do better
                                                   Validators.NonEmptyValidator,
                                                   args,
                                                   enforceSupplied);

                try
                {
                    poolId = await GetPoolId(poolName);
                }
                catch (Exception e)
                {
                    Trace.Error(e);
                }

                if (enforceSupplied || poolId > 0)
                {
                    break;
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("FailedToFindPool"));
                }
            }

            var capProvider = HostContext.GetService <ICapabilitiesProvider>();

            while (true)
            {
                agentName = consoleWizard.ReadValue(CliArgs.Agent,
                                                    StringUtil.Loc("AgentName"),
                                                    false,
                                                    Environment.MachineName ?? "myagent",
                                                    // can do better
                                                    Validators.NonEmptyValidator,
                                                    args,
                                                    enforceSupplied);

                Dictionary <string, string> capabilities = await capProvider.GetCapabilitiesAsync(agentName, CancellationToken.None);

                TaskAgent agent = await GetAgent(agentName, poolId);

                bool exists     = agent != null;
                bool replace    = false;
                bool registered = false;
                if (exists)
                {
                    replace = consoleWizard.ReadBool(CliArgs.Replace,
                                                     StringUtil.Loc("Replece"),
                                                     false,
                                                     args,
                                                     enforceSupplied);
                    if (replace)
                    {
                        // update - update instead of delete so we don't lose user capabilities etc...
                        agent.MaxParallelism = Constants.Agent.MaxParallelism;
                        agent.Version        = Constants.Agent.Version;

                        foreach (var capability in capabilities)
                        {
                            agent.SystemCapabilities.Add(capability.Key, capability.Value);
                        }

                        try
                        {
                            agent = await UpdateAgent(poolId, agent);

                            _term.WriteLine(StringUtil.Loc("AgentReplaced"));
                            registered = true;
                        }
                        catch (Exception e)
                        {
                            Trace.Error(e);
                            _term.WriteLine(StringUtil.Loc("FailedToReplaceAgent"));
                        }
                    }
                }
                else
                {
                    agent = new TaskAgent(agentName)
                    {
                        MaxParallelism = Constants.Agent.MaxParallelism,
                        Version        = Constants.Agent.Version
                    };

                    foreach (var capability in capabilities)
                    {
                        agent.SystemCapabilities.Add(capability.Key, capability.Value);
                    }

                    try
                    {
                        agent = await AddAgent(poolId, agent);

                        _term.WriteLine(StringUtil.Loc("AgentAddedSuccessfully"));
                        registered = true;
                    }
                    catch (Exception e)
                    {
                        Trace.Error(e);
                        _term.WriteLine(StringUtil.Loc("AddAgentFailed"));
                    }
                }
                agentId = agent.Id;

                if (enforceSupplied || registered)
                {
                    break;
                }
            }

            // We will Combine() what's stored with root.  Defaults to string a relative path
            string workFolder = consoleWizard.ReadValue(CliArgs.Work,
                                                        StringUtil.Loc("WorkFolderDescription"),
                                                        false,
                                                        "_work",
                                                        // can do better
                                                        Validators.NonEmptyValidator,
                                                        args,
                                                        enforceSupplied);

            // Get Agent settings
            var settings = new AgentSettings
            {
                AgentId    = agentId,
                ServerUrl  = serverUrl,
                AgentName  = agentName,
                PoolName   = poolName,
                PoolId     = poolId,
                WorkFolder = workFolder,
            };

            bool runAsService = false;

            if (flags != null && flags.Contains("runasservice"))
            {
                runAsService = true;
            }
            else
            {
                runAsService = consoleWizard.ReadBool(
                    CliArgs.RunAsService,
                    StringUtil.Loc("RunAgentAsServiceDescription"),
                    false,
                    null,
                    enforceSupplied);
            }

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

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

            _store.SaveSettings(settings);

            if (runAsService && successfullyConfigured)
            {
                Trace.Info("Configuration was successful, trying to start the service");
                serviceControlManager.StartService(settings.ServiceName);
            }
        }
예제 #13
0
        public async Task <int> RunAsync(CommandSettings command, CancellationToken token)
        {
            Trace.Info(nameof(RunAsync));
            var           configStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings    = configStore.GetSettings();

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

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

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

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

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

            // Create job message.
            IJobDispatcher jobDispatcher = null;

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

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

            return(Constants.Agent.ReturnCode.Success);
        }
        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.
                }
            }

            _term.WriteLine(StringUtil.Loc("SavingCredential"));
            Trace.Verbose("Saving credential");
            _store.SaveCredential(credProvider.CredentialData);

            // 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())
                    {
                        // 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)
                    {
                        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"));
                    }
                }
            }

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

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

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

            bool runAsService           = command.GetRunAsService();
            var  serviceControlManager  = HostContext.GetService <IServiceControlManager>();
            bool successfullyConfigured = false;

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

            // chown/chmod the _diag and settings files to the current user, if we started with sudo.
            // Also if we started with sudo, the _diag will be owned by root. Change this to current login user
            if (Constants.Agent.Platform == Constants.OSPlatform.Linux ||
                Constants.Agent.Platform == Constants.OSPlatform.OSX)
            {
                string uidValue = Environment.GetEnvironmentVariable("SUDO_UID");
                string gidValue = Environment.GetEnvironmentVariable("SUDO_GID");

                if (!string.IsNullOrEmpty(uidValue) && !string.IsNullOrEmpty(gidValue))
                {
                    var filesToChange = new Dictionary <string, string>
                    {
                        { IOUtil.GetDiagPath(), "775" },
                        { IOUtil.GetConfigFilePath(), "770" },
                        { IOUtil.GetCredFilePath(), "770" },
                    };
                    var unixUtil = HostContext.CreateService <IUnixUtil>();
                    foreach (var file in filesToChange)
                    {
                        await unixUtil.Chown(uidValue, gidValue, file.Key);

                        await unixUtil.Chmod(file.Value, file.Key);
                    }
                }
            }

            if (runAsService && successfullyConfigured)
            {
                Trace.Info("Configuration was successful, trying to start the service");
                serviceControlManager.StartService();
            }
        }