/// <summary> /// Expands any environment variables of the form <b>${NAME}</b> in the input /// string and returns the expanded result. /// </summary> /// <param name="input">The input string.</param> /// <returns>The expanded output string.</returns> private string ExpandEnvironmentVars(string input) { Covenant.Requires <ArgumentNullException>(input != null); using (var reader = new PreprocessReader(input)) { reader.VariableExpansionRegex = PreprocessReader.CurlyVariableExpansionRegex; // Load the environment variables. foreach (DictionaryEntry item in Environment.GetEnvironmentVariables()) { // $hack(jeff.lill): // // Some common Windows enmvironment variables names include characters // like parens that are not compatible with PreprocessReader. We're // just going to catch the exceptions and ignore these. var key = (string)item.Key; if (PreprocessReader.VariableValidationRegex.IsMatch(key)) { reader.Set(key, (string)item.Value); } } // Perform the substitutions. return(reader.ReadToEnd()); } }
/// <summary> /// Reads the script text from a file path, replacing any variable references with the /// variable's value. /// </summary> /// <param name="scriptPath">The script file path.</param> /// <returns>The processed script text.</returns> private string LoadScript(string scriptPath) { using (var reader = new StreamReader(scriptPath)) { using (var preprocessReader = new PreprocessReader(reader, variables)) { return(preprocessReader.ReadToEnd()); } } }
/// <summary> /// Reads the script text from an embedded resource file, replacing any variable references with the /// variable's value. /// </summary> /// <param name="scriptFile">The embedded resurce script file.</param> /// <returns>The processed script text.</returns> private string LoadScript(IStaticFile scriptFile) { using (var reader = scriptFile.OpenReader()) { using (var preprocessReader = new PreprocessReader(reader, variables)) { return(preprocessReader.ReadToEnd()); } } }
/// <summary> /// Reads the script text from an embedded resource file, replacing any variable references with the /// variable's value. /// </summary> /// <param name="scriptFile">The embedded resurce script file.</param> /// <returns>The processed script text.</returns> private string LoadScript(IStaticFile scriptFile) { using (var reader = scriptFile.OpenReader()) { using (var preprocessReader = new PreprocessReader(reader, variables)) { preprocessReader.VariableExpansionRegex = PreprocessReader.CurlyVariableExpansionRegex; return(preprocessReader.ReadToEnd()); } } }
/// <summary> /// Reads the script text from a file path, replacing any variable references with the /// variable's value. /// </summary> /// <param name="scriptPath">The script file path.</param> /// <returns>The processed script text.</returns> private string LoadScript(string scriptPath) { using (var reader = new StreamReader(scriptPath)) { using (var preprocessReader = new PreprocessReader(reader, variables)) { preprocessReader.VariableExpansionRegex = PreprocessReader.CurlyVariableExpansionRegex; return(preprocessReader.ReadToEnd()); } } }
/// <summary> /// Parses a hive definition from JSON text. /// </summary> /// <param name="json">The JSON text.</param> /// <param name="strict">Optionally require that all input properties map to <see cref="HiveDefinition"/> properties.</param> /// <returns>The parsed <see cref="HiveDefinition"/>.</returns> /// <remarks> /// <note> /// The source is first preprocessed using <see cref="PreprocessReader"/> /// and then is parsed as JSON. /// </note> /// </remarks> public static HiveDefinition FromJson(string json, bool strict = false) { Covenant.Requires <ArgumentNullException>(json != null); using (var stringReader = new StringReader(json)) { using (var preprocessReader = new PreprocessReader(stringReader)) { return(NeonHelper.JsonDeserialize <HiveDefinition>(preprocessReader.ReadToEnd(), strict: strict)); } } }
public void Comments_CustomMarkers() { // Verify that we can handle multiple custom comment prefixes. var input = @"# This is a comment # This is a comment This is a test // This is a comment of the emergency # not a comment broadcasting system # a ab abc "; var expected = @" This is a test of the emergency # not a comment broadcasting system a ab abc "; using (var reader = new PreprocessReader(input)) { reader.ClearCommentMarkers(); reader.AddCommentMarker("#"); reader.AddCommentMarker("//"); var output = reader.ReadToEnd(); Assert.Equal(expected, output); } }
public void DisableStatements() { const string input = @"# line1 line2 # line3 "; using (var reader = new PreprocessReader(input) { LineEnding = LineEnding.CRLF }) { reader.ProcessStatements = false; Assert.Equal("# line1\r\nline2\r\n# line3\r\n", reader.ReadToEnd()); } }
public async Task LineEndings() { const string input = @"line1 line2 line3 "; using (var reader = new PreprocessReader(input) { LineEnding = LineEnding.CRLF }) { Assert.Equal("line1\r\nline2\r\nline3\r\n", reader.ReadToEnd()); } using (var reader = new PreprocessReader(input) { LineEnding = LineEnding.LF }) { Assert.Equal("line1\nline2\nline3\n", reader.ReadToEnd()); } using (var reader = new PreprocessReader(input) { LineEnding = LineEnding.CRLF }) { Assert.Equal("line1\r\nline2\r\nline3\r\n", await reader.ReadToEndAsync()); } using (var reader = new PreprocessReader(input) { LineEnding = LineEnding.LF }) { Assert.Equal("line1\nline2\nline3\n", await reader.ReadToEndAsync()); } }
/// <summary> /// Parses a hive definition from a file. /// </summary> /// <param name="path">The file path.</param> /// <param name="strict">Optionally require that all input properties map to <see cref="HiveDefinition"/> properties.</param> /// <returns>The parsed <see cref="HiveDefinition"/>.</returns> /// <exception cref="ArgumentException">Thrown if the definition is not valid.</exception> /// <remarks> /// <note> /// The source is first preprocessed using <see cref="PreprocessReader"/> /// and then is parsed as JSON. /// </note> /// </remarks> public static HiveDefinition FromFile(string path, bool strict = false) { Covenant.Requires <ArgumentNullException>(path != null); using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (var stringReader = new StreamReader(stream)) { using (var preprocessReader = new PreprocessReader(stringReader)) { var hiveDefinition = NeonHelper.JsonDeserialize <HiveDefinition>(preprocessReader.ReadToEnd(), strict: strict); if (hiveDefinition == null) { throw new ArgumentException($"Invalid hive definition in [{path}]."); } // Populate the [node.Name] properties from the dictionary name. foreach (var item in hiveDefinition.NodeDefinitions) { var node = item.Value; if (string.IsNullOrEmpty(node.Name)) { node.Name = item.Key; } else if (item.Key != node.Name) { throw new FormatException($"The node names don't match [\"{item.Key}\" != \"{node.Name}\"]."); } } hiveDefinition.Validate(); return(hiveDefinition); } } } }
/// <inheritdoc/> public override async Task RunAsync(CommandLine commandLine) { if (commandLine.HasHelpOption) { Console.WriteLine(usage); Program.Exit(0); } var splitCommandLine = commandLine.Split("--"); var leftCommandLine = splitCommandLine.Left; var rightCommandLine = splitCommandLine.Right; if (rightCommandLine == null || rightCommandLine.Arguments.Length == 0) { Console.Error.WriteLine("*** ERROR: Expected a command after a [--] argument."); Program.Exit(1); } // All arguments on the left command line should be VARIABLES files. // We're going to open each of these and set any enviroment variables // like [NAME=VALUE] we find. // // Note that these files may be encrypted. If any are, we'll decrypt // to a temporary file before we read them. foreach (var path in leftCommandLine.Arguments) { if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } DecryptWithAction(path, decryptedPath => { var lineNumber = 1; foreach (var line in File.ReadAllLines(decryptedPath)) { var trimmed = line.Trim(); if (line == string.Empty || line.StartsWith("#")) { continue; } var fields = line.Split('=', 2); if (fields.Length != 2 || fields[0] == string.Empty) { Console.Error.WriteLine($"*** ERROR: [{path}:{lineNumber}] is not formatted like: NAME=VALUE"); Program.Exit(1); } var name = fields[0].Trim(); var value = fields[1].Trim(); Environment.SetEnvironmentVariable(name, value); lineNumber++; } }); } // Any left command line options with a "--" prefix also specify environment variables. foreach (var option in leftCommandLine.Options.Where(o => o.Key.StartsWith("--"))) { Environment.SetEnvironmentVariable(option.Key.Substring(2), option.Value); } // We've read all of the variable files and left command line options // and initialized all environment variables. Now we need to process // and then execute the right command line. var tempFiles = new List <TempFile>(); try { var subcommand = rightCommandLine.Items; // Note that the first element of the subcommand specifies the // executable so we don't need to process that. for (int i = 1; i < subcommand.Length; i++) { var arg = subcommand[i]; if (arg.StartsWith("_...")) { // Argument is a reference to a potentially encrypted // file that needs to be passed decrypted. var path = arg.Substring(4); if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } if (NeonVault.IsEncrypted(path)) { var tempFile = new TempFile(); tempFiles.Add(tempFile); vault.Decrypt(path, tempFile.Path); path = tempFile.Path; } subcommand[i] = path; } else if (arg.StartsWith("_..")) { // Argument is a reference to a potentially encrypted text file // with environment variable references we'll need to update. var path = arg.Substring(3); if (!File.Exists(path)) { Console.Error.WriteLine($"*** ERROR: File [{path}] does not exist."); Program.Exit(1); } if (NeonVault.IsEncrypted(path)) { var tempFile = new TempFile(); tempFiles.Add(tempFile); vault.Decrypt(path, tempFile.Path); path = tempFile.Path; } subcommand[i] = path; // Perform the subsitutions. var unprocessed = File.ReadAllText(path); var processed = string.Empty; var linuxLineEndings = !unprocessed.Contains("\r\n"); using (var reader = new StreamReader(path)) { using (var preprocessor = new PreprocessReader(reader)) { preprocessor.ExpandVariables = true; preprocessor.LineEnding = linuxLineEndings ? LineEnding.LF : LineEnding.CRLF; preprocessor.ProcessStatements = false; preprocessor.StripComments = false; preprocessor.VariableExpansionRegex = PreprocessReader.AngleVariableExpansionRegex; processed = preprocessor.ReadToEnd(); } } File.WriteAllText(path, processed); } else if (arg.StartsWith("_.")) { // Argument is a reference to an environment variable. var name = arg.Substring(2); if (name == string.Empty) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid."); Program.Exit(1); } var value = Environment.GetEnvironmentVariable(name); if (value == null) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable."); Program.Exit(2); } subcommand[i] = value; } else if (arg.StartsWith("-")) { // Argument is a command line option. We'll check to see if // it contains a reference to an environment variable. var valuePos = arg.IndexOf("=_."); if (valuePos != -1) { var optionPart = arg.Substring(0, valuePos); var name = arg.Substring(valuePos + 3); if (name == string.Empty) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] is not valid."); Program.Exit(1); } var value = Environment.GetEnvironmentVariable(name); if (value == null) { Console.Error.WriteLine($"*** ERROR: Subcommand argument [{arg}] references an undefined environment variable."); Program.Exit(1); } subcommand[i] = $"{optionPart}={value}"; } } else { // Otherwise, expand any envrionment variable references. subcommand[i] = Environment.ExpandEnvironmentVariables(subcommand[i]); } } // Execute the subcommand. var subcommandArgs = new List <object>(); foreach (var subcommandArg in subcommand) { subcommandArgs.Add(subcommandArg); } var exitCode = NeonHelper.Execute(subcommand[0], subcommandArgs.Skip(1).ToArray()); Program.Exit(exitCode); } finally { foreach (var tempFile in tempFiles) { tempFile.Dispose(); } } Program.Exit(0); await Task.CompletedTask; }
/// <summary> /// Parses a cluster definition from YAML text. /// </summary> /// <param name="yaml">The JSON text.</param> /// <param name="strict">Optionally require that all input properties map to <see cref="ClusterDefinition"/> properties.</param> /// <returns>The parsed <see cref="ClusterDefinition"/>.</returns> /// <remarks> /// <note> /// The source is first preprocessed using <see cref="PreprocessReader"/> /// and then is parsed as YAML. /// </note> /// </remarks> public static ClusterDefinition FromYaml(string yaml, bool strict = false) { Covenant.Requires <ArgumentNullException>(yaml != null, nameof(yaml)); using (var stringReader = new StringReader(yaml)) { using (var preprocessReader = new PreprocessReader(stringReader)) { var clusterDefinition = NeonHelper.YamlDeserialize <ClusterDefinition>(preprocessReader.ReadToEnd(), strict: strict); clusterDefinition.Validate(); return(clusterDefinition); } } }