internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var validHostingPlanSkus = new string[] { "Y1", "F1", "D1", "B1", "S1", "S2", "S3", "P1", "P2", "P3", "P1V2", "P2V2", "P3V2" }; var validHostingPlanTiers = new string[] { "Dynamic", "Free", "Shared", "Basic", "Standard", "Premium" }; if (!validHostingPlanSkus.Contains(HostingPlanSku)) { Logger.WriteError($"Invalid value for hostingPlanSku: must be one of {String.Join(",", validHostingPlanSkus)}"); return(ExitCodes.InvalidArguments); } if (!validHostingPlanTiers.Contains(HostingPlanTier)) { Logger.WriteError($"Invalid value for hostingPlanTier: must be one of {String.Join(",", validHostingPlanTiers)}"); return(ExitCodes.InvalidArguments); } var tuning = new AggregatorInstances.InstanceFineTuning { AppInsightLocation = string.IsNullOrWhiteSpace(AppInsightLocation) ? Location : AppInsightLocation, HostingPlanSku = HostingPlanSku, HostingPlanTier = HostingPlanTier }; var context = await Context .WithAzureLogon() .WithDevOpsLogon() // need the token, so we can save it in the app settings .BuildAsync(cancellationToken); context.ResourceGroupDeprecationCheck(this.ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); var instance = context.Naming.GetInstanceCreateNames(Name, ResourceGroup); bool ok = await instances.AddAsync(instance, Location, RequiredVersion, SourceUrl, tuning, cancellationToken); return(ok ? ExitCodes.Success : ExitCodes.Failure); }
internal async Task <(string url, string key)> GetInvocationUrlAndKey(InstanceName instance, string rule, CancellationToken cancellationToken) { var instances = new AggregatorInstances(_azure, _logger); var kudu = new KuduApi(instance, _azure, _logger); // see https://github.com/projectkudu/kudu/wiki/Functions-API using (var client = new HttpClient()) using (var request = await kudu.GetRequestAsync(HttpMethod.Post, $"api/functions/{rule}/listsecrets", cancellationToken)) { using (var response = await client.SendAsync(request, cancellationToken)) { if (response.IsSuccessStatusCode) { using (var stream = await response.Content.ReadAsStreamAsync()) using (var sr = new StreamReader(stream)) using (var jtr = new JsonTextReader(sr)) { var js = new JsonSerializer(); var secret = js.Deserialize <KuduSecret>(jtr); (string url, string key)invocation = (GetInvocationUrl(instance, rule), secret.Key); return(invocation); } } string error = await response.Content.ReadAsStringAsync(); _logger.WriteError($"Failed to retrieve function key: {error}"); throw new InvalidOperationException("Failed to retrieve function key."); } } }
internal async Task <(string url, string key)> GetInvocationUrlAndKey(InstanceName instance, string rule) { var instances = new AggregatorInstances(azure, logger); var kudu = new KuduApi(instance, azure, logger); logger.WriteVerbose($"Querying Function key..."); // see https://github.com/projectkudu/kudu/wiki/Functions-API using (var client = new HttpClient()) using (var request = await kudu.GetRequestAsync(HttpMethod.Post, $"api/functions/{rule}/listsecrets")) { using (var response = await client.SendAsync(request)) { if (response.IsSuccessStatusCode) { using (var stream = await response.Content.ReadAsStreamAsync()) using (var sr = new StreamReader(stream)) using (var jtr = new JsonTextReader(sr)) { var js = new JsonSerializer(); var secret = js.Deserialize <KuduSecret>(jtr); (string url, string key)invocation = (GetInvocationUrl(instance, rule), secret.Key); logger.WriteInfo($"Function key retrieved."); return(invocation); } } else { return(default);
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .BuildAsync(cancellationToken); context.ResourceGroupDeprecationCheck(this.ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); if (!string.IsNullOrEmpty(Location)) { context.Logger.WriteVerbose($"Searching aggregator instances in {Location} Region..."); return(await ListByLocationAsync(context, instances, cancellationToken)); } else if (!string.IsNullOrEmpty(ResourceGroup)) { context.Logger.WriteVerbose($"Searching aggregator instances in {ResourceGroup} Resource Group..."); return(await ListInResourceGroupAsync(context, instances, cancellationToken)); } else { context.Logger.WriteVerbose($"Searching aggregator instances in whole subscription..."); return(await ListAllAsync(context, instances, cancellationToken)); } }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var validHostingPlanSkus = new string[] { "Y1", "F1", "D1", "B1", "S1", "S2", "S3", "P1", "P2", "P3", "P1V2", "P2V2", "P3V2" }; var validHostingPlanTiers = new string[] { "Dynamic", "Free", "Shared", "Basic", "Standard", "Premium" }; if (!validHostingPlanSkus.Contains(HostingPlanSku)) { Logger.WriteError($"Invalid value for hostingPlanSku: must be one of {String.Join(",", validHostingPlanSkus)}"); return(2); } if (!validHostingPlanTiers.Contains(HostingPlanTier)) { Logger.WriteError($"Invalid value for hostingPlanTier: must be one of {String.Join(",", validHostingPlanTiers)}"); return(2); } var context = await Context .WithAzureLogon() .WithDevOpsLogon() // need the token, so we can save it in the app settings .BuildAsync(cancellationToken); var instances = new AggregatorInstances(context.Azure, context.Logger); var instance = new InstanceName(Name, ResourceGroup); bool ok = await instances.AddAsync(instance, Location, RequiredVersion, SourceUrl, cancellationToken); return(ok ? 0 : 1); }
internal async Task <IEnumerable <KuduFunction> > ListAsync(InstanceName instance) { var instances = new AggregatorInstances(azure, logger); var kudu = new KuduApi(instance, azure, logger); logger.WriteInfo($"Retrieving Functions in {instance.PlainName}..."); using (var client = new HttpClient()) using (var request = await kudu.GetRequestAsync(HttpMethod.Get, $"api/functions")) using (var response = await client.SendAsync(request)) { var stream = await response.Content.ReadAsStreamAsync(); if (response.IsSuccessStatusCode) { using (var sr = new StreamReader(stream)) using (var jtr = new JsonTextReader(sr)) { var js = new JsonSerializer(); var functionList = js.Deserialize <KuduFunction[]>(jtr); return(functionList); } } else { logger.WriteError($"{response.ReasonPhrase} {await response.Content.ReadAsStringAsync()}"); return(new KuduFunction[0]); } } }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .BuildAsync(cancellationToken); var instance = context.Naming.Instance(Instance, ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); bool ok = await instances.StreamLogsAsync(instance, cancellationToken); return(ok ? ExitCodes.Success : ExitCodes.Failure); }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .BuildAsync(cancellationToken); var instance = new InstanceName(Instance, ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger); bool ok = await instances.StreamLogsAsync(instance, cancellationToken); return(ok ? 0 : 1); }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() // need the token, so we can save it in the app settings .BuildAsync(cancellationToken); var instances = new AggregatorInstances(context.Azure, context.Logger); var instance = new InstanceName(Name, ResourceGroup); bool ok = await instances.AddAsync(instance, Location, RequiredVersion, cancellationToken); return(ok ? 0 : 1); }
internal override async Task <int> RunAsync() { var context = await Context .WithAzureLogon() .WithVstsLogon() // need the token, so we can save it in the app settings .Build(); var instances = new AggregatorInstances(context.Azure, context.Logger); var instance = new InstanceName(Name); bool ok = await instances.Add(instance, Location); return(ok ? 0 : 1); }
private async Task <int> ListByLocationAsync(CommandContext context, AggregatorInstances instances, CancellationToken cancellationToken) { var found = await instances.ListByLocationAsync(Location, cancellationToken); bool any = false; foreach (var dataObject in found) { context.Logger.WriteOutput(dataObject); any = true; } if (!any) { context.Logger.WriteInfo($"No aggregator instances found in {Location}."); } return(0); }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .BuildAsync(cancellationToken); var instance = new InstanceName(Instance, ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger); var boards = new Boards(context.Devops, context.Logger); var streamTask = instances.StreamLogsAsync(instance, lastLinePattern: this.LastLinePattern, cancellationToken: cancellationToken); int id = await boards.CreateWorkItemAsync(this.Project, this.Title, cancellationToken); streamTask.Wait(cancellationToken); return(id > 0 ? 0 : 1); }
private static async Task <int> ListAllAsync(CommandContext context, AggregatorInstances instances) { var found = await instances.ListAllAsync(); bool any = false; foreach (var item in found) { context.Logger.WriteOutput( item, (data) => $"Instance {item.name} in {item.region} region"); any = true; } if (!any) { context.Logger.WriteInfo("No aggregator instances found."); } return(0); }
private async Task <int> ListInResourceGroupAsync(CommandContext context, AggregatorInstances instances) { var found = await instances.ListInResourceGroupAsync(ResourceGroup); bool any = false; foreach (var name in found) { context.Logger.WriteOutput( name, (data) => $"Instance {name}"); any = true; } if (!any) { context.Logger.WriteInfo("No aggregator instances found."); } return(0); }
private async Task <int> ListByLocationAsync(CommandContext context, AggregatorInstances instances) { var found = await instances.ListByLocationAsync(Location); bool any = false; foreach (var name in found) { context.Logger.WriteOutput( name, (data) => $"Instance {name}"); any = true; } if (!any) { context.Logger.WriteInfo($"No aggregator instances found in {Location}."); } return(0); }
private async Task <int> ListInResourceGroupAsync(CommandContext context, AggregatorInstances instances, CancellationToken cancellationToken) { var found = await instances.ListInResourceGroupAsync(ResourceGroup, cancellationToken); bool any = false; foreach (var dataObject in found) { context.Logger.WriteOutput(dataObject); any = true; } if (!any) { context.Logger.WriteInfo($"No aggregator instances found in {ResourceGroup} Resource Group."); } return(ExitCodes.Success); }
internal override async Task <int> RunAsync() { var context = await Context .WithAzureLogon() .Build(); var instances = new AggregatorInstances(context.Azure, context.Logger); if (string.IsNullOrEmpty(Location)) { context.Logger.WriteVerbose($"Searching aggregator instances in subscription..."); return(await ListAllAsync(context, instances)); } else { context.Logger.WriteVerbose($"Searching aggregator instances in {Location}..."); return(await ListByLocationAsync(context, instances)); } }
internal async Task <bool> RemoveAsync(InstanceName instance, string name) { var kudu = new KuduApi(instance, azure, logger); var instances = new AggregatorInstances(azure, logger); // 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}")) using (var response = await client.SendAsync(request)) { bool ok = response.IsSuccessStatusCode; if (!ok) { logger.WriteError($"Failed removing Function {name} from {instance.PlainName} with {response.ReasonPhrase}"); } return(ok); } }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .BuildAsync(cancellationToken); var instance = context.Naming.Instance(Name, ResourceGroup); if (!Mappings) { var mappings = new AggregatorMappings(context.Devops, context.Azure, context.Logger, context.Naming); _ = await mappings.RemoveInstanceAsync(instance); } var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); var ok = await instances.RemoveAsync(instance, Location); return(ok ? 0 : 1); }
internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() .BuildAsync(cancellationToken); var instance = context.Naming.Instance(Instance, ResourceGroup); var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); var boards = new Boards(context.Devops, context.Logger); int id = await boards.CreateWorkItemAsync(this.Project, this.Title, cancellationToken); // wait for the Event to be processed in AzDO, sent via WebHooks, and the Function to run Thread.Sleep(new TimeSpan(0, 2, 0)); await instances.ReadLogAsync(instance, this.RuleName, -1, cancellationToken : cancellationToken); return(id > 0 ? 0 : 1); }
private static async Task <int> ListAllAsync(CommandContext context, AggregatorInstances instances, CancellationToken cancellationToken) { var found = await instances.ListAllAsync(cancellationToken); bool any = false; foreach (var dataObject in found) { context.Logger.WriteOutput(dataObject); any = true; } if (!any) { context.Logger.WriteInfo("No aggregator instances found."); return(ExitCodes.NotFound); } else { return(ExitCodes.Success); } }
// TODO add --swap.slot to support App Service Deployment Slots internal override async Task <int> RunAsync() { var context = await Context .WithAzureLogon() .WithDevOpsLogon() // need the token, so we can save it in the app settings .Build(); var instances = new AggregatorInstances(context.Azure, context.Logger); var instance = new InstanceName(Name, ResourceGroup); bool ok = false; if (Authentication) { ok = await instances.ChangeAppSettings(instance, Location, SaveMode); } else { context.Logger.WriteError($"Unsupported command option(s)"); } return(ok ? 0 : 1); }
// TODO add --swap.slot to support App Service Deployment Slots internal override async Task <int> RunAsync(CancellationToken cancellationToken) { var context = await Context .WithAzureLogon() .WithDevOpsLogon() // need the token, so we can save it in the app settings .BuildAsync(cancellationToken); var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming); var instance = context.Naming.Instance(Name, ResourceGroup); if (Authentication) { bool ok = await instances.ChangeAppSettingsAsync(instance, Location, SaveMode, cancellationToken); return(ok ? ExitCodes.Success : ExitCodes.Failure); } else { context.Logger.WriteError($"Unsupported command option(s)"); return(ExitCodes.InvalidArguments); } }
internal override async Task <int> RunAsync() { var context = await Context .WithAzureLogon() .WithVstsLogon() .Build(); var instance = new InstanceName(Name); bool ok; if (!Mappings) { var mappings = new AggregatorMappings(context.Vsts, context.Azure, context.Logger); ok = await mappings.RemoveInstanceAsync(instance); } var instances = new AggregatorInstances(context.Azure, context.Logger); ok = await instances.Remove(instance, Location); return(ok ? 0 : 1); }
private async Task <bool> UploadRuleFiles(InstanceName instance, string name, string baseDirPath) { /* * PUT /api/vfs/{path} * Puts a file at path. * * PUT /api/vfs/{path}/ * Creates a directory at path. The path can be nested, e.g. `folder1/folder2`. * * Note: when updating or deleting a file, ETag behavior will apply. You can pass a If-Match: "*" header to disable the ETag check. */ var kudu = new KuduApi(instance, azure, logger); string relativeUrl = $"api/vfs/site/wwwroot/{name}/"; var instances = new AggregatorInstances(azure, logger); using (var client = new HttpClient()) { bool exists = false; // check if function already exists using (var request = await kudu.GetRequestAsync(HttpMethod.Head, relativeUrl)) { logger.WriteVerbose($"Checking if function {name} already exists in {instance.PlainName}..."); using (var response = await client.SendAsync(request)) { exists = response.IsSuccessStatusCode; } } if (!exists) { logger.WriteVerbose($"Creating function {name} in {instance.PlainName}..."); using (var request = await kudu.GetRequestAsync(HttpMethod.Put, relativeUrl)) { using (var response = await client.SendAsync(request)) { bool ok = response.IsSuccessStatusCode; if (!ok) { logger.WriteError($"Upload failed with {response.ReasonPhrase}"); return(ok); } } } logger.WriteInfo($"Function {name} created."); } var files = Directory.EnumerateFiles(baseDirPath, "*", SearchOption.AllDirectories); foreach (var file in files) { logger.WriteVerbose($"Uploading {Path.GetFileName(file)} to {instance.PlainName}..."); string fileUrl = $"{relativeUrl}{Path.GetFileName(file)}"; using (var request = await kudu.GetRequestAsync(HttpMethod.Put, fileUrl)) { //HACK -> request.Headers.IfMatch.Add(new EntityTagHeaderValue("*", false)); <- won't work request.Headers.Add("If-Match", "*"); request.Content = new StringContent(File.ReadAllText(file)); using (var response = await client.SendAsync(request)) { bool ok = response.IsSuccessStatusCode; if (!ok) { logger.WriteError($"Failed uploading {file} with {response.ReasonPhrase}"); return(ok); } } } logger.WriteInfo($"{Path.GetFileName(file)} uploaded to {instance.PlainName}."); }//for } return(true); }