Esempio n. 1
0
 /// <inheritdoc/>
 public void WriteYaml(IEmitter emitter, object value, Type type)
 {
     emitter.Emit(new Scalar(null, NeonHelper.EnumToString(type, value)));
 }
Esempio n. 2
0
        /// <summary>
        /// Returns the serialized YAML value for a <see cref="JValue"/>.
        /// </summary>
        /// <param name="jValue">The value.</param>
        /// <returns>The serialized value.</returns>
        private static string GetYamlValue(JValue jValue)
        {
            switch (jValue.Type)
            {
            case JTokenType.Boolean:

                return(NeonHelper.ToBoolString((bool)jValue.Value));

            case JTokenType.Integer:
            case JTokenType.Float:

                return(jValue.ToString());

            case JTokenType.String:

                // Strings are a bit tricky:
                //
                //      * Strings like "true", "yes", "on", "false", "no", or "off" will be single quoted so they won't
                //        be misinterpreted as booleans.
                //
                //      * Strings like "1234" or "123.4" that parse as a number will be single quoted so they
                //        won't be misinterpreted as a number.
                //
                //      * Strings with special characters will to be double quoted with appropriate escapes.
                //
                //      * Otherwise, we can render the string without quotes.

                var value = (string)jValue.Value;

                // Handle reserved booleans.

                switch (value.ToLowerInvariant())
                {
                case "true":
                case "false":
                case "yes":
                case "no":
                case "on":
                case "off":

                    return($"'{value}'");
                }

                // Handle strings that parse to a number.

                if (double.TryParse(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var number))
                {
                    return($"'{value}'");
                }

                // Handle strings with special characters.

                if (value.IndexOfAny(specialYamlChars) != -1)
                {
                    var sb = new StringBuilder();

                    sb.Append("\"");

                    foreach (var ch in value)
                    {
                        switch (ch)
                        {
                        case '\r':

                            sb.Append("\\r");
                            break;

                        case '\n':

                            sb.Append("\\n");
                            break;

                        case '\'':

                            sb.Append("'");
                            break;

                        case '"':

                            sb.Append("\\\"");
                            break;

                        default:

                            sb.Append(ch);
                            break;
                        }
                    }

                    sb.Append("\"");

                    return(sb.ToString());
                }

                // We dont need to quote the string.

                return(value);

            case JTokenType.Guid:
            case JTokenType.TimeSpan:
            case JTokenType.Uri:

                return($"\"{jValue.Value}\"");

            case JTokenType.Null:

                return("null");

            default:

                throw new NotImplementedException();
            }
        }
Esempio n. 3
0
        /// <summary>
        /// <para>
        /// Returns a dictionary mapping optional Windows feature names to a <see cref="WindowsFeatureStatus"/>
        /// indicating feature installation status.
        /// </para>
        /// <note>
        /// This method requires elevated permissions.
        /// </note>
        /// </summary>
        /// <returns>The feature dictionary.</returns>
        /// <exception cref="InvalidOperationException">Thrown when not running on Windows.</exception>
        /// <exception cref="ExecuteException">Thrown when the current process doesn't have elevated permissions.</exception>
        /// <remarks>
        /// <note>
        /// The feature names are in English and the lookup is case-insensitive.
        /// </note>
        /// </remarks>
        public static Dictionary <string, WindowsFeatureStatus> GetWindowsOptionalFeatures()
        {
            EnsureWindows();

            // We're going to use the DSIM.EXE app to list these.  The table output
            // will look like:
            //
            //      Deployment Image Servicing and Management tool
            //      Version: 10.0.19041.844
            //
            //      Image Version: 10.0.19042.1083
            //
            //      Features listing for package : Microsoft-Windows-Foundation-Package~31bf3856ad364e35~amd64~~10.0.19041.1
            //
            //
            //      ------------------------------------------- | --------
            //      Feature Name                                | State
            //      ------------------------------------------- | --------
            //      Printing - PrintToPDFServices-Features      | Enabled
            //      Printing - XPSServices-Features             | Enabled
            //      TelnetClient                                | Disabled
            //      TFTP                                        | Disabled
            //      LegacyComponents                            | Disabled
            //      DirectPlay                                  | Disabled
            //      Printing-Foundation-Features                | Enabled
            //      Printing-Foundation-InternetPrinting-Client | Enabled
            //
            //      ...
            //
            // We're simply going to parse the lines with a pipe ("|").

            var response = NeonHelper.ExecuteCapture("dism.exe",
                                                     new object[]
            {
                "/Online",
                "/English",
                "/Get-Features",
                "/Format:table"
            });

            response.EnsureSuccess();

            var featureMap = new Dictionary <string, WindowsFeatureStatus>(StringComparer.InvariantCultureIgnoreCase);

            foreach (var line in response.OutputText.ToLines())
            {
                if (!line.StartsWith("----") && line.Contains('|'))
                {
                    var fields = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                    var status = WindowsFeatureStatus.Unknown;

                    fields[0] = fields[0].Trim();
                    fields[1] = fields[1].Trim();

                    if (fields[0] == "Feature Name")
                    {
                        continue;   // Ignore column headers
                    }

                    switch (fields[1].ToLowerInvariant())
                    {
                    case "disabled":

                        status = WindowsFeatureStatus.Disabled;
                        break;

                    case "enabled":

                        status = WindowsFeatureStatus.Enabled;
                        break;

                    case "enable pending":

                        status = WindowsFeatureStatus.EnabledPending;
                        break;
                    }

                    featureMap[fields[0]] = status;
                }
            }

            return(featureMap);
        }
Esempio n. 4
0
 /// <summary>
 /// Attempts to parse an environment variable as an eumeration, writting messages
 /// to the associated logger if one was passed to the constructor.
 /// </summary>
 /// <typeparam name="TEnum">The desired enumeration type.</typeparam>
 /// <param name="variable">The variable name.</param>
 /// <param name="defaultInput">The default value.</param>
 /// <param name="required">Optionally specifies that the variable is required to exist.</param>
 /// <param name="validator">
 /// Optional validation function to be called to verify that the parsed variable
 /// value is valid.  This should return <c>null</c> for valid values and an error
 /// message for invalid ones.
 /// </param>
 /// <returns>The parsed value.</returns>
 /// <exception cref="KeyNotFoundException">Thrown if the variable does not exists and <paramref name="required"/>=<c>true</c>.</exception>
 /// <exception cref="FormatException">Thrown if the variable could not be parsed or the <paramref name="validator"/> returned an error.</exception>
 public TEnum Get <TEnum>(string variable, TEnum defaultInput, bool required = false, Validator <TEnum> validator = null)
     where TEnum : Enum
 {
     return(Parse <TEnum>(variable, NeonHelper.EnumToString(defaultInput), EnumParser, required, validator));
 }
Esempio n. 5
0
        /// <summary>
        /// Starts a process to run an executable file and then waits for the process to terminate.
        /// </summary>
        /// <param name="path">Path to the executable file.</param>
        /// <param name="args">Command line arguments (or <c>null</c>).</param>
        /// <param name="timeout">
        /// Optional maximum time to wait for the process to complete or <c>null</c> to wait
        /// indefinitely.
        /// </param>
        /// <param name="process">
        /// The optional <see cref="Process"/> instance to use to launch the process.
        /// </param>
        /// <returns>The process exit code.</returns>
        /// <exception cref="TimeoutException">Thrown if the process did not exit within the <paramref name="timeout"/> limit.</exception>
        /// <remarks>
        /// <note>
        /// If <paramref name="timeout"/> is exceeded and execution has not commpleted in time
        /// then a <see cref="TimeoutException"/> will be thrown and the process will be killed
        /// if it was created by this method.  Process instances passed via the <paramref name="process"/>
        /// parameter will not be killed in this case.
        /// </note>
        /// </remarks>
        public static int Execute(string path, string args, TimeSpan?timeout = null, Process process = null)
        {
            var processInfo   = new ProcessStartInfo(GetProgramPath(path), args ?? string.Empty);
            var killOnTimeout = process == null;

            if (process == null)
            {
                process = new Process();
            }

            try
            {
                processInfo.UseShellExecute        = false;
                processInfo.CreateNoWindow         = true;
                processInfo.RedirectStandardError  = true;
                processInfo.RedirectStandardOutput = true;
                processInfo.WorkingDirectory       = Environment.CurrentDirectory;

                process.StartInfo           = processInfo;
                process.EnableRaisingEvents = true;

                // Configure the sub-process STDOUT and STDERR streams to use
                // code page 1252 which simply passes byte values through.

                processInfo.StandardErrorEncoding      =
                    processInfo.StandardOutputEncoding = ByteEncoding.Instance;

                // Relay STDOUT and STDERR output from the child process
                // to this process's STDOUT and STDERR streams.

                // $todo(jefflill):
                //
                // This won't work properly for binary data streaming
                // back from the process because we're not going to be
                // sure whether the "line" was terminated by a CRLF or
                // just a CR.  I'm not sure if there's a clean way to
                // address this in .NET code.
                //
                //      https://github.com/nforgeio/neonKUBE/issues/461

                var stdErrClosed = false;
                var stdOutClosed = false;

                process.ErrorDataReceived +=
                    (s, a) =>
                {
                    if (a.Data == null)
                    {
                        stdErrClosed = true;
                    }
                    else
                    {
                        Console.Error.WriteLine(a.Data);
                    }
                };

                process.OutputDataReceived +=
                    (s, a) =>
                {
                    if (a.Data == null)
                    {
                        stdOutClosed = true;
                    }
                    else
                    {
                        Console.Out.WriteLine(a.Data);
                    }
                };

                process.Start();
                process.BeginErrorReadLine();
                process.BeginOutputReadLine();

                if (!timeout.HasValue || timeout.Value >= TimeSpan.FromDays(1))
                {
                    NeonHelper.WaitFor(() => stdErrClosed && stdOutClosed, timeout: timeout ?? TimeSpan.FromDays(365), pollTime: TimeSpan.FromMilliseconds(250));
                    process.WaitForExit();
                }
                else
                {
                    NeonHelper.WaitFor(() => stdErrClosed && stdOutClosed, timeout: timeout.Value, pollTime: TimeSpan.FromMilliseconds(250));
                    process.WaitForExit((int)timeout.Value.TotalMilliseconds);

                    if (!process.HasExited)
                    {
                        if (killOnTimeout)
                        {
                            process.Kill();
                        }

                        throw new TimeoutException(string.Format("Process [{0}] execute has timed out.", path));
                    }
                }

                return(process.ExitCode);
            }
            finally
            {
                process.Dispose();
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Generates a 13 digit base-36 UUID including only lowercase ASCII
        /// characters and digits.  This is useful for generating unique shorter
        /// names for Kubernetes objects etc.
        /// </summary>
        /// <param name="secure">
        /// Optionally specifies that a cryptographically secure algorithm is
        /// is used to generate the UUID, rather than the default pseudo random
        /// generator.  Cryptogrphically secure algorithms consume system entropy.
        /// </param>
        /// <returns>The new base-36 string.</returns>
        public static string CreateBase36Uuid(bool secure = true)
        {
            ulong value;

            // Generate a non-zero random long.

            do
            {
                byte[] randomBytes;

                if (secure)
                {
                    randomBytes = NeonHelper.GetCryptoRandomBytes(8);
                }
                else
                {
                    randomBytes = NeonHelper.PseudoRandomBytes(8);
                }

                value = 0;

                foreach (var b in randomBytes)
                {
                    value = (value * 256) + b;
                }
            }while (value == 0);

            // Convert the value into a base-36 string.

            var sb = new StringBuilder();

            while (value > 0)
            {
                var digit = (int)(value % 36);

                if (digit < 10)
                {
                    sb.Append((char)('0' + digit));
                }
                else
                {
                    sb.Append((char)('a' + (digit - 10)));
                }

                value /= 36;
            }

            var result = sb.ToString();

            // We may end up up with more than 13 digits because 72-bit values
            // have more possible values than 13 digit base-36 numbers.  We'll
            // just take the last 13 digits just in case.
            //
            // We may also end up with fewer than 13 digits when the random number
            // generated is very small (should be quite rare).  We'll prepend zero
            // digits in this case.

            if (result.Length > 13)
            {
                result = result.Substring(result.Length - 13);
            }
            else if (result.Length < 13)
            {
                result = new string('0', 13 - result.Length) + result;
            }

            return(result);
        }