Пример #1
0
#pragma warning disable S107  // Methods should not have too many parameters
#pragma warning disable S4457 // Parameter validation in "async"/"await" methods should be wrapped
        internal async Task <bool> InvokeLocalAsync(string projectName, string @event, int workItemId, string ruleFilePath, bool dryRun, SaveMode saveMode, bool impersonateExecution, CancellationToken cancellationToken)
#pragma warning restore S4457 // Parameter validation in "async"/"await" methods should be wrapped
#pragma warning restore S107  // Methods should not have too many parameters
        {
            if (!File.Exists(ruleFilePath))
            {
                _logger.WriteError($"Rule code not found at {ruleFilePath}");
                return(false);
            }

            var devopsLogonData = DevOpsLogon.Load().connection;

            _logger.WriteVerbose($"Connecting to Azure DevOps using {devopsLogonData.Mode}...");
            VssCredentials clientCredentials;

            if (devopsLogonData.Mode == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(devopsLogonData.Mode.ToString(), devopsLogonData.Token);
            }
            else
            {
                _logger.WriteError($"Azure DevOps Token type {devopsLogonData.Mode} not supported!");
                throw new ArgumentException($"Azure DevOps Token type {devopsLogonData.Mode} not supported!");
            }

            // see https://rules.sonarsource.com/csharp/RSPEC-4457
            return(await InvokeLocalAsyncImpl(projectName, @event, workItemId, ruleFilePath, dryRun, saveMode, impersonateExecution, devopsLogonData, clientCredentials, cancellationToken));
        }
Пример #2
0
        internal async Task <bool> InvokeLocalAsync(string projectName, string @event, int workItemId, string ruleFilePath, bool dryRun, SaveMode saveMode)
        {
            if (!File.Exists(ruleFilePath))
            {
                logger.WriteError($"Rule code not found at {ruleFilePath}");
                return(false);
            }

            var devopsLogonData = DevOpsLogon.Load().connection;

            logger.WriteVerbose($"Connecting to Azure DevOps using {devopsLogonData.Mode}...");
            var clientCredentials = default(VssCredentials);

            if (devopsLogonData.Mode == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(devopsLogonData.Mode.ToString(), devopsLogonData.Token);
            }
            else
            {
                logger.WriteError($"Azure DevOps Token type {devopsLogonData.Mode} not supported!");
                throw new ArgumentOutOfRangeException(nameof(devopsLogonData.Mode));
            }

            string collectionUrl = devopsLogonData.Url;

            using (var devops = new VssConnection(new Uri(collectionUrl), clientCredentials))
            {
                await devops.ConnectAsync();

                logger.WriteInfo($"Connected to Azure DevOps");

                Guid   teamProjectId;
                string teamProjectName;
                using (var projectClient = devops.GetClient <ProjectHttpClient>())
                {
                    logger.WriteVerbose($"Reading Azure DevOps project data...");
                    var project = await projectClient.GetProject(projectName);

                    logger.WriteInfo($"Project {projectName} data read.");
                    teamProjectId   = project.Id;
                    teamProjectName = project.Name;
                }

                using (var witClient = devops.GetClient <WorkItemTrackingHttpClient>())
                {
                    logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    string[] ruleCode = File.ReadAllLines(ruleFilePath);

                    var engineLogger = new EngineWrapperLogger(logger);
                    var engine       = new Engine.RuleEngine(engineLogger, ruleCode, saveMode);
                    engine.DryRun = dryRun;

                    string result = await engine.ExecuteAsync(collectionUrl, teamProjectId, teamProjectName, devopsLogonData.Token, workItemId, witClient);

                    logger.WriteInfo($"Rule returned '{result}'");

                    return(true);
                }
            }
        }
Пример #3
0
        internal override async Task <int> RunAsync(CancellationToken cancellationToken)
        {
            var context = await Context.BuildAsync(cancellationToken);

            var azData = new AzureLogon()
            {
                SubscriptionId = Environment.GetEnvironmentVariable("AGGREGATOR_SUBSCRIPTIONID") ?? string.Empty,
                ClientId       = Environment.GetEnvironmentVariable("AGGREGATOR_CLIENTID") ?? string.Empty,
                ClientSecret   = Environment.GetEnvironmentVariable("AGGREGATOR_CLIENTSECRET") ?? string.Empty,
                TenantId       = Environment.GetEnvironmentVariable("AGGREGATOR_TENANTID") ?? string.Empty
            };

            _ = azData.Save();
            // now check for validity
            context.Logger.WriteInfo("Connecting to Azure...");
            var azure = azData.Logon();

            if (azure == null)
            {
                context.Logger.WriteError("Invalid azure credentials");
                return(ExitCodes.InvalidArguments);
            }
            // FIX #60: call some read API to validate parameters
            try
            {
                await azure.Subscriptions.ListAsync(false, cancellationToken);
            }
            catch (Exception ex)
            {
                int    nl = ex.Message.IndexOf(Environment.NewLine);
                string m  = nl != -1 ? ex.Message.Remove(nl) : ex.Message;
                context.Logger.WriteError("Invalid azure credentials: " + m);
                return(ExitCodes.InvalidArguments);
            }

            var data = new DevOpsLogon()
            {
                Url  = Environment.GetEnvironmentVariable("AGGREGATOR_AZDO_URL") ?? string.Empty,
                Mode = (DevOpsTokenType)Enum.Parse(typeof(DevOpsTokenType),
                                                   Environment.GetEnvironmentVariable("AGGREGATOR_AZDO_MODE") ?? "PAT"),
                Token = Environment.GetEnvironmentVariable("AGGREGATOR_AZDO_TOKEN") ?? string.Empty,
            };

            _ = data.Save();
            // now check for validity
            context.Logger.WriteInfo($"Connecting to Azure DevOps using {data.Mode} credential...");
            var devops = await data.LogonAsync(cancellationToken);

            if (devops == null)
            {
                context.Logger.WriteError("Invalid Azure DevOps credentials");
                return(ExitCodes.InvalidArguments);
            }

            return(ExitCodes.Success);
        }
Пример #4
0
        internal override async Task <int> RunAsync(CancellationToken cancellationToken)
        {
            var context = await Context.BuildAsync(cancellationToken);

            context.Logger.WriteVerbose($"Clearing cached credentials");
            AzureLogon.Clear();
            DevOpsLogon.Clear();

            return(ExitCodes.Success);
        }
        internal async Task <CommandContext> BuildAsync(CancellationToken cancellationToken)
        {
            IAzure        azure  = null;
            VssConnection devops = null;

            if (azureLogon)
            {
                logger.WriteVerbose($"Authenticating to Azure...");
                var(connection, reason) = AzureLogon.Load();
                if (reason != LogonResult.Succeeded)
                {
                    string msg = TranslateResult(reason);
                    throw new InvalidOperationException(string.Format(msg, "Azure", "logon.azure"));
                }

                azure = connection.Logon();
                logger.WriteInfo($"Connected to subscription {azure.SubscriptionId}");
            }

            if (devopsLogon)
            {
                logger.WriteVerbose($"Authenticating to Azure DevOps...");
                var(connection, reason) = DevOpsLogon.Load();
                if (reason != LogonResult.Succeeded)
                {
                    string msg = TranslateResult(reason);
                    throw new InvalidOperationException(string.Format(msg, "Azure DevOps", "logon.ado"));
                }

                devops = await connection.LogonAsync(cancellationToken);

                logger.WriteInfo($"Connected to {devops.Uri.Host}");
            }

            INamingTemplates naming;

            switch (namingTemplate.ToLower())
            {
            case "builtin":
#pragma warning disable S907 // "goto" statement should not be used
                goto case "";

#pragma warning restore S907 // "goto" statement should not be used
            case "":
                naming = new BuiltInNamingTemplates();
                break;

            default:
                // implement custom Naming Templates, e.g. reading from a file
                naming = new FileNamingTemplates(File.ReadAllText(namingTemplate));
                break;
            }

            return(new CommandContext(logger, azure, devops, naming));
        }
        internal async Task <bool> ChangeAppSettingsAsync(InstanceName instance, DevOpsLogon devopsLogonData, SaveMode saveMode, CancellationToken cancellationToken)
        {
            var webFunctionApp = await GetWebApp(instance, cancellationToken);

            var configuration = await AggregatorConfiguration.ReadConfiguration(webFunctionApp);

            configuration.DevOpsTokenType = devopsLogonData.Mode;
            configuration.DevOpsToken     = devopsLogonData.Token;
            configuration.SaveMode        = saveMode;

            configuration.WriteConfiguration(webFunctionApp);
            return(true);
        }
        internal async Task <bool> AddAsync(InstanceName instance, string location, string requiredVersion, string sourceUrl, InstanceFineTuning tuning, CancellationToken cancellationToken)
        {
            string rgName = instance.ResourceGroupName;
            bool   ok     = await MakeSureResourceGroupExistsAsync(instance.IsCustom, location, rgName, cancellationToken);

            if (!ok)
            {
                return(false);
            }

            ok = await DeployArmTemplateAsync(instance, location, rgName, tuning, cancellationToken);

            if (!ok)
            {
                return(false);
            }

            // check runtime package
            var package = new FunctionRuntimePackage(logger);

            ok = await package.UpdateVersionAsync(requiredVersion, sourceUrl, instance, azure, cancellationToken);

            if (ok)
            {
                var devopsLogonData = DevOpsLogon.Load().connection;
                if (devopsLogonData.Mode == DevOpsTokenType.PAT)
                {
                    logger.WriteVerbose($"Saving Azure DevOps token");
                    ok = await ChangeAppSettingsAsync(instance, devopsLogonData, SaveMode.Default, cancellationToken);

                    if (ok)
                    {
                        logger.WriteInfo($"Azure DevOps token saved");
                    }
                    else
                    {
                        logger.WriteError($"Failed to save Azure DevOps token");
                    }
                }
                else
                {
                    logger.WriteWarning($"Azure DevOps token type {devopsLogonData.Mode} is unsupported");
                    ok = false;
                }
            }
            return(ok);
        }
Пример #8
0
        internal async Task <bool> ChangeAppSettingsAsync(InstanceName instance, DevOpsLogon devopsLogonData, SaveMode saveMode, CancellationToken cancellationToken)
        {
            var webFunctionApp = await azure
                                 .AppServices
                                 .WebApps
                                 .GetByResourceGroupAsync(
                instance.ResourceGroupName,
                instance.FunctionAppName, cancellationToken);

            var configuration = new AggregatorConfiguration
            {
                DevOpsTokenType = devopsLogonData.Mode,
                DevOpsToken     = devopsLogonData.Token,
                SaveMode        = saveMode
            };

            configuration.Write(webFunctionApp);
            return(true);
        }
        internal async Task <bool> ChangeAppSettings(InstanceName instance, string location, SaveMode saveMode)
        {
            bool ok;
            var  devopsLogonData = DevOpsLogon.Load().connection;

            if (devopsLogonData.Mode == DevOpsTokenType.PAT)
            {
                logger.WriteVerbose($"Saving Azure DevOps token");
                ok = await ChangeAppSettings(instance, devopsLogonData, saveMode);

                logger.WriteInfo($"Azure DevOps token saved");
            }
            else
            {
                logger.WriteWarning($"Azure DevOps token type {devopsLogonData.Mode} is unsupported");
                ok = false;
            }
            return(ok);
        }
Пример #10
0
        internal override async Task <int> RunAsync(CancellationToken cancellationToken)
        {
            var context = await Context.BuildAsync(cancellationToken);

            var data = new DevOpsLogon()
            {
                Url   = this.Url,
                Mode  = this.Mode,
                Token = this.Token
            };

            _ = data.Save();
            // now check for validity
            context.Logger.WriteInfo($"Connecting to Azure DevOps using {Mode} credential...");
            var devops = await data.LogonAsync(cancellationToken);

            if (devops == null)
            {
                context.Logger.WriteError("Invalid Azure DevOps credentials");
                return(2);
            }

            return(0);
        }
Пример #11
0
#pragma warning disable S107 // Methods should not have too many parameters
        private async Task <bool> InvokeLocalAsyncImpl(string projectName, string @event, int workItemId, string ruleFilePath, bool dryRun, SaveMode saveMode, bool impersonateExecution, DevOpsLogon devopsLogonData, VssCredentials clientCredentials, CancellationToken cancellationToken)
#pragma warning restore S107 // Methods should not have too many parameters
        {
            string collectionUrl = devopsLogonData.Url;

            using (var devops = new VssConnection(new Uri(collectionUrl), clientCredentials))
            {
                await devops.ConnectAsync(cancellationToken);

                _logger.WriteInfo($"Connected to Azure DevOps");

                Guid teamProjectId;
                using (var projectClient = devops.GetClient <ProjectHttpClient>())
                {
                    _logger.WriteVerbose($"Reading Azure DevOps project data...");
                    var project = await projectClient.GetProject(projectName);

                    _logger.WriteInfo($"Project {projectName} data read.");
                    teamProjectId = project.Id;
                }

                using (var clientsContext = new AzureDevOpsClientsContext(devops))
                {
                    _logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    var(preprocessedRule, _) = await RuleFileParser.ReadFile(ruleFilePath, cancellationToken);

                    var rule = new Engine.ScriptedRuleWrapper(Path.GetFileNameWithoutExtension(ruleFilePath), preprocessedRule)
                    {
                        ImpersonateExecution = impersonateExecution
                    };

                    var engineLogger = new EngineWrapperLogger(_logger);
                    var engine       = new Engine.RuleEngine(engineLogger, saveMode, dryRun: dryRun);

                    var workItem = await clientsContext.WitClient.GetWorkItemAsync(projectName, workItemId, expand : WorkItemExpand.All, cancellationToken : cancellationToken);

                    string result = await engine.RunAsync(rule, teamProjectId, workItem, @event, clientsContext, cancellationToken);

                    _logger.WriteInfo($"Rule returned '{result}'");

                    return(true);
                }
            }
        }
Пример #12
0
        internal async Task <bool> InvokeLocalAsync(string projectName, string @event, int workItemId, string ruleFilePath, bool dryRun, SaveMode saveMode, bool impersonateExecution, CancellationToken cancellationToken)
        {
            if (!File.Exists(ruleFilePath))
            {
                _logger.WriteError($"Rule code not found at {ruleFilePath}");
                return(false);
            }

            var devopsLogonData = DevOpsLogon.Load().connection;

            _logger.WriteVerbose($"Connecting to Azure DevOps using {devopsLogonData.Mode}...");
            var clientCredentials = default(VssCredentials);

            if (devopsLogonData.Mode == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(devopsLogonData.Mode.ToString(), devopsLogonData.Token);
            }
            else
            {
                _logger.WriteError($"Azure DevOps Token type {devopsLogonData.Mode} not supported!");
                throw new ArgumentOutOfRangeException(nameof(devopsLogonData.Mode));
            }

            string collectionUrl = devopsLogonData.Url;

            using (var devops = new VssConnection(new Uri(collectionUrl), clientCredentials))
            {
                await devops.ConnectAsync(cancellationToken);

                _logger.WriteInfo($"Connected to Azure DevOps");

                Guid teamProjectId;
                using (var projectClient = devops.GetClient <ProjectHttpClient>())
                {
                    _logger.WriteVerbose($"Reading Azure DevOps project data...");
                    var project = await projectClient.GetProject(projectName);

                    _logger.WriteInfo($"Project {projectName} data read.");
                    teamProjectId = project.Id;
                }

                using (var clientsContext = new AzureDevOpsClientsContext(devops))
                {
                    _logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    var(preprocessedRule, _) = await RuleFileParser.ReadFile(ruleFilePath, cancellationToken);

                    var rule = new Engine.ScriptedRuleWrapper(Path.GetFileNameWithoutExtension(ruleFilePath), preprocessedRule)
                    {
                        ImpersonateExecution = impersonateExecution
                    };

                    var engineLogger = new EngineWrapperLogger(_logger);
                    var engine       = new Engine.RuleEngine(engineLogger, saveMode, dryRun: dryRun);

                    var workItem = await clientsContext.WitClient.GetWorkItemAsync(projectName, workItemId, expand : WorkItemExpand.All, cancellationToken : cancellationToken);

                    string result = await engine.RunAsync(rule, teamProjectId, workItem, clientsContext, cancellationToken);

                    _logger.WriteInfo($"Rule returned '{result}'");

                    return(true);
                }
            }
        }
Пример #13
0
        internal async Task <bool> AddAsync(InstanceName instance, string location, string requiredVersion, string sourceUrl, CancellationToken cancellationToken)
        {
            string rgName = instance.ResourceGroupName;

            logger.WriteVerbose($"Checking if Resource Group {rgName} already exists");
            if (!await azure.ResourceGroups.ContainAsync(rgName, cancellationToken))
            {
                if (instance.IsCustom)
                {
                    logger.WriteError($"Resource group {rgName} is custom and cannot be created.");
                    return(false);
                }

                logger.WriteVerbose($"Creating resource group {rgName}");
                await azure.ResourceGroups
                .Define(rgName)
                .WithRegion(location)
                .CreateAsync();

                logger.WriteInfo($"Resource group {rgName} created.");
            }

            // IDEA the template should create a Storage account and/or a Key Vault for Rules' use
            // TODO https://github.com/gjlumsden/AzureFunctionsSlots suggest that slots must be created in template
            var    resourceName = "aggregator.cli.Instances.instance-template.json";
            string armTemplateString;
            var    assembly = Assembly.GetExecutingAssembly();

            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
                using (StreamReader reader = new StreamReader(stream))
                {
                    armTemplateString = await reader.ReadToEndAsync();
                }

            var parsedTemplate = JObject.Parse(armTemplateString);

            // sanity checks
            if (parsedTemplate.SelectToken("parameters.appName") == null)
            {
                // not good, blah
                logger.WriteWarning($"Something is wrong with the ARM template");
            }

            string appName        = instance.FunctionAppName;
            var    infoVersion    = GetCustomAttribute <AssemblyInformationalVersionAttribute>();
            var    templateParams = new Dictionary <string, Dictionary <string, object> > {
                // TODO give use more control by setting more parameters
                { "location", new Dictionary <string, object> {
                      { "value", location }
                  } },
                { "storageAccountType", new Dictionary <string, object> {
                      { "value", "Standard_LRS" }
                  } },
                { "appName", new Dictionary <string, object> {
                      { "value", appName }
                  } },
                { "aggregatorVersion", new Dictionary <string, object> {
                      { "value", infoVersion.InformationalVersion }
                  } },
                { "hostingPlanSkuName", new Dictionary <string, object> {
                      { "value", "Y1" }
                  } },
                { "hostingPlanSkuTier", new Dictionary <string, object> {
                      { "value", "Dynamic" }
                  } },
            };

            string deploymentName = SdkContext.RandomResourceName("aggregator", 24);

            logger.WriteInfo($"Started deployment (id: {deploymentName})");
            var deployment = await azure.Deployments.Define(deploymentName)
                             .WithExistingResourceGroup(rgName)
                             .WithTemplate(armTemplateString)
                             .WithParameters(templateParams)
                             .WithMode(DeploymentMode.Incremental)
                             .CreateAsync(cancellationToken);

            // poll
            const int pollIntervalInSeconds = 3;
            int       totalDelay            = 0;

            while (!(StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Succeeded") ||
                     StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Failed") ||
                     StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Cancelled")))
            {
                SdkContext.DelayProvider.Delay(pollIntervalInSeconds * 1000);
                totalDelay += pollIntervalInSeconds;
                logger.WriteVerbose($"Deployment running ({totalDelay}s)");
                await deployment.RefreshAsync(cancellationToken);
            }
            logger.WriteInfo($"Deployment {deployment.ProvisioningState}");

            // check runtime package
            var  package = new FunctionRuntimePackage(logger);
            bool ok      = await package.UpdateVersionAsync(requiredVersion, sourceUrl, instance, azure, cancellationToken);

            if (ok)
            {
                var devopsLogonData = DevOpsLogon.Load().connection;
                if (devopsLogonData.Mode == DevOpsTokenType.PAT)
                {
                    logger.WriteVerbose($"Saving Azure DevOps token");
                    ok = await ChangeAppSettingsAsync(instance, devopsLogonData, SaveMode.Default, cancellationToken);

                    if (ok)
                    {
                        logger.WriteInfo($"Azure DevOps token saved");
                    }
                    else
                    {
                        logger.WriteError($"Failed to save Azure DevOps token");
                    }
                }
                else
                {
                    logger.WriteWarning($"Azure DevOps token type {devopsLogonData.Mode} is unsupported");
                    ok = false;
                }
            }
            return(ok);
        }