コード例 #1
0
        public void PublishEvent(IExecutionContext context, CustomerIntelligenceEvent ciEvent)
        {
            ICustomerIntelligenceServer ciService;
            VssConnection vssConnection;

            try
            {
                ciService     = context.GetHostContext().GetService <ICustomerIntelligenceServer>();
                vssConnection = WorkerUtilities.GetVssConnection(context);
                ciService.Initialize(vssConnection);
            }
            catch (SocketException ex)
            {
                ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(context).Uri.ToString(), context.Warning);
                return;
            }
            catch (Exception ex)
            {
                context.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message));
                return;
            }

            var commandContext = context.GetHostContext().CreateService <IAsyncCommandContext>();

            commandContext.InitializeCommandContext(context, StringUtil.Loc("Telemetry"));
            commandContext.Task = PublishEventsAsync(context, ciService, ciEvent);
        }
コード例 #2
0
        public async Task <Stream> GetFileTask(ContainerItem ticketedItem, CancellationToken cancellationToken)
        {
            ArgUtil.NotNull(ticketedItem, nameof(ticketedItem));

            this._executionContext.Debug(StringUtil.Format("Get file container client for file {0}", ticketedItem.Path));

            VssConnection vssConnection = await GetVssConnection();

            FileContainerHttpClient fileContainer = null;

            try
            {
                fileContainer = vssConnection.GetClient <FileContainerHttpClient>();
            }
            catch (SocketException e)
            {
                ExceptionsUtil.HandleSocketException(e, vssConnection.Uri.ToString(), this._executionContext.Error);
                throw;
            }

            this._executionContext.Debug(StringUtil.Format("Start fetch file stream from filecontainer service for file {0}", ticketedItem.Path));

            Stream stream = await fileContainer.DownloadFileAsync(
                ticketedItem.ContainerId,
                ticketedItem.Path,
                cancellationToken,
                scopeIdentifier : ticketedItem.ScopeIdentifier);

            this._executionContext.Debug(StringUtil.Format("Finished fetch file stream from filecontainer service for file {0}", ticketedItem.Path));

            return(stream);
        }
コード例 #3
0
        public async Task ConnectAsync(VssConnection jobConnection)
        {
            ArgUtil.NotNull(jobConnection, nameof(jobConnection));

            _connection = jobConnection;
            int attemptCount = 5;

            while (!_connection.HasAuthenticated && attemptCount-- > 0)
            {
                try
                {
                    await _connection.ConnectAsync();

                    break;
                }
                catch (SocketException ex)
                {
                    ExceptionsUtil.HandleSocketException(ex, _connection.Uri.ToString(), Trace.Error);
                }
                catch (Exception ex) when(attemptCount > 0)
                {
                    Trace.Info($"Catch exception during connect. {attemptCount} attemp left.");
                    Trace.Error(ex);
                }

                await Task.Delay(100);
            }

            _releaseHttpClient = _connection.GetClient <ReleaseHttpClient>();
        }
コード例 #4
0
        /// <inheritdoc />
        public async Task <bool> InitializeAsync(IAgentLogPluginContext context)
        {
            try
            {
                _logger    = _logger ?? new TraceLogger(context);
                _telemetry = _telemetry ?? new TelemetryDataCollector(new ClientFactory(context.VssConnection), _logger);

                await PopulatePipelineConfig(context);

                if (DisablePlugin(context))
                {
                    _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true);
                    await _telemetry.PublishCumulativeTelemetryAsync();

                    return(false); // disable the plugin
                }

                _testFilePublisher = _testFilePublisher ??
                                     new TestFilePublisher(context.VssConnection, PipelineConfig, new TestFileTraceListener(context), _logger, _telemetry);
                await _testFilePublisher.InitializeAsync();

                _telemetry.AddOrUpdate(TelemetryConstants.PluginInitialized, true);
            }
            catch (SocketException ex)
            {
                ExceptionsUtil.HandleSocketException(ex, context.VssConnection.Uri.ToString(), _logger.Warning);

                if (_telemetry != null)
                {
                    _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true);
                    _telemetry.AddOrUpdate(TelemetryConstants.InitializeFailed, ex);
                    await _telemetry.PublishCumulativeTelemetryAsync();
                }
                return(false);
            }
            catch (Exception ex)
            {
                context.Trace(ex.ToString());
                _logger?.Warning($"Unable to initialize {FriendlyName}.");
                if (_telemetry != null)
                {
                    _telemetry.AddOrUpdate(TelemetryConstants.PluginDisabled, true);
                    _telemetry.AddOrUpdate(TelemetryConstants.InitializeFailed, ex);
                    await _telemetry.PublishCumulativeTelemetryAsync();
                }
                return(false);
            }

            return(true);
        }
コード例 #5
0
        private Uri GetTenantAuthorityUrl(IHostContext context, string serverUrl)
        {
            Tracing trace = context.GetTrace(nameof(AadDeviceCodeAccessToken));

            using (var handler = context.CreateHttpClientHandler())
                using (var client = new HttpClient(handler))
                {
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
                    client.DefaultRequestHeaders.UserAgent.Clear();
                    client.DefaultRequestHeaders.UserAgent.AddRange(VssClientHttpRequestSettings.Default.UserAgent);
                    using (var requestMessage = new HttpRequestMessage(HttpMethod.Head, $"{serverUrl.Trim('/')}/_apis/connectiondata"))
                    {
                        HttpResponseMessage response;
                        try
                        {
                            response = client.SendAsync(requestMessage).GetAwaiter().GetResult();
                        }
                        catch (SocketException e)
                        {
                            ExceptionsUtil.HandleSocketException(e, serverUrl, trace.Error);
                            throw;
                        }

                        // Get the tenant from the Login URL, MSA backed accounts will not return `Bearer` www-authenticate header.
                        var bearerResult = response.Headers.WwwAuthenticate.Where(p => p.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                        if (bearerResult != null && bearerResult.Parameter.StartsWith("authorization_uri=", StringComparison.OrdinalIgnoreCase))
                        {
                            var authorizationUri = bearerResult.Parameter.Substring("authorization_uri=".Length);
                            if (Uri.TryCreate(authorizationUri, UriKind.Absolute, out Uri aadTenantUrl))
                            {
                                return(aadTenantUrl);
                            }
                        }

                        return(null);
                    }
                }
        }
コード例 #6
0
        private async Task DownloadArtifactsAndCommitsAsync(IExecutionContext executionContext, object data)
        {
            Trace.Entering();

            try
            {
                await DownloadArtifacts(executionContext, ReleaseArtifacts, ArtifactsWorkingFolder);
                await DownloadCommits(executionContext, TeamProjectId, ReleaseArtifacts);
            }
            catch (SocketException ex)
            {
                LogDownloadFailureTelemetry(executionContext, ex);

                ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(executionContext).Uri.ToString(), Trace.Error);
                throw;
            }
            catch (Exception ex)
            {
                LogDownloadFailureTelemetry(executionContext, ex);
                throw;
            }
        }
コード例 #7
0
        private async Task <string> GetAzureSubscriptionIdAsync()
        {
            // We will use the Azure Instance Metadata Service in order to fetch metadata ( in this case Subscription Id used to provision the VM) if the VM is an Azure VM
            // More on Instance Metadata Service can be found here: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service
            string       azureSubscriptionId = string.Empty;
            const string imdsUri             = "http://169.254.169.254/metadata/instance/compute/subscriptionId?api-version=2017-08-01&format=text";

            using (var handler = HostContext.CreateHttpClientHandler())
                using (var httpClient = new HttpClient(handler))
                {
                    httpClient.DefaultRequestHeaders.Add("Metadata", "True");
                    httpClient.Timeout = TimeSpan.FromSeconds(5);
                    try
                    {
                        azureSubscriptionId = await httpClient.GetStringAsync(imdsUri);

                        if (!Guid.TryParse(azureSubscriptionId, out Guid result))
                        {
                            azureSubscriptionId = string.Empty;
                        }
                    }
                    catch (SocketException ex)
                    {
                        azureSubscriptionId = string.Empty;
                        ExceptionsUtil.HandleSocketException(ex, imdsUri, Trace.Info);
                    }
                    catch (Exception ex)
                    {
                        // An exception will be thrown if the Agent Machine is a non-Azure VM.
                        azureSubscriptionId = string.Empty;
                        Trace.Info($"GetAzureSubscriptionId ex: {ex.Message}");
                    }
                }

            return(azureSubscriptionId);
        }
コード例 #8
0
        public async Task ConnectAsync(VssConnection jobConnection)
        {
            ArgUtil.NotNull(jobConnection, nameof(jobConnection));
            _connection = jobConnection;

            try
            {
                await _connection.ConnectAsync();
            }
            catch (SocketException ex)
            {
                ExceptionsUtil.HandleSocketException(ex, _connection.Uri.ToString(), Trace.Error);
                throw;
            }
            catch (Exception ex)
            {
                Trace.Info($"Unable to connect to {_connection.Uri}.");
                Trace.Error(ex);
                throw;
            }

            _locationClient = _connection.GetClient <LocationHttpClient>();
            _hasConnection  = true;
        }
コード例 #9
0
        public static int Main(string[] args)
        {
            if (PlatformUtil.UseLegacyHttpHandler)
            {
                AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
            }

            Console.CancelKeyPress += Console_CancelKeyPress;

            // Set encoding to UTF8, process invoker will use UTF8 write to STDIN
            Console.InputEncoding  = Encoding.UTF8;
            Console.OutputEncoding = Encoding.UTF8;
            try
            {
                ArgUtil.NotNull(args, nameof(args));
                ArgUtil.Equal(2, args.Length, nameof(args.Length));

                string pluginType = args[0];
                if (string.Equals("task", pluginType, StringComparison.OrdinalIgnoreCase))
                {
                    string assemblyQualifiedName = args[1];
                    ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName));

                    string serializedContext = Console.ReadLine();
                    ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext));

                    AgentTaskPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentTaskPluginExecutionContext>(serializedContext);
                    ArgUtil.NotNull(executionContext, nameof(executionContext));

                    VariableValue culture;
                    ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables));
                    if (executionContext.Variables.TryGetValue("system.culture", out culture) &&
                        !string.IsNullOrEmpty(culture?.Value))
                    {
                        CultureInfo.DefaultThreadCurrentCulture   = new CultureInfo(culture.Value);
                        CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(culture.Value);
                    }

                    AssemblyLoadContext.Default.Resolving += ResolveAssembly;
                    try
                    {
                        Type type       = Type.GetType(assemblyQualifiedName, throwOnError: true);
                        var  taskPlugin = Activator.CreateInstance(type) as IAgentTaskPlugin;
                        ArgUtil.NotNull(taskPlugin, nameof(taskPlugin));
                        taskPlugin.RunAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult();
                    }
                    catch (SocketException ex)
                    {
                        ExceptionsUtil.HandleSocketException(ex, executionContext.VssConnection.Uri.ToString(), executionContext.Error);
                    }
                    catch (AggregateException ex)
                    {
                        ExceptionsUtil.HandleAggregateException((AggregateException)ex, executionContext.Error);
                    }
                    catch (Exception ex)
                    {
                        executionContext.Error(ex.Message);
                        executionContext.Debug(ex.StackTrace);
                    }
                    finally
                    {
                        AssemblyLoadContext.Default.Resolving -= ResolveAssembly;
                    }

                    return(0);
                }
                else if (string.Equals("command", pluginType, StringComparison.OrdinalIgnoreCase))
                {
                    string assemblyQualifiedName = args[1];
                    ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName));

                    string serializedContext = Console.ReadLine();
                    ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext));

                    AgentCommandPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentCommandPluginExecutionContext>(serializedContext);
                    ArgUtil.NotNull(executionContext, nameof(executionContext));

                    AssemblyLoadContext.Default.Resolving += ResolveAssembly;
                    try
                    {
                        Type type          = Type.GetType(assemblyQualifiedName, throwOnError: true);
                        var  commandPlugin = Activator.CreateInstance(type) as IAgentCommandPlugin;
                        ArgUtil.NotNull(commandPlugin, nameof(commandPlugin));
                        commandPlugin.ProcessCommandAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult();
                    }
                    catch (Exception ex)
                    {
                        // any exception throw from plugin will fail the command.
                        executionContext.Error(ex.ToString());
                    }
                    finally
                    {
                        AssemblyLoadContext.Default.Resolving -= ResolveAssembly;
                    }

                    return(0);
                }
                else if (string.Equals("log", pluginType, StringComparison.OrdinalIgnoreCase))
                {
                    // read commandline arg to get the instance id
                    var instanceId = args[1];
                    ArgUtil.NotNullOrEmpty(instanceId, nameof(instanceId));

                    // read STDIN, the first line will be the HostContext for the log plugin host
                    string serializedContext = Console.ReadLine();
                    ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext));
                    AgentLogPluginHostContext hostContext = StringUtil.ConvertFromJson <AgentLogPluginHostContext>(serializedContext);
                    ArgUtil.NotNull(hostContext, nameof(hostContext));

                    // create plugin object base on plugin assembly names from the HostContext
                    List <IAgentLogPlugin> logPlugins = new List <IAgentLogPlugin>();
                    AssemblyLoadContext.Default.Resolving += ResolveAssembly;
                    try
                    {
                        foreach (var pluginAssembly in hostContext.PluginAssemblies)
                        {
                            try
                            {
                                Type type      = Type.GetType(pluginAssembly, throwOnError: true);
                                var  logPlugin = Activator.CreateInstance(type) as IAgentLogPlugin;
                                ArgUtil.NotNull(logPlugin, nameof(logPlugin));
                                logPlugins.Add(logPlugin);
                            }
                            catch (Exception ex)
                            {
                                // any exception throw from plugin will get trace and ignore, error from plugins will not fail the job.
                                Console.WriteLine($"Unable to load plugin '{pluginAssembly}': {ex}");
                            }
                        }
                    }
                    finally
                    {
                        AssemblyLoadContext.Default.Resolving -= ResolveAssembly;
                    }

                    // start the log plugin host
                    var  logPluginHost = new AgentLogPluginHost(hostContext, logPlugins);
                    Task hostTask      = logPluginHost.Run();
                    while (true)
                    {
                        var consoleInput = Console.ReadLine();
                        if (string.Equals(consoleInput, $"##vso[logplugin.finish]{instanceId}", StringComparison.OrdinalIgnoreCase))
                        {
                            // singal all plugins, the job has finished.
                            // plugin need to start their finalize process.
                            logPluginHost.Finish();
                            break;
                        }
                        else
                        {
                            // the format is TimelineRecordId(GUID):Output(String)
                            logPluginHost.EnqueueOutput(consoleInput);
                        }
                    }

                    // wait for the host to finish.
                    hostTask.GetAwaiter().GetResult();

                    return(0);
                }
                else
                {
                    throw new ArgumentOutOfRangeException(pluginType);
                }
            }
            catch (Exception ex)
            {
                // infrastructure failure.
                Console.Error.WriteLine(ex.ToString());
                return(1);
            }
            finally
            {
                Console.CancelKeyPress -= Console_CancelKeyPress;
            }
        }
コード例 #10
0
        public async Task ConfigureAsync(CommandSettings command)
        {
            ArgUtil.NotNull(command, nameof(command));

            if (PlatformUtil.RunningOnWindows)
            {
                CheckAgentRootDirectorySecure();
            }

            Trace.Info(nameof(ConfigureAsync));
            if (IsConfigured())
            {
                throw new InvalidOperationException(StringUtil.Loc("AlreadyConfiguredError"));
            }

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

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

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

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

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

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

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

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

            AgentSettings agentSettings = new AgentSettings();

            // TEE EULA
            agentSettings.AcceptTeeEula = false;
            switch (PlatformUtil.HostOS)
            {
            case PlatformUtil.OS.OSX:
            case PlatformUtil.OS.Linux:
                // Write the section header.
                WriteSection(StringUtil.Loc("EulasSectionHeader"));

                // Verify the EULA exists on disk in the expected location.
                string eulaFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), "license.html");
                ArgUtil.File(eulaFile, nameof(eulaFile));

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

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

            case PlatformUtil.OS.Windows:
                // Warn and continue if .NET 4.6 is not installed.
                if (!NetFrameworkUtil.Test(new Version(4, 6), Trace))
                {
                    WriteSection(StringUtil.Loc("PrerequisitesSectionHeader"));     // Section header.
                    _term.WriteLine(StringUtil.Loc("MinimumNetFrameworkTfvc"));     // Warning.
                }

                break;

            default:
                throw new NotSupportedException();
            }

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

            if (command.GetDeploymentOrMachineGroup())
            {
                agentType = Constants.Agent.AgentConfigurationProvider.DeploymentAgentConfiguration;
            }
            else if (command.GetDeploymentPool())
            {
                agentType = Constants.Agent.AgentConfigurationProvider.SharedDeploymentAgentConfiguration;
            }
            else if (command.GetEnvironmentVMResource())
            {
                agentType = Constants.Agent.AgentConfigurationProvider.EnvironmentVMResourceConfiguration;
            }
            else
            {
                agentType = Constants.Agent.AgentConfigurationProvider.BuildReleasesAgentConfiguration;
            }

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

            ArgUtil.NotNull(agentProvider, agentType);

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

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

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

                    if (!_serverUtil.TryGetDeploymentType(out isHostedServer))
                    {
                        Trace.Warning(@"Deployment type determination has been failed;
assume it is OnPremises and the deployment type determination was not implemented for this server version.");
                    }

                    // 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 (SocketException e)
                {
                    ExceptionsUtil.HandleSocketException(e, agentSettings.ServerUrl, _term.WriteError);
                }
                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
            WriteSection(StringUtil.Loc("RegisterAgentSectionHeader"));

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

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

            TaskAgent agent;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                default:
                    throw new NotSupportedException();
                }
            }

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

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

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

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

            agentSettings.MonitorSocketAddress = command.GetMonitorSocketAddress();

            agentSettings.NotificationSocketAddress = command.GetNotificationSocketAddress();

            agentSettings.DisableLogUploads = command.GetDisableLogUploads();

            agentSettings.AlwaysExtractTask = command.GetAlwaysExtractTask();

            _store.SaveSettings(agentSettings);

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

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

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

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

            if (PlatformUtil.RunningOnWindows && command.GetGitUseSChannel())
            {
                saveRuntimeOptions = true;
                runtimeOptions.GitUseSecureChannel = true;
            }
            if (saveRuntimeOptions)
            {
                Trace.Info("Save agent runtime options to disk.");
                _store.SaveAgentRuntimeOptions(runtimeOptions);
            }

            if (PlatformUtil.RunningOnWindows)
            {
                // config windows service
                bool runAsService = command.GetRunAsService();
                if (runAsService)
                {
                    Trace.Info("Configuring to run the agent as service");
                    var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>();
                    agentSettings.EnableServiceSidTypeUnrestricted = command.GetEnableServiceSidTypeUnrestricted();

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

                    //Important: The machine may restart if the autologon user is not same as the current user
                    //if you are adding code after this, keep that in mind
                }
            }
            else if (PlatformUtil.RunningOnLinux)
            {
                // generate service config script for Linux
                var serviceControlManager = HostContext.GetService <ILinuxServiceControlManager>();
                serviceControlManager.GenerateScripts(agentSettings);
            }
            else if (PlatformUtil.RunningOnMacOS)
            {
                // generate service config script for macOS
                var serviceControlManager = HostContext.GetService <IMacOSServiceControlManager>();
                serviceControlManager.GenerateScripts(agentSettings);
            }
        }
コード例 #11
0
        public async Task UnconfigureAsync(CommandSettings command)
        {
            ArgUtil.NotNull(command, nameof(command));
            string currentAction = string.Empty;

            try
            {
                //stop, uninstall service and remove service config file
                if (_store.IsServiceConfigured())
                {
                    currentAction = StringUtil.Loc("UninstallingService");
                    _term.WriteLine(currentAction);
                    if (PlatformUtil.RunningOnWindows)
                    {
                        var serviceControlManager = HostContext.GetService <IWindowsServiceControlManager>();
                        serviceControlManager.UnconfigureService();
                        _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                    }
                    else if (PlatformUtil.RunningOnLinux)
                    {
                        // unconfig systemd service first
                        throw new InvalidOperationException(StringUtil.Loc("UnconfigureServiceDService"));
                    }
                    else if (PlatformUtil.RunningOnMacOS)
                    {
                        // unconfig macOS service first
                        throw new InvalidOperationException(StringUtil.Loc("UnconfigureOSXService"));
                    }
                }
                else
                {
                    if (PlatformUtil.RunningOnWindows)
                    {
                        //running as process, unconfigure autologon if it was configured
                        if (_store.IsAutoLogonConfigured())
                        {
                            currentAction = StringUtil.Loc("UnconfigAutologon");
                            _term.WriteLine(currentAction);
                            var autoLogonConfigManager = HostContext.GetService <IAutoLogonManager>();
                            autoLogonConfigManager.Unconfigure();
                            _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                        }
                        else
                        {
                            Trace.Info("AutoLogon was not configured on the agent.");
                        }
                    }
                }

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

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

                    bool isEnvironmentVMResource = false;
                    bool isDeploymentGroup       = (settings.MachineGroupId > 0) || (settings.DeploymentGroupId > 0);
                    if (!isDeploymentGroup)
                    {
                        isEnvironmentVMResource = settings.EnvironmentId > 0;
                    }

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

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

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

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

                    if (!_serverUtil.TryGetDeploymentType(out isHostedServer))
                    {
                        Trace.Warning(@"Deployment type determination has been failed;
assume it is OnPremises and the deployment type determination was not implemented for this server version.");
                    }

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

                    TaskAgent agent = await agentProvider.GetAgentAsync(settings);

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

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

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

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

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

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

                    _store.DeleteSettings();
                    _term.WriteLine(StringUtil.Loc("Success") + currentAction);
                }
                else
                {
                    _term.WriteLine(StringUtil.Loc("Skipping") + currentAction);
                }
            }
            catch (SocketException ex)
            {
                ExceptionsUtil.HandleSocketException(ex, _store.GetSettings().ServerUrl, _term.WriteLine);
                throw;
            }
            catch (Exception)
            {
                _term.WriteLine(StringUtil.Loc("Failed") + currentAction);
                throw;
            }
        }
コード例 #12
0
        /// <summary>
        /// _work
        ///     \_update
        ///            \bin
        ///            \externals
        ///            \run.sh
        ///            \run.cmd
        ///            \package.zip //temp download .zip/.tar.gz
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private async Task DownloadLatestAgent(CancellationToken token)
        {
            string latestAgentDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory);

            IOUtil.DeleteDirectory(latestAgentDirectory, token);
            Directory.CreateDirectory(latestAgentDirectory);

            int    agentSuffix         = 1;
            string archiveFile         = null;
            bool   downloadSucceeded   = false;
            bool   validationSucceeded = false;

            try
            {
                // Download the agent, using multiple attempts in order to be resilient against any networking/CDN issues
                for (int attempt = 1; attempt <= Constants.AgentDownloadRetryMaxAttempts && !validationSucceeded; attempt++)
                {
                    // Generate an available package name, and do our best effort to clean up stale local zip files
                    while (true)
                    {
                        if (_targetPackage.Platform.StartsWith("win"))
                        {
                            archiveFile = Path.Combine(latestAgentDirectory, $"agent{agentSuffix}.zip");
                        }
                        else
                        {
                            archiveFile = Path.Combine(latestAgentDirectory, $"agent{agentSuffix}.tar.gz");
                        }

                        // The package name is generated, check if there is already a file with the same name and path
                        if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile))
                        {
                            Trace.Verbose("Deleting latest agent package zip '{0}'", archiveFile);
                            try
                            {
                                // Such a file already exists, so try deleting it
                                IOUtil.DeleteFile(archiveFile);

                                // The file was successfully deleted, so we can use the generated package name
                                break;
                            }
                            catch (Exception ex)
                            {
                                // Couldn't delete the file for whatever reason, so generate another package name
                                Trace.Warning("Failed to delete agent package zip '{0}'. Exception: {1}", archiveFile, ex);
                                agentSuffix++;
                            }
                        }
                        else
                        {
                            // There is no a file with the same name and path, so we can use the generated package name
                            break;
                        }
                    }

                    // Allow a 15-minute package download timeout, which is good enough to update the agent from a 1 Mbit/s ADSL connection.
                    var timeoutSeconds = AgentKnobs.AgentDownloadTimeout.GetValue(_knobContext).AsInt();

                    Trace.Info($"Attempt {attempt}: save latest agent into {archiveFile}.");

                    using (var downloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
                        using (var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(downloadTimeout.Token, token))
                        {
                            try
                            {
                                Trace.Info($"Download agent: begin download");

                                //open zip stream in async mode
                                using (var handler = HostContext.CreateHttpClientHandler())
                                    using (var httpClient = new HttpClient(handler))
                                        using (var fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
                                            using (var result = await httpClient.GetStreamAsync(_targetPackage.DownloadUrl))
                                            {
                                                //81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
                                                await result.CopyToAsync(fs, 81920, downloadCts.Token);

                                                await fs.FlushAsync(downloadCts.Token);
                                            }

                                Trace.Info($"Download agent: finished download");

                                downloadSucceeded   = true;
                                validationSucceeded = HashValidation(archiveFile);
                            }
                            catch (OperationCanceledException) when(token.IsCancellationRequested)
                            {
                                Trace.Info($"Agent download has been canceled.");
                                throw;
                            }
                            catch (SocketException ex)
                            {
                                ExceptionsUtil.HandleSocketException(ex, _targetPackage.DownloadUrl, Trace.Warning);
                            }
                            catch (Exception ex)
                            {
                                if (downloadCts.Token.IsCancellationRequested)
                                {
                                    Trace.Warning($"Agent download has timed out after {timeoutSeconds} seconds");
                                }

                                Trace.Warning($"Failed to get package '{archiveFile}' from '{_targetPackage.DownloadUrl}'. Exception {ex}");
                            }
                        }
                }

                if (!downloadSucceeded)
                {
                    throw new TaskCanceledException($"Agent package '{archiveFile}' failed after {Constants.AgentDownloadRetryMaxAttempts} download attempts.");
                }

                if (!validationSucceeded)
                {
                    throw new TaskCanceledException(@"Agent package checksum validation failed.
There are possible reasons why this happened:
  1) The agent package was compromised.
  2) The agent package was not fully downloaded or was corrupted during the download process.
You can skip checksum validation for the agent package by setting the environment variable DISABLE_HASH_VALIDATION=true");
                }

                // If we got this far, we know that we've successfully downloadeded the agent package
                if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
                {
                    ZipFile.ExtractToDirectory(archiveFile, latestAgentDirectory);
                }
                else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
                {
                    string tar = WhichUtil.Which("tar", trace: Trace);

                    if (string.IsNullOrEmpty(tar))
                    {
                        throw new NotSupportedException($"tar -xzf");
                    }

                    // tar -xzf
                    using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                    {
                        processInvoker.OutputDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) =>
                        {
                            if (!string.IsNullOrEmpty(args.Data))
                            {
                                Trace.Info(args.Data);
                            }
                        });

                        processInvoker.ErrorDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) =>
                        {
                            if (!string.IsNullOrEmpty(args.Data))
                            {
                                Trace.Error(args.Data);
                            }
                        });

                        int exitCode = await processInvoker.ExecuteAsync(latestAgentDirectory, tar, $"-xzf \"{archiveFile}\"", null, token);

                        if (exitCode != 0)
                        {
                            throw new NotSupportedException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. return code: {exitCode}.");
                        }
                    }
                }
                else
                {
                    throw new NotSupportedException($"{archiveFile}");
                }

                Trace.Info($"Finished getting latest agent package at: {latestAgentDirectory}.");
            }
            finally
            {
                try
                {
                    // delete .zip file
                    if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile))
                    {
                        Trace.Verbose("Deleting latest agent package zip: {0}", archiveFile);
                        IOUtil.DeleteFile(archiveFile);
                    }
                }
                catch (Exception ex)
                {
                    //it is not critical if we fail to delete the .zip file
                    Trace.Warning("Failed to delete agent package zip '{0}'. Exception: {1}", archiveFile, ex);
                }
            }

            if (!String.IsNullOrEmpty(AgentKnobs.DisableAuthenticodeValidation.GetValue(HostContext).AsString()))
            {
                Trace.Warning("Authenticode validation skipped for downloaded agent package since it is disabled currently by agent settings.");
            }
            else
            {
                if (PlatformUtil.RunningOnWindows)
                {
                    var isValid = this.VerifyAgentAuthenticode(latestAgentDirectory);
                    if (!isValid)
                    {
                        throw new Exception("Authenticode validation of agent assemblies failed.");
                    }
                    else
                    {
                        Trace.Info("Authenticode validation of agent assemblies passed successfully.");
                    }
                }
                else
                {
                    Trace.Info("Authenticode validation skipped since it's not supported on non-Windows platforms at the moment.");
                }
            }

            // copy latest agent into agent root folder
            // copy bin from _work/_update -> bin.version under root
            string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}");

            Directory.CreateDirectory(binVersionDir);
            Trace.Info($"Copy {Path.Combine(latestAgentDirectory, Constants.Path.BinDirectory)} to {binVersionDir}.");
            IOUtil.CopyDirectory(Path.Combine(latestAgentDirectory, Constants.Path.BinDirectory), binVersionDir, token);

            // copy externals from _work/_update -> externals.version under root
            string externalsVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.ExternalsDirectory}.{_targetPackage.Version}");

            Directory.CreateDirectory(externalsVersionDir);
            Trace.Info($"Copy {Path.Combine(latestAgentDirectory, Constants.Path.ExternalsDirectory)} to {externalsVersionDir}.");
            IOUtil.CopyDirectory(Path.Combine(latestAgentDirectory, Constants.Path.ExternalsDirectory), externalsVersionDir, token);

            // copy and replace all .sh/.cmd files
            Trace.Info($"Copy any remaining .sh/.cmd files into agent root.");
            foreach (FileInfo file in new DirectoryInfo(latestAgentDirectory).GetFiles() ?? new FileInfo[0])
            {
                // Copy and replace the file.
                file.CopyTo(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name), true);
            }

            // for windows service back compat with old windows agent, we need make sure the servicehost.exe is still the old name
            // if the current bin folder has VsoAgentService.exe, then the new agent bin folder needs VsoAgentService.exe as well
            if (PlatformUtil.RunningOnWindows)
            {
                if (File.Exists(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "VsoAgentService.exe")))
                {
                    Trace.Info($"Make a copy of AgentService.exe, name it VsoAgentService.exe");
                    File.Copy(Path.Combine(binVersionDir, "AgentService.exe"), Path.Combine(binVersionDir, "VsoAgentService.exe"), true);
                    File.Copy(Path.Combine(binVersionDir, "AgentService.exe.config"), Path.Combine(binVersionDir, "VsoAgentService.exe.config"), true);

                    Trace.Info($"Make a copy of Agent.Listener.exe, name it VsoAgent.exe");
                    File.Copy(Path.Combine(binVersionDir, "Agent.Listener.exe"), Path.Combine(binVersionDir, "VsoAgent.exe"), true);
                    File.Copy(Path.Combine(binVersionDir, "Agent.Listener.dll"), Path.Combine(binVersionDir, "VsoAgent.dll"), true);

                    // in case of we remove all pdb file from agent package.
                    if (File.Exists(Path.Combine(binVersionDir, "AgentService.pdb")))
                    {
                        File.Copy(Path.Combine(binVersionDir, "AgentService.pdb"), Path.Combine(binVersionDir, "VsoAgentService.pdb"), true);
                    }

                    if (File.Exists(Path.Combine(binVersionDir, "Agent.Listener.pdb")))
                    {
                        File.Copy(Path.Combine(binVersionDir, "Agent.Listener.pdb"), Path.Combine(binVersionDir, "VsoAgent.pdb"), true);
                    }
                }
            }
        }
コード例 #13
0
        private async Task <UploadResult> BlobUploadAsync(IAsyncCommandContext context, IReadOnlyList <string> files, int concurrentUploads, CancellationToken token)
        {
            // return files that fail to upload and total artifact size
            var uploadResult = new UploadResult();

            // nothing needs to upload
            if (files.Count == 0)
            {
                return(uploadResult);
            }

            DedupStoreClient            dedupClient     = null;
            BlobStoreClientTelemetryTfs clientTelemetry = null;

            try
            {
                var verbose        = String.Equals(context.GetVariableValueOrDefault("system.debug"), "true", StringComparison.InvariantCultureIgnoreCase);
                int maxParallelism = context.GetHostContext().GetService <IConfigurationStore>().GetSettings().MaxDedupParallelism;
                (dedupClient, clientTelemetry) = await DedupManifestArtifactClientFactory.Instance
                                                 .CreateDedupClientAsync(verbose, (str) => context.Output(str), this._connection, maxParallelism, token);

                // Upload to blobstore
                var results = await BlobStoreUtils.UploadBatchToBlobstore(verbose, files, (level, uri, type) =>
                                                                          new BuildArtifactActionRecord(level, uri, type, nameof(BlobUploadAsync), context), (str) => context.Output(str), dedupClient, clientTelemetry, token, enableReporting : true);

                // Associate with TFS
                context.Output(StringUtil.Loc("AssociateFiles"));
                var queue = new ConcurrentQueue <BlobFileInfo>();
                foreach (var file in results.fileDedupIds)
                {
                    queue.Enqueue(file);
                }

                // Start associate monitor
                var uploadFinished   = new TaskCompletionSource <int>();
                var associateMonitor = AssociateReportingAsync(context, files.Count(), uploadFinished, token);

                // Start parallel associate tasks.
                var parallelAssociateTasks = new List <Task <UploadResult> >();
                for (int uploader = 0; uploader < concurrentUploads; uploader++)
                {
                    parallelAssociateTasks.Add(AssociateAsync(context, queue, token));
                }

                // Wait for parallel associate tasks to finish.
                await Task.WhenAll(parallelAssociateTasks);

                foreach (var associateTask in parallelAssociateTasks)
                {
                    // record all failed files.
                    uploadResult.AddUploadResult(await associateTask);
                }

                // Stop monitor task
                uploadFinished.SetResult(0);
                await associateMonitor;

                // report telemetry
                if (!Guid.TryParse(context.GetVariableValueOrDefault(WellKnownDistributedTaskVariables.PlanId), out var planId))
                {
                    planId = Guid.Empty;
                }
                if (!Guid.TryParse(context.GetVariableValueOrDefault(WellKnownDistributedTaskVariables.JobId), out var jobId))
                {
                    jobId = Guid.Empty;
                }
                await clientTelemetry.CommitTelemetryUpload(planId, jobId);
            }
            catch (SocketException e)
            {
                ExceptionsUtil.HandleSocketException(e, this._connection.Uri.ToString(), context.Warn);

                throw;
            }
            catch
            {
                var blobStoreHost  = dedupClient.Client.BaseAddress.Host;
                var allowListLink  = BlobStoreWarningInfoProvider.GetAllowListLinkForCurrentPlatform();
                var warningMessage = StringUtil.Loc("BlobStoreUploadWarning", blobStoreHost, allowListLink);

                context.Warn(warningMessage);

                throw;
            }

            return(uploadResult);
        }
コード例 #14
0
        public IArtifactDetails GetArtifactDetails(
            IExecutionContext context,
            AgentArtifactDefinition agentArtifactDefinition)
        {
            ArgUtil.NotNull(context, nameof(context));
            ArgUtil.NotNull(agentArtifactDefinition, nameof(agentArtifactDefinition));

            var artifactDetails =
                JsonConvert.DeserializeObject <Dictionary <string, string> >(agentArtifactDefinition.Details);

            string connectionName;
            string repositoryName = string.Empty;
            string branch         = string.Empty;

            if (artifactDetails.TryGetValue(ArtifactDefinitionConstants.ConnectionName, out connectionName) &&
                artifactDetails.TryGetValue(ArtifactDefinitionConstants.RepositoryId, out repositoryName) &&
                artifactDetails.TryGetValue(ArtifactDefinitionConstants.BranchId, out branch))
            {
                string checkoutNestedSubmodules;
                string checkoutSubmodules;
                string gitLfsSupport;
                string fetchDepth;

                artifactDetails.TryGetValue("checkoutNestedSubmodules", out checkoutNestedSubmodules);
                artifactDetails.TryGetValue("checkoutSubmodules", out checkoutSubmodules);
                artifactDetails.TryGetValue("gitLfsSupport", out gitLfsSupport);
                artifactDetails.TryGetValue("fetchDepth", out fetchDepth);

                ServiceEndpoint gitHubEndpoint = context.Endpoints.FirstOrDefault((e => string.Equals(e.Name, connectionName, StringComparison.OrdinalIgnoreCase)));
                if (gitHubEndpoint == null)
                {
                    throw new InvalidOperationException(StringUtil.Loc("RMGitHubEndpointNotFound", agentArtifactDefinition.Name));
                }

                string accessToken = gitHubEndpoint.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];

                GitHubRepository repository = null;
                try
                {
                    repository = HostContext.GetService <IGitHubHttpClient>().GetUserRepo(accessToken, repositoryName);
                }
                catch (SocketException e)
                {
                    ExceptionsUtil.HandleSocketException(e, "https://api.github.com", Trace.Info);
                    throw;
                }

                Trace.Info($"Found github repository url {repository.Clone_url}");
                return(new GitHubArtifactDetails
                {
                    RelativePath = Path.DirectorySeparatorChar.ToString(),
                    ConnectionName = connectionName,
                    CloneUrl = new Uri(repository.Clone_url),
                    Branch = branch,
                    CheckoutSubmodules = checkoutSubmodules,
                    CheckoutNestedSubmodules = checkoutNestedSubmodules,
                    GitLfsSupport = gitLfsSupport,
                    FetchDepth = fetchDepth
                });
            }
            else
            {
                throw new InvalidOperationException(StringUtil.Loc("RMArtifactDetailsIncomplete"));
            }
        }
コード例 #15
0
        private async Task DownloadFileContainerAsync(IEnumerable <FileContainerItem> items, ArtifactDownloadParameters downloadParameters, BuildArtifact artifact, string rootPath, AgentTaskPluginExecutionContext context, CancellationToken cancellationToken, bool isSingleArtifactDownload = true)
        {
            var containerIdAndRoot = ParseContainerId(artifact.Resource.Data);
            var projectId          = downloadParameters.ProjectId;

            tracer.Info($"Start downloading FCS artifact- {artifact.Name}");

            if (!isSingleArtifactDownload && items.Any())
            {
                Directory.CreateDirectory(rootPath);
            }

            var folderItems = items.Where(i => i.ItemType == ContainerItemType.Folder);

            Parallel.ForEach(folderItems, (folder) =>
            {
                var targetPath = ResolveTargetPath(rootPath, folder, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath);
                Directory.CreateDirectory(targetPath);
            });

            var fileItems = items.Where(i => i.ItemType == ContainerItemType.File);

            // Only initialize these clients if we know we need to download from Blobstore
            // If a client cannot connect to Blobstore, we shouldn't stop them from downloading from FCS
            var downloadFromBlob = !AgentKnobs.DisableBuildArtifactsToBlob.GetValue(context).AsBoolean();
            DedupStoreClient            dedupClient     = null;
            BlobStoreClientTelemetryTfs clientTelemetry = null;

            if (downloadFromBlob && fileItems.Any(x => x.BlobMetadata != null))
            {
                try
                {
                    (dedupClient, clientTelemetry) = await DedupManifestArtifactClientFactory.Instance.CreateDedupClientAsync(
                        false, (str) => this.tracer.Info(str), this.connection, DedupManifestArtifactClientFactory.Instance.GetDedupStoreClientMaxParallelism(context), cancellationToken);
                }
                catch (SocketException e)
                {
                    ExceptionsUtil.HandleSocketException(e, connection.Uri.ToString(), context.Warning);
                }
                catch
                {
                    var blobStoreHost  = dedupClient.Client.BaseAddress.Host;
                    var allowListLink  = BlobStoreWarningInfoProvider.GetAllowListLinkForCurrentPlatform();
                    var warningMessage = StringUtil.Loc("BlobStoreDownloadWarning", blobStoreHost, allowListLink);

                    // Fall back to streaming through TFS if we cannot reach blobstore
                    downloadFromBlob = false;
                    tracer.Warn(warningMessage);
                }
            }

            var downloadBlock = NonSwallowingActionBlock.Create <FileContainerItem>(
                async item =>
            {
                var targetPath = ResolveTargetPath(rootPath, item, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath);
                var directory  = Path.GetDirectoryName(targetPath);
                Directory.CreateDirectory(directory);
                await AsyncHttpRetryHelper.InvokeVoidAsync(
                    async() =>
                {
                    tracer.Info($"Downloading: {targetPath}");
                    if (item.BlobMetadata != null && downloadFromBlob)
                    {
                        await this.DownloadFileFromBlobAsync(context, containerIdAndRoot, targetPath, projectId, item, dedupClient, clientTelemetry, cancellationToken);
                    }
                    else
                    {
                        using (var sourceStream = await this.DownloadFileAsync(containerIdAndRoot, projectId, containerClient, item, cancellationToken))
                            using (var targetStream = new FileStream(targetPath, FileMode.Create))
                            {
                                await sourceStream.CopyToAsync(targetStream);
                            }
                    }
                },
                    maxRetries: downloadParameters.RetryDownloadCount,
                    cancellationToken: cancellationToken,
                    tracer: tracer,
                    continueOnCapturedContext: false,
                    canRetryDelegate: exception => exception is IOException,
                    context: null
                    );
            },
                new ExecutionDataflowBlockOptions()
            {
                BoundedCapacity        = 5000,
                MaxDegreeOfParallelism = downloadParameters.ParallelizationLimit,
                CancellationToken      = cancellationToken,
            });

            await downloadBlock.SendAllAndCompleteSingleBlockNetworkAsync(fileItems, cancellationToken);

            // Send results to CustomerIntelligence
            if (clientTelemetry != null)
            {
                var planId = new Guid(context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.PlanId)?.Value ?? Guid.Empty.ToString());
                var jobId  = new Guid(context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.JobId)?.Value ?? Guid.Empty.ToString());
                context.PublishTelemetry(area: PipelineArtifactConstants.AzurePipelinesAgent, feature: PipelineArtifactConstants.BuildArtifactDownload,
                                         properties: clientTelemetry.GetArtifactDownloadTelemetry(planId, jobId));
            }

            // check files (will throw an exception if a file is corrupt)
            if (downloadParameters.CheckDownloadedFiles)
            {
                CheckDownloads(items, rootPath, containerIdAndRoot.Item2, downloadParameters.IncludeArtifactNameInPath);
            }
        }
コード例 #16
0
        private async Task PublishCodeCoverageAsync(
            IExecutionContext executionContext,
            IAsyncCommandContext commandContext,
            ICodeCoveragePublisher codeCoveragePublisher,
            IEnumerable <CodeCoverageStatistics> coverageData,
            string project,
            Guid projectId,
            long containerId,
            CancellationToken cancellationToken)
        {
            //step 2: publish code coverage summary to TFS
            if (coverageData != null && coverageData.Count() > 0)
            {
                commandContext.Output(StringUtil.Loc("PublishingCodeCoverage"));
                foreach (var coverage in coverageData)
                {
                    commandContext.Output(StringUtil.Format(" {0}- {1} of {2} covered.", coverage.Label, coverage.Covered, coverage.Total));
                }
                await codeCoveragePublisher.PublishCodeCoverageSummaryAsync(commandContext, coverageData, project, cancellationToken);
            }

            // step 3: publish code coverage files as build artifacts

            string additionalCodeCoverageFilePath = null;
            string destinationSummaryFile         = null;
            var    newReportDirectory             = _reportDirectory;

            try
            {
                var filesToPublish = new List <Tuple <string, string> >();

                if (!Directory.Exists(newReportDirectory))
                {
                    if (!string.IsNullOrWhiteSpace(newReportDirectory))
                    {
                        // user gave a invalid report directory. Write warning and continue.
                        executionContext.Warning(StringUtil.Loc("DirectoryNotFound", newReportDirectory));
                    }
                    newReportDirectory = GetCoverageDirectory(_buildId.ToString(), CodeCoverageConstants.ReportDirectory);
                    Directory.CreateDirectory(newReportDirectory);
                }

                var summaryFileName = Path.GetFileName(_summaryFileLocation);
                destinationSummaryFile = Path.Combine(newReportDirectory, CodeCoverageConstants.SummaryFileDirectory + _buildId, summaryFileName);
                Directory.CreateDirectory(Path.GetDirectoryName(destinationSummaryFile));
                File.Copy(_summaryFileLocation, destinationSummaryFile, true);

                commandContext.Output(StringUtil.Loc("ModifyingCoberturaIndexFile"));
                ModifyCoberturaIndexDotHTML(newReportDirectory, executionContext);

                filesToPublish.Add(new Tuple <string, string>(newReportDirectory, GetCoverageDirectoryName(_buildId.ToString(), CodeCoverageConstants.ReportDirectory)));

                if (_additionalCodeCoverageFiles != null && _additionalCodeCoverageFiles.Count != 0)
                {
                    additionalCodeCoverageFilePath = GetCoverageDirectory(_buildId.ToString(), CodeCoverageConstants.RawFilesDirectory);
                    CodeCoverageUtilities.CopyFilesFromFileListWithDirStructure(_additionalCodeCoverageFiles, ref additionalCodeCoverageFilePath);
                    filesToPublish.Add(new Tuple <string, string>(additionalCodeCoverageFilePath, GetCoverageDirectoryName(_buildId.ToString(), CodeCoverageConstants.RawFilesDirectory)));
                }
                commandContext.Output(StringUtil.Loc("PublishingCodeCoverageFiles"));

                ChangeHtmExtensionToHtmlIfRequired(newReportDirectory, executionContext);

                await codeCoveragePublisher.PublishCodeCoverageFilesAsync(commandContext, projectId, executionContext.Variables.System_JobId, containerId, filesToPublish, File.Exists(Path.Combine(newReportDirectory, CodeCoverageConstants.DefaultIndexFile)), cancellationToken);
            }
            catch (SocketException ex)
            {
                #pragma warning disable CA2000 // Dispose objects before losing scope
                ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(executionContext).Uri.ToString(), executionContext.Warning);
                #pragma warning restore CA2000 // Dispose objects before losing scope
            }
            catch (Exception ex)
            {
                executionContext.Warning(StringUtil.Loc("ErrorOccurredWhilePublishingCCFiles", ex.Message));
            }
            finally
            {
                // clean temporary files.
                if (!string.IsNullOrEmpty(additionalCodeCoverageFilePath))
                {
                    if (Directory.Exists(additionalCodeCoverageFilePath))
                    {
                        Directory.Delete(path: additionalCodeCoverageFilePath, recursive: true);
                    }
                }

                if (!string.IsNullOrEmpty(destinationSummaryFile))
                {
                    var summaryFileDirectory = Path.GetDirectoryName(destinationSummaryFile);
                    if (Directory.Exists(summaryFileDirectory))
                    {
                        Directory.Delete(path: summaryFileDirectory, recursive: true);
                    }
                }

                if (!Directory.Exists(_reportDirectory))
                {
                    if (Directory.Exists(newReportDirectory))
                    {
                        //delete the generated report directory
                        Directory.Delete(path: newReportDirectory, recursive: true);
                    }
                }
            }
        }
コード例 #17
0
        public async Task <Boolean> CreateSessionAsync(CancellationToken token)
        {
            Trace.Entering();

            // Settings
            var configManager = HostContext.GetService <IConfigurationManager>();

            _settings = configManager.LoadSettings();
            var serverUrl = _settings.ServerUrl;

            Trace.Info(_settings);

            // Capabilities.
            _term.WriteLine(StringUtil.Loc("ScanToolCapabilities"));
            Dictionary <string, string> systemCapabilities = await HostContext.GetService <ICapabilitiesManager>().GetCapabilitiesAsync(_settings, token);

            // Create connection.
            Trace.Info("Loading Credentials");
            var            credMgr = HostContext.GetService <ICredentialManager>();
            VssCredentials creds   = credMgr.LoadCredentials();

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

            string errorMessage      = string.Empty;
            bool   encounteringError = false;

            _term.WriteLine(StringUtil.Loc("ConnectToServer"));
            while (true)
            {
                token.ThrowIfCancellationRequested();
                Trace.Info($"Attempt to create session.");
                try
                {
                    Trace.Info("Connecting to the Agent Server...");
                    await _agentServer.ConnectAsync(new Uri(serverUrl), creds);

                    Trace.Info("VssConnection created");

                    _session = await _agentServer.CreateAgentSessionAsync(
                        _settings.PoolId,
                        taskAgentSession,
                        token);

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

                    return(true);
                }
                catch (OperationCanceledException) when(token.IsCancellationRequested)
                {
                    Trace.Info("Session creation has been cancelled.");
                    throw;
                }
                catch (TaskAgentAccessTokenExpiredException)
                {
                    Trace.Info("Agent OAuth token has been revoked. Session creation failed.");
                    throw;
                }
                catch (SocketException ex)
                {
                    ExceptionsUtil.HandleSocketException(ex, serverUrl, Trace.Error);
                    throw;
                }
                catch (Exception ex)
                {
                    Trace.Error("Catch exception during create session.");
                    Trace.Error(ex);

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

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

                    Trace.Info("Sleeping for {0} seconds before retrying.", _sessionCreationRetryInterval.TotalSeconds);
                    await HostContext.Delay(_sessionCreationRetryInterval, token);
                }
            }
        }
コード例 #18
0
        // Refresh connection is best effort. it should never throw exception
        public async Task RefreshConnectionAsync(AgentConnectionType connectionType, TimeSpan timeout)
        {
            Trace.Info($"Refresh {connectionType} VssConnection to get on a different AFD node.");
            VssConnection newConnection = null;

            switch (connectionType)
            {
            case AgentConnectionType.MessageQueue:
                try
                {
                    _hasMessageConnection = false;
                    newConnection         = await EstablishVssConnection(_messageConnection.Uri, _messageConnection.Credentials, timeout);

                    var client = newConnection.GetClient <TaskAgentHttpClient>();
                    _messageConnection      = newConnection;
                    _messageTaskAgentClient = client;
                }
                catch (SocketException ex)
                {
                    ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error);
                    newConnection?.Dispose();
                }
                catch (Exception ex)
                {
                    Trace.Error($"Catch exception during reset {connectionType} connection.");
                    Trace.Error(ex);
                    newConnection?.Dispose();
                }
                finally
                {
                    _hasMessageConnection = true;
                }
                break;

            case AgentConnectionType.JobRequest:
                try
                {
                    _hasRequestConnection = false;
                    newConnection         = await EstablishVssConnection(_requestConnection.Uri, _requestConnection.Credentials, timeout);

                    var client = newConnection.GetClient <TaskAgentHttpClient>();
                    _requestConnection      = newConnection;
                    _requestTaskAgentClient = client;
                }
                catch (SocketException ex)
                {
                    ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error);
                    newConnection?.Dispose();
                }
                catch (Exception ex)
                {
                    Trace.Error($"Catch exception during reset {connectionType} connection.");
                    Trace.Error(ex);
                    newConnection?.Dispose();
                }
                finally
                {
                    _hasRequestConnection = true;
                }
                break;

            case AgentConnectionType.Generic:
                try
                {
                    _hasGenericConnection = false;
                    newConnection         = await EstablishVssConnection(_genericConnection.Uri, _genericConnection.Credentials, timeout);

                    var client = newConnection.GetClient <TaskAgentHttpClient>();
                    _genericConnection      = newConnection;
                    _genericTaskAgentClient = client;
                }
                catch (SocketException ex)
                {
                    ExceptionsUtil.HandleSocketException(ex, _requestConnection.Uri.ToString(), Trace.Error);
                    newConnection?.Dispose();
                }
                catch (Exception ex)
                {
                    Trace.Error($"Catch exception during reset {connectionType} connection.");
                    Trace.Error(ex);
                    newConnection?.Dispose();
                }
                finally
                {
                    _hasGenericConnection = true;
                }
                break;

            default:
                Trace.Error($"Unexpected connection type: {connectionType}.");
                break;
            }
        }
コード例 #19
0
        public bool TryProcessCommand(IExecutionContext context, string input)
        {
            ArgUtil.NotNull(context, nameof(context));
            if (string.IsNullOrEmpty(input))
            {
                return(false);
            }

            // TryParse input to Command
            Command command;
            var     unescapePercents = AgentKnobs.DecodePercents.GetValue(context).AsBoolean();

            if (!Command.TryParse(input, unescapePercents, out command))
            {
                // if parse fail but input contains ##vso, print warning with DOC link
                if (input.IndexOf("##vso") >= 0)
                {
                    context.Warning(StringUtil.Loc("CommandKeywordDetected", input));
                }

                return(false);
            }

            IWorkerCommandExtension extension = null;

            if (_invokePluginInternalCommand && string.Equals(command.Area, _pluginInternalCommandExtensions.CommandArea, StringComparison.OrdinalIgnoreCase))
            {
                extension = _pluginInternalCommandExtensions;
            }

            if (extension != null || _commandExtensions.TryGetValue(command.Area, out extension))
            {
                if (!extension.SupportedHostTypes.HasFlag(context.Variables.System_HostType))
                {
                    context.Error(StringUtil.Loc("CommandNotSupported", command.Area, context.Variables.System_HostType));
                    context.CommandResult = TaskResult.Failed;
                    return(false);
                }

                // process logging command in serialize oreder.
                lock (_commandSerializeLock)
                {
                    try
                    {
                        extension.ProcessCommand(context, command);
                    }
                    catch (SocketException ex)
                    {
                        #pragma warning disable CA2000 // Dispose objects before losing scope
                        ExceptionsUtil.HandleSocketException(ex, WorkerUtilities.GetVssConnection(context).Uri.ToString(), context.Error);
                        #pragma warning restore CA2000 // Dispose objects before losing scope
                        context.CommandResult = TaskResult.Failed;
                    }
                    catch (Exception ex)
                    {
                        context.Error(StringUtil.Loc("CommandProcessFailed", input));
                        context.Error(ex);
                        context.CommandResult = TaskResult.Failed;
                    }
                    finally
                    {
                        // trace the ##vso command as long as the command is not a ##vso[task.debug] command.
                        if (!(string.Equals(command.Area, "task", StringComparison.OrdinalIgnoreCase) &&
                              string.Equals(command.Event, "debug", StringComparison.OrdinalIgnoreCase)))
                        {
                            context.Debug($"Processed: {input}");
                        }
                    }
                }
            }
            else
            {
                context.Warning(StringUtil.Loc("CommandNotFound", command.Area));
            }

            // Only if we've successfully parsed do we show this warning
            if (AgentKnobs.DecodePercents.GetValue(context).AsString() == "" && input.Contains("%AZP25"))
            {
                context.Warning("%AZP25 detected in ##vso command. In March 2021, the agent command parser will be updated to unescape this to %. To opt out of this behavior, set a job level variable DECODE_PERCENTS to false. Setting to true will force this behavior immediately. More information can be found at https://github.com/microsoft/azure-pipelines-agent/blob/master/docs/design/percentEncoding.md");
            }

            return(true);
        }