internal async Task <bool> Add(InstanceName instance, string location) { string rgName = instance.ResourceGroupName; if (!await azure.ResourceGroups.ContainAsync(rgName)) { logger.WriteVerbose($"Creating resource group {rgName}"); await azure.ResourceGroups .Define(rgName) .WithRegion(location) .CreateAsync(); logger.WriteInfo($"Resource group {rgName} created."); } // TODO the template should create a Storage account and/or a Key Vault 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 templateParams = new Dictionary <string, Dictionary <string, object> > { { "appName", new Dictionary <string, object> { { "value", appName } } } }; string deploymentName = SdkContext.RandomResourceName("aggregator", 24); logger.WriteInfo($"Started deployment {deploymentName}"); var deployment = await azure.Deployments.Define(deploymentName) .WithExistingResourceGroup(rgName) .WithTemplate(armTemplateString) .WithParameters(templateParams) .WithMode(DeploymentMode.Incremental) .CreateAsync(); // 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(); } logger.WriteInfo($"Deployment {deployment.ProvisioningState}"); // check runtime package logger.WriteVerbose($"Checking runtime package version"); var package = new FunctionRuntimePackage(); string zipPath = package.RuntimePackageFile; (string rel_name, DateTimeOffset? rel_when, string rel_url) = await package.FindVersion(); logger.WriteVerbose($"Downloading runtime package {rel_name}"); await package.Download(rel_url); logger.WriteInfo($"Runtime package downloaded."); // upload logger.WriteVerbose($"Uploading runtime package to {instance.DnsHostName}"); bool ok = await package.UploadRuntimeZip(instance, azure, logger); if (ok) { logger.WriteInfo($"Runtime package uploaded to {instance.PlainName}."); // TODO requires VSTS logon!!!!!!!!! var vstsLogonData = VstsLogon.Load().connection; if (vstsLogonData.Mode == VstsTokenType.PAT) { logger.WriteVerbose($"Saving VSTS token"); ok = await ChangeAppSettings(instance, vstsLogonData); logger.WriteInfo($"VSTS token saved"); } else { logger.WriteWarning($"VSTS token type {vstsLogonData.Mode} is unsupported"); ok = false; } } return(ok); }
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); }
public static int Main(string[] args) { var mainTimer = new Stopwatch(); mainTimer.Start(); var save = Console.ForegroundColor; Telemetry.TrackEvent("CLI Start"); using (var cancellationTokenSource = new CancellationTokenSource()) { void cancelEventHandler(object sender, ConsoleCancelEventArgs e) { // call methods to clean up Console.ForegroundColor = save; if (!cancellationTokenSource.IsCancellationRequested) { cancellationTokenSource.Cancel(); } } Console.CancelKeyPress += cancelEventHandler; var cancellationToken = cancellationTokenSource.Token; bool versionCheckEnabled = !EnvironmentVariables.GetAsBool("AGGREGATOR_NEW_VERSION_CHECK_DISABLED", false); if (versionCheckEnabled) { var tempLogger = new ConsoleLogger(false); var verChecker = new FunctionRuntimePackage(tempLogger); (bool upgrade, string newversion) = verChecker.IsCliUpgradable().Result; if (upgrade) { // bug user tempLogger.WriteWarning($"A new version ({newversion}) of Aggregator CLI is available, please upgrade."); } } var parser = new Parser(settings => { settings.CaseSensitive = false; // fails see https://github.com/commandlineparser/commandline/issues/198 settings.CaseInsensitiveEnumValues = true; }); var types = new Type[] { typeof(CreateTestCommand), typeof(CleanupTestCommand), typeof(LogonAzureCommand), typeof(LogonDevOpsCommand), typeof(LogoffCommand), typeof(LogonEnvCommand), typeof(ListInstancesCommand), typeof(InstallInstanceCommand), typeof(UpdateInstanceCommand), typeof(UninstallInstanceCommand), typeof(ConfigureInstanceCommand), typeof(StreamLogsCommand), typeof(ListRulesCommand), typeof(AddRuleCommand), typeof(RemoveRuleCommand), typeof(ConfigureRuleCommand), typeof(UpdateRuleCommand), typeof(InvokeRuleCommand), typeof(ListMappingsCommand), typeof(MapRuleCommand), typeof(UnmapRuleCommand), typeof(UpdateMappingsCommand), typeof(MapLocalRuleCommand) }; var parserResult = parser.ParseArguments(args, types); int rc = ExitCodes.Unexpected; parserResult .WithParsed <CreateTestCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <CleanupTestCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <LogonAzureCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <LogonDevOpsCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <LogoffCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <LogonEnvCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <ListInstancesCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <InstallInstanceCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <UpdateInstanceCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <UninstallInstanceCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <ConfigureInstanceCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <ListRulesCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <AddRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <RemoveRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <ConfigureRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <StreamLogsCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <UpdateRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <InvokeRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <ListMappingsCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <MapRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <UnmapRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <UpdateMappingsCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithParsed <MapLocalRuleCommand>(cmd => rc = cmd.Run(cancellationToken)) .WithNotParsed(errs => { var helpText = HelpText.AutoBuild(parserResult); Console.Error.Write(helpText); rc = ExitCodes.InvalidArguments; }); mainTimer.Stop(); Telemetry.TrackEvent("CLI End", null, new Dictionary <string, double> { { "RunDuration", mainTimer.ElapsedMilliseconds } }); Telemetry.Shutdown(); Console.ForegroundColor = save; Console.CancelKeyPress -= cancelEventHandler; return(rc); } }
private static async Task <Dictionary <string, string> > UpdateDefaultFilesAsync(FunctionRuntimePackage package) { var uploadFiles = new Dictionary <string, string>(); using (var archive = System.IO.Compression.ZipFile.OpenRead(package.RuntimePackageFile)) { var entry = archive.Entries .Single(e => string.Equals("aggregator-function.dll", e.Name, StringComparison.OrdinalIgnoreCase)); using (var assemblyStream = entry.Open()) { await uploadFiles.AddFunctionDefaultFiles(assemblyStream); } } //TODO handle FileNotFound Exception when trying to get resource content, and resource not found return(uploadFiles); }