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