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> ChangeAppSettings(InstanceName instance, DevOpsLogon devopsLogonData, SaveMode saveMode) { var webFunctionApp = await azure .AppServices .WebApps .GetByResourceGroupAsync( instance.ResourceGroupName, instance.FunctionAppName); var configuration = new AggregatorConfiguration { DevOpsTokenType = devopsLogonData.Mode, DevOpsToken = devopsLogonData.Token, SaveMode = saveMode }; configuration.Write(webFunctionApp); return(true); }
internal async Task <IEnumerable <ILogDataObject> > ListInResourceGroupAsync(string resourceGroup, CancellationToken cancellationToken) { var runtime = new FunctionRuntimePackage(logger); var apps = await azure.AppServices.FunctionApps.ListByResourceGroupAsync(resourceGroup, cancellationToken : cancellationToken); var result = new List <InstanceOutputData>(); foreach (var app in apps) { cancellationToken.ThrowIfCancellationRequested(); var name = InstanceName.FromFunctionAppName(app.Name, resourceGroup); result.Add(new InstanceOutputData( name.PlainName, app.Region.Name, await runtime.GetDeployedRuntimeVersion(name, azure, cancellationToken)) ); } return(result); }
internal async Task <bool> AddAsync(InstanceName instance, string ruleName, string filePath, CancellationToken cancellationToken) { _logger.WriteInfo($"Validate rule file {filePath}"); var preprocessedRule = await LoadAndValidateRule(ruleName, filePath, cancellationToken); if (preprocessedRule == null) { _logger.WriteError("Rule file is invalid"); return(false); } _logger.WriteInfo("Rule file is valid"); _logger.WriteVerbose($"Layout rule files"); var inMemoryFiles = await PackagingFilesAsync(ruleName, preprocessedRule); using (var assemblyStream = await FunctionRuntimePackage.GetDeployedFunctionEntrypoint(instance, _azure, _logger, cancellationToken)) { await inMemoryFiles.AddFunctionDefaultFiles(assemblyStream); } _logger.WriteInfo($"Packaging rule {ruleName} complete."); _logger.WriteVerbose($"Uploading rule files to {instance.PlainName}"); bool ok = await UploadRuleFilesAsync(instance, ruleName, inMemoryFiles, cancellationToken); if (ok) { _logger.WriteInfo($"All {ruleName} files successfully uploaded to {instance.PlainName}."); } if (preprocessedRule.Impersonate) { _logger.WriteInfo($"Configure {ruleName} to execute impersonated."); ok &= await ConfigureAsync(instance, ruleName, impersonate : true, cancellationToken : cancellationToken); if (ok) { _logger.WriteInfo($"Updated {ruleName} configuration successfully."); } } return(ok); }
internal async Task <bool> RemoveRuleEventAsync(string @event, InstanceName instance, string projectName, string rule) { logger.WriteInfo($"Querying the Azure DevOps subscriptions for rule(s) {instance.PlainName}/{rule}"); var serviceHooksClient = devops.GetClient <ServiceHooksPublisherHttpClient>(); var subscriptions = await serviceHooksClient.QuerySubscriptionsAsync(DevOpsEvents.PublisherId); var ruleSubs = subscriptions // TODO can we trust this equality? // && s.ActionDescription == $"To host {instance.DnsHostName}" .Where(s => s.ConsumerInputs["url"].ToString().StartsWith( instance.FunctionAppUrl)); if (@event != "*") { ruleSubs = ruleSubs.Where(s => s.EventType == @event); } if (projectName != "*") { logger.WriteVerbose($"Reading Azure DevOps project data..."); var projectClient = devops.GetClient <ProjectHttpClient>(); var project = await projectClient.GetProject(projectName); logger.WriteInfo($"Project {projectName} data read."); ruleSubs = ruleSubs.Where(s => s.PublisherInputs["projectId"] == project.Id.ToString()); } if (rule != "*") { ruleSubs = ruleSubs .Where(s => s.ConsumerInputs["url"].ToString().StartsWith( AggregatorRules.GetInvocationUrl(instance, rule))); } foreach (var ruleSub in ruleSubs) { logger.WriteVerbose($"Deleting subscription {ruleSub.EventDescription} {ruleSub.EventType}..."); await serviceHooksClient.DeleteSubscriptionAsync(ruleSub.Id); logger.WriteInfo($"Subscription {ruleSub.EventDescription} {ruleSub.EventType} deleted."); } return(true); }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .BuildAsync(cancellationToken); var instance = new InstanceName(Name, ResourceGroup); if (!Mappings) { var mappings = new AggregatorMappings(context.Devops, context.Azure, context.Logger); _ = await mappings.RemoveInstanceAsync(instance); } var instances = new AggregatorInstances(context.Azure, context.Logger); var ok = await instances.RemoveAsync(instance, Location); return(ok ? 0 : 1); }
internal async Task <bool> AddAsync(InstanceName instance, string name, string filePath) { var kudu = new KuduApi(instance, azure, logger); logger.WriteVerbose($"Layout rule files"); string baseDirPath = LayoutRuleFiles(name, filePath); logger.WriteInfo($"Packaging {filePath} into rule {name} complete."); logger.WriteVerbose($"Uploading rule files to {instance.PlainName}"); bool ok = await UploadRuleFiles(instance, name, baseDirPath); if (ok) { logger.WriteInfo($"All {name} files uploaded to {instance.PlainName}."); } CleanupRuleFiles(baseDirPath); logger.WriteInfo($"Cleaned local working directory."); return(ok); }
internal IEnumerable <(string rule, string project, string events)> List(InstanceName instance) { var serviceHooksClient = vsts.GetClient <ServiceHooksPublisherHttpClient>(); var subscriptions = serviceHooksClient .QuerySubscriptionsAsync() .Result .Where(s => s.PublisherId == "tfs" && s.ConsumerInputs["url"].ToString().StartsWith( instance.FunctionAppUrl) ); foreach (var subscription in subscriptions) { yield return( subscription.ConsumerInputs["url"], subscription.PublisherInputs["projectId"], subscription.EventType ); } }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .BuildAsync(cancellationToken); var mappings = new AggregatorMappings(context.Devops, context.Azure, context.Logger); bool ok = DevOpsEvents.IsValidEvent(Event); if (!ok) { context.Logger.WriteError($"Invalid event type."); return(2); } var instance = new InstanceName(Instance, ResourceGroup); var id = await mappings.AddAsync(Project, Event, instance, Rule, cancellationToken); return(id.Equals(Guid.Empty) ? 1 : 0); }
internal async Task <bool> RemoveAsync(InstanceName instance, string name, CancellationToken cancellationToken) { var kudu = GetKudu(instance); // undocumented but works, see https://github.com/projectkudu/kudu/wiki/Functions-API _logger.WriteInfo($"Removing Function {name} in {instance.PlainName}..."); using (var client = new HttpClient()) using (var request = await kudu.GetRequestAsync(HttpMethod.Delete, $"api/functions/{name}", cancellationToken)) using (var response = await client.SendAsync(request, cancellationToken)) { bool ok = response.IsSuccessStatusCode; if (!ok) { _logger.WriteError($"Failed removing Function {name} from {instance.PlainName} with {response.ReasonPhrase}"); } return(ok); } //TODO BobSilent remove configuration (Enable/Disable or Impersonate) }
internal async Task <IEnumerable <MappingOutputData> > ListAsync(InstanceName instance, string projectName) { logger.WriteVerbose($"Searching aggregator mappings in Azure DevOps..."); var serviceHooksClient = devops.GetClient <ServiceHooksPublisherHttpClient>(); var subscriptions = await serviceHooksClient.QuerySubscriptionsAsync(); var filteredSubs = instance != null ? subscriptions.Where(s => s.PublisherId == DevOpsEvents.PublisherId && s.ConsumerInputs.GetValue("url", "").StartsWith( instance.FunctionAppUrl, StringComparison.OrdinalIgnoreCase)) : subscriptions.Where(s => s.PublisherId == DevOpsEvents.PublisherId // HACK && s.ConsumerInputs.GetValue("url", "").IndexOf("aggregator.azurewebsites.net") > 8); var projectClient = devops.GetClient <ProjectHttpClient>(); var projects = await projectClient.GetProjects(); var projectsDict = projects.ToDictionary(p => p.Id); var result = new List <MappingOutputData>(); foreach (var subscription in filteredSubs) { var foundProject = projectsDict[ new Guid(subscription.PublisherInputs["projectId"]) ]; if (!string.IsNullOrEmpty(projectName) && foundProject.Name != projectName) { continue; } Uri ruleUrl = new Uri(subscription.ConsumerInputs.GetValue("url", MagicConstants.MissingUrl)); string ruleFullName = GetRuleFullName(ruleUrl); result.Add( new MappingOutputData(instance, ruleFullName, ruleUrl.IsImpersonationEnabled(), foundProject.Name, subscription.EventType, subscription.Status.ToString()) ); } return(result); }
private async Task <bool> UploadRuntimeZip(InstanceName instance, byte[] zipContent) { var kudu = new KuduApi(instance, azure, logger); // POST /api/zipdeploy?isAsync=true // Deploy from zip asynchronously. The Location header of the response will contain a link to a pollable deployment status. var body = new ByteArrayContent(zipContent); using (var client = new HttpClient()) using (var request = await kudu.GetRequestAsync(HttpMethod.Post, $"api/zipdeploy")) { request.Content = body; using (var response = await client.SendAsync(request)) { bool ok = response.IsSuccessStatusCode; if (!ok) { logger.WriteError($"Upload failed with {response.ReasonPhrase}"); } return(ok); } } }
internal override async Task <int> RunAsync() { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .Build(); var rules = new AggregatorRules(context.Azure, context.Logger); if (Local) { bool ok = await rules.InvokeLocalAsync(Project, Event, WorkItemId, Source, DryRun, SaveMode); return(ok ? 0 : 1); } else { var instance = new InstanceName(Instance, ResourceGroup); context.Logger.WriteWarning("Not implemented yet."); return(2); } }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .BuildAsync(cancellationToken); var instance = new InstanceName(Instance, ResourceGroup); var rules = new AggregatorRules(context.Azure, context.Logger); bool any = false; foreach (var item in await rules.ListAsync(instance, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); context.Logger.WriteOutput(new RuleOutputData(instance, item)); any = true; } if (!any) { context.Logger.WriteInfo($"No rules found in aggregator instance {instance.PlainName}."); } return(0); }