Exemple #1
0
        /// <summary>
        /// Starts the controller.
        /// </summary>
        /// <param name="k8s">The <see cref="IKubernetes"/> client to use.</param>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        public static async Task StartAsync(IKubernetes k8s)
        {
            Covenant.Requires <ArgumentNullException>(k8s != null, nameof(k8s));
            // Load the configuration settings.

            var leaderConfig =
                new LeaderElectionConfig(
                    k8s,
                    @namespace: KubeNamespace.NeonSystem,
                    leaseName:        $"{Program.Service.Name}.nodetask",
                    identity:         Pod.Name,
                    promotionCounter: Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_promoted", "Leader promotions"),
                    demotionCounter:  Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_demoted", "Leader demotions"),
                    newLeaderCounter: Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_newLeader", "Leadership changes"));

            var options = new ResourceManagerOptions()
            {
                IdleInterval             = Program.Service.Environment.Get("NODETASK_IDLE_INTERVAL", TimeSpan.FromSeconds(1)),
                ErrorMinRequeueInterval  = Program.Service.Environment.Get("NODETASK_ERROR_MIN_REQUEUE_INTERVAL", TimeSpan.FromSeconds(15)),
                ErrorMaxRetryInterval    = Program.Service.Environment.Get("NODETASK_ERROR_MAX_REQUEUE_INTERVAL", TimeSpan.FromSeconds(60)),
                IdleCounter              = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle", "IDLE events processed."),
                ReconcileCounter         = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle", "RECONCILE events processed."),
                DeleteCounter            = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle", "DELETED events processed."),
                StatusModifyCounter      = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle", "STATUS-MODIFY events processed."),
                IdleErrorCounter         = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_idle_error", "Failed NodeTask IDLE event processing."),
                ReconcileErrorCounter    = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_reconcile_error", "Failed NodeTask RECONCILE event processing."),
                DeleteErrorCounter       = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_delete_error", "Failed NodeTask DELETE event processing."),
                StatusModifyErrorCounter = Metrics.CreateCounter($"{Program.Service.MetricsPrefix}nodetask_statusmodify_error", "Failed NodeTask STATUS-MODIFY events processing.")
            };

            resourceManager = new ResourceManager <V1NeonNodeTask, NodeTaskController>(
                k8s,
                options:      options,
                leaderConfig: leaderConfig);

            await resourceManager.StartAsync();
        }
Exemple #2
0
        /// <summary>
        /// Constructor.  Note that you should dispose the instance when you're finished with it.
        /// </summary>
        /// <param name="addressOrFQDN">The target XenServer IP address or FQDN.</param>
        /// <param name="username">The user name.</param>
        /// <param name="password">The password.</param>
        /// <param name="name">Optionally specifies the XenServer name.</param>
        /// <param name="logFolder">
        /// The folder where log files are to be written, otherwise or <c>null</c> or
        /// empty if logging is disabled.
        /// </param>
        public XenClient(string addressOrFQDN, string username, string password, string name = null, string logFolder = null)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(username));

            if (!IPAddress.TryParse(addressOrFQDN, out var address))
            {
                var hostEntry = Dns.GetHostEntry(addressOrFQDN);

                if (hostEntry.AddressList.Length == 0)
                {
                    throw new XenException($"[{addressOrFQDN}] is not a valid IP address or fully qualified domain name of a XenServer host.");
                }

                address = hostEntry.AddressList.First();
            }

            var logWriter = (TextWriter)null;

            if (!string.IsNullOrEmpty(logFolder))
            {
                Directory.CreateDirectory(logFolder);

                logWriter = new StreamWriter(Path.Combine(logFolder, $"XENSERVER-{addressOrFQDN}.log"));
            }

            Address           = addressOrFQDN;
            Name              = name;
            SshProxy          = new SshProxy <XenClient>(addressOrFQDN, null, address, SshCredentials.FromUserPassword(username, password), logWriter);
            SshProxy.Metadata = this;
            runOptions        = RunOptions.IgnoreRemotePath;

            // Initialize the operation classes.

            Repository = new RepositoryOperations(this);
            Template   = new TemplateOperations(this);
            Machine    = new MachineOperations(this);
        }
Exemple #3
0
        /// <summary>
        /// <para>
        /// Plays a playbook within a specific working directory using <b>neon ansible play -- [args] playbook</b>.
        /// </para>
        /// <note>
        /// This method will have Ansible gather facts by default which can be quite slow.
        /// Consider using <see cref="PlayInFolderNoGather(string, string, string[])"/> instead
        /// for unit tests that don't required the facts.
        /// </note>
        /// </summary>
        /// <param name="workDir">The playbook working directory (or <c>null</c> to use a temporary folder).</param>
        /// <param name="playbook">The playbook text.</param>
        /// <param name="args">Optional command line arguments to be included in the command.</param>
        /// <returns>An <see cref="AnsiblePlayResults"/> describing what happened.</returns>
        /// <remarks>
        /// <note>
        /// Use this method for playbooks that need to read or write files.
        /// </note>
        /// </remarks>
        public static AnsiblePlayResults PlayInFolder(string workDir, string playbook, params string[] args)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(playbook));

            if (!string.IsNullOrEmpty(workDir))
            {
                Directory.CreateDirectory(workDir);

                Environment.CurrentDirectory = workDir;
                File.WriteAllText(Path.Combine(workDir, "play.yaml"), playbook);

                var response = NeonHelper.ExecuteCapture("neon", new object[] { "ansible", "play", "--noterminal", "--", args, "-vvvv", "play.yaml" });

                return(new AnsiblePlayResults(response));
            }
            else
            {
                using (var folder = new TempFolder())
                {
                    var orgDirectory = Environment.CurrentDirectory;

                    try
                    {
                        Environment.CurrentDirectory = folder.Path;
                        File.WriteAllText(Path.Combine(folder.Path, "play.yaml"), playbook);

                        var response = NeonHelper.ExecuteCapture("neon", new object[] { "ansible", "play", "--noterminal", "--", args, "-vvvv", "play.yaml" });

                        return(new AnsiblePlayResults(response));
                    }
                    finally
                    {
                        Environment.CurrentDirectory = orgDirectory;
                    }
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="controller">The setup controller.</param>
        internal SetupClusterStatus(ISetupController controller)
        {
            Covenant.Requires <ArgumentNullException>(controller != null, nameof(controller));

            this.isClone      = false;
            this.controller   = controller;
            this.cluster      = controller.Get <ClusterProxy>(KubeSetupProperty.ClusterProxy);
            this.GlobalStatus = controller.GlobalStatus;
            this.globalStatus = this.GlobalStatus;

            // Initialize the cluster node/host status instances.

            this.Nodes = new List <SetupNodeStatus>();

            foreach (var node in cluster.Nodes)
            {
                Nodes.Add(new SetupNodeStatus(node, node.NodeDefinition));
            }

            this.Hosts = new List <SetupNodeStatus>();

            foreach (var host in cluster.Hosts)
            {
                Hosts.Add(new SetupNodeStatus(host, new object()));
            }

            // Initialize the setup steps.

            this.Steps = new List <SetupStepStatus>();

            foreach (var step in controller.GetStepStatus().Where(step => !step.IsQuiet))
            {
                Steps.Add(step);
            }

            this.CurrentStep = Steps.SingleOrDefault(step => step.Number == controller.CurrentStepNumber);
        }
Exemple #5
0
        /// <summary>
        /// Returns the Cadence activity type name to be used for a activity interface or
        /// implementation class.
        /// </summary>
        /// <param name="activityType">The activity interface or implementation type.</param>
        /// <param name="activityAttribute">Specifies the <see cref="ActivityAttribute"/>.</param>
        /// <returns>The type name.</returns>
        /// <remarks>
        /// <para>
        /// If <paramref name="activityAttribute"/> is passed and <see cref="ActivityAttribute.Name"/>
        /// is not <c>null</c> or empty, then the name specified in the attribute is returned.
        /// </para>
        /// <para>
        /// Otherwise, we'll return the fully qualified name of the activity interface
        /// with the leadting "I" removed.
        /// </para>
        /// </remarks>
        internal static string GetActivityTypeName(Type activityType, ActivityAttribute activityAttribute)
        {
            Covenant.Requires <ArgumentNullException>(activityType != null, nameof(activityType));

            if (activityAttribute != null && !string.IsNullOrEmpty(activityAttribute.Name))
            {
                return(activityAttribute.Name);
            }

            if (activityType.IsClass)
            {
                CadenceHelper.ValidateActivityImplementation(activityType);

                activityType = CadenceHelper.GetActivityInterface(activityType);
            }
            else
            {
                CadenceHelper.ValidateActivityInterface(activityType);
            }

            var fullName = activityType.FullName;
            var name     = activityType.Name;

            if (name.StartsWith("I") && name != "I")
            {
                // We're going to strip the leading "I" from the unqualified
                // type name (unless that's the only character).

                fullName  = fullName.Substring(0, fullName.Length - name.Length);
                fullName += name.Substring(1);
            }

            // We need to replace the "+" characters .NET uses for nested types into
            // "." so the result will be a valid C# type identifier.

            return(fullName.Replace('+', '.'));
        }
Exemple #6
0
        /// <summary>
        /// Create the node folders required by neoneKUBE.
        /// </summary>
        /// <param name="controller">The setup controller.</param>
        public void BaseCreateKubeFolders(ISetupController controller)
        {
            Covenant.Requires <ArgumentException>(controller != null, nameof(controller));

            InvokeIdempotent("base/folders",
                             () =>
            {
                controller.LogProgress(this, verb: "create", message: "node folders");

                var folderScript =
                    $@"
set -euo pipefail

mkdir -p {KubeNodeFolder.Bin}
chmod 750 {KubeNodeFolder.Bin}

mkdir -p {KubeNodeFolder.Config}
chmod 750 {KubeNodeFolder.Config}

mkdir -p {KubeNodeFolder.Setup}
chmod 750 {KubeNodeFolder.Setup}

mkdir -p {KubeNodeFolder.Helm}
chmod 750 {KubeNodeFolder.Helm}

mkdir -p {KubeNodeFolder.State}
chmod 750 {KubeNodeFolder.State}

mkdir -p {KubeNodeFolder.State}/setup
chmod 750 {KubeNodeFolder.State}/setup

mkdir -p {KubeNodeFolder.NeonRun}
chmod 740 {KubeNodeFolder.NeonRun}
";
                SudoCommand(CommandBundle.FromScript(folderScript), RunOptions.Defaults | RunOptions.FaultOnError);
            });
        }
Exemple #7
0
        /// <summary>
        /// Performs an HTTP <b>PATCH</b> ensuring that a success code was returned.
        /// </summary>
        /// <param name="uri">The URI</param>
        /// <param name="document">The optional object to be uploaded as the request payload.</param>
        /// <param name="args">The optional query arguments.</param>
        /// <param name="headers">The Optional HTTP headers.</param>
        /// <param name="cancellationToken">The optional <see cref="CancellationToken"/>.</param>
        /// <param name="logActivity">The optional <see cref="LogActivity"/> whose ID is to be included in the request.</param>
        /// <returns>The <see cref="JsonResponse"/>.</returns>
        /// <exception cref="SocketException">Thrown for network connectivity issues.</exception>
        /// <exception cref="HttpException">Thrown when the server responds with an HTTP error status code.</exception>
        public async Task <JsonResponse> PatchAsync(
            string uri,
            object document       = null,
            ArgDictionary args    = null,
            ArgDictionary headers = null,
            CancellationToken cancellationToken = default,
            LogActivity logActivity             = default)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(uri));

            return(await safeRetryPolicy.InvokeAsync(
                       async() =>
            {
                var requestUri = FormatUri(uri, args);

                try
                {
                    var client = this.HttpClient;

                    if (client == null)
                    {
                        throw new ObjectDisposedException(nameof(JsonClient));
                    }

                    var httpResponse = await client.PatchAsync(requestUri, CreateContent(document), cancellationToken: cancellationToken, headers: headers, activity: logActivity);
                    var jsonResponse = new JsonResponse(requestUri, httpResponse, await httpResponse.Content.ReadAsStringAsync());

                    jsonResponse.EnsureSuccess();

                    return jsonResponse;
                }
                catch (HttpRequestException e)
                {
                    throw new HttpException(e, requestUri);
                }
            }));
        }
Exemple #8
0
        /// <summary>
        /// Disables DHCP.
        /// </summary>
        /// <param name="controller">The setup controller.</param>
        public void BaseDisableDhcp(ISetupController controller)
        {
            Covenant.Requires <ArgumentException>(controller != null, nameof(controller));

            var hostingEnvironment = controller.Get <HostingEnvironment>(KubeSetupProperty.HostingEnvironment);

            InvokeIdempotent("base/dhcp",
                             () =>
            {
                controller.LogProgress(this, verb: "disable", message: "dhcp");

                var initNetPlanScript =
                    $@"
set -euo pipefail

rm -rf /etc/netplan/*

cat <<EOF > /etc/netplan/no-dhcp.yaml
# This file is used to disable the network when a new VM is created 
# from a template is booted.  The [neon-init] service handles network
# provisioning in conjunction with the cluster prepare step.
#
# Cluster prepare inserts a virtual DVD disc with a script that
# handles the network configuration which [neon-init] will
# execute.

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
EOF
";
                SudoCommand(CommandBundle.FromScript(initNetPlanScript), RunOptions.Defaults | RunOptions.FaultOnError);
            });
        }
Exemple #9
0
        /// <summary>
        /// Restarts the service unless it never been started.
        /// </summary>
        /// <param name="serviceCreator">Callback that creates and returns the new service instance.</param>
        /// <param name="runningTimeout">
        /// Optionally specifies the maximum time the fixture should wait for the service to transition
        /// to the <see cref="NeonServiceStatus.Running"/> state.  This defaults to <b>30 seconds</b>.
        /// </param>
        /// <exception cref="TimeoutException">
        /// Thrown if the service didn't transition to the running (or terminated) state
        /// within <paramref name="runningTimeout"/>.
        /// </exception>
        /// <remarks>
        /// <para>
        /// This method first calls the <paramref name="serviceCreator"/> callback and expects
        /// it to return a new service instance that has been initialized by setting its environment
        /// variables and configuration files as required.  The callback should not start thge service.
        /// </para>
        /// </remarks>
        public void Restart(Func <TService> serviceCreator = null, TimeSpan runningTimeout = default)
        {
            Covenant.Requires <ArgumentNullException>(serviceCreator != null, nameof(serviceCreator));

            if (Service != null && Service.Status == NeonServiceStatus.NotStarted)
            {
                return;
            }

            if (runningTimeout == default)
            {
                runningTimeout = defaultRunningTimeout;
            }

            TerminateService();
            ClearCaches();

            Service = serviceCreator();
            Covenant.Assert(Service != null);

            serviceTask = Service.RunAsync();

            // Wait for the service to signal that it's running or has terminated.

            try
            {
                NeonHelper.WaitFor(() => Service.Status == NeonServiceStatus.Running || Service.Status == NeonServiceStatus.Terminated, runningTimeout);
            }
            catch (TimeoutException)
            {
                // Throw a nicer exception that explains what's happened in more detail.

                throw new TimeoutException($"Service [{Service.Name}]'s [{typeof(TService).Name}.OnRunAsync()] method did not call [{nameof(NeonService.StartedAsync)}()] within [{runningTimeout}] indicating that the service is ready.  Ensure that [{nameof(NeonService.StartedAsync)}()] is being called or increase the timeout.");
            }

            IsRunning = Service.Status == NeonServiceStatus.Running;
        }
Exemple #10
0
        /// <summary>
        /// Transmits a signal to an external workflow, starting the workflow if it's not currently running.
        /// This low-level method accepts a byte array with the already encoded parameters.
        /// </summary>
        /// <param name="workflowTypeName">The target workflow type name.</param>
        /// <param name="signalName">Identifies the signal.</param>
        /// <param name="signalArgs">Optionally specifies the signal arguments as a byte array.</param>
        /// <param name="startArgs">Optionally specifies the workflow arguments.</param>
        /// <param name="options">Optionally specifies the options to be used for starting the workflow when required.</param>
        /// <returns>The <see cref="WorkflowExecution"/>.</returns>
        /// <exception cref="EntityNotExistsException">Thrown if the domain does not exist.</exception>
        /// <exception cref="BadRequestException">Thrown if the request is invalid.</exception>
        /// <exception cref="InternalServiceException">Thrown for internal Cadence problems.</exception>
        internal async Task <WorkflowExecution> SignalWorkflowWithStartAsync(string workflowTypeName, string signalName, byte[] signalArgs, byte[] startArgs, WorkflowOptions options)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(workflowTypeName), nameof(workflowTypeName));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(signalName), nameof(signalName));
            EnsureNotDisposed();

            options = WorkflowOptions.Normalize(this, options);

            var reply = (WorkflowSignalWithStartReply) await CallProxyAsync(
                new WorkflowSignalWithStartRequest()
            {
                Workflow     = workflowTypeName,
                WorkflowId   = options.WorkflowId,
                Options      = options.ToInternal(),
                SignalName   = signalName,
                SignalArgs   = signalArgs,
                WorkflowArgs = startArgs,
                Domain       = options.Domain
            });

            reply.ThrowOnError();

            return(reply.Execution.ToPublic());
        }
Exemple #11
0
        /// <summary>
        /// Scans the assembly passed looking for workflow implementations derived from
        /// <see cref="WorkflowBase"/> and tagged by <see cref="WorkflowAttribute"/> with
        /// <see cref="WorkflowAttribute.AutoRegister"/> set to <c>true</c> and registers
        /// them with Cadence.
        /// </summary>
        /// <param name="assembly">The target assembly.</param>
        /// <param name="domain">Optionally overrides the default client domain.</param>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        /// <exception cref="TypeLoadException">
        /// Thrown for types tagged by <see cref="WorkflowAttribute"/> that are not
        /// derived from <see cref="WorkflowBase"/>.
        /// </exception>
        /// <exception cref="InvalidOperationException">Thrown if one of the tagged classes conflict with an existing registration.</exception>
        /// <exception cref="WorkflowWorkerStartedException">
        /// Thrown if a workflow worker has already been started for the client.  You must
        /// register workflow implementations before starting workers.
        /// </exception>
        /// <remarks>
        /// <note>
        /// Be sure to register all of your workflow implementations before starting workers.
        /// </note>
        /// </remarks>
        public async Task RegisterAssemblyWorkflowsAsync(Assembly assembly, string domain = null)
        {
            await SyncContext.ClearAsync;

            Covenant.Requires <ArgumentNullException>(assembly != null, nameof(assembly));
            EnsureNotDisposed();

            foreach (var type in assembly.GetTypes().Where(t => t.IsClass))
            {
                var workflowAttribute = type.GetCustomAttribute <WorkflowAttribute>();

                if (workflowAttribute != null && workflowAttribute.AutoRegister)
                {
                    var workflowTypeName = CadenceHelper.GetWorkflowTypeName(type, workflowAttribute);

                    await WorkflowBase.RegisterAsync(this, type, workflowTypeName, ResolveDomain(domain));

                    lock (registeredWorkflowTypes)
                    {
                        registeredWorkflowTypes.Add(CadenceHelper.GetWorkflowInterface(type));
                    }
                }
            }
        }
Exemple #12
0
            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="name">The folder name.</param>
            /// <param name="files">Optional files to be added.</param>
            /// <param name="folders">Optional folders to be added.</param>
            public Folder(string name, IEnumerable <File> files = null, IEnumerable <Folder> folders = null)
            {
                Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(name), nameof(name));

                this.Name    = name;
                this.files   = new Dictionary <string, File>();
                this.folders = new Dictionary <string, Folder>();

                if (files != null)
                {
                    foreach (var file in files)
                    {
                        this.files.Add(file.Name, file);
                    }
                }

                if (folders != null)
                {
                    foreach (var folder in folders)
                    {
                        this.folders.Add(folder.Name, folder);
                    }
                }
            }
Exemple #13
0
        /// <summary>
        /// Deletes a hive Docker config.
        /// </summary>
        /// <param name="configName">The config name.</param>
        /// <param name="options">Optional command run options.</param>
        /// <exception cref="HiveException">Thrown if the operation failed.</exception>
        public void Remove(string configName, RunOptions options = RunOptions.None)
        {
            Covenant.Requires <ArgumentException>(HiveDefinition.IsValidName(configName));

            var bundle = new CommandBundle("./delete-config.sh");

            bundle.AddFile("delete-config.sh",
                           $@"#!/bin/bash
docker config inspect {configName}

if [ ""$?"" != ""0"" ] ; then
    echo ""Config doesn't exist.""
else
    docker config rm {configName}
fi
", isExecutable: true);

            var response = hive.GetReachableManager().SudoCommand(bundle, RunOptions.None);

            if (response.ExitCode != 0)
            {
                throw new HiveException(response.ErrorSummary);
            }
        }
Exemple #14
0
        /// <summary>
        /// Checks the argument passed for wildcards and expands them into the
        /// appopriate set of matching file names.
        /// </summary>
        /// <param name="path">The file path potentially including wildcards.</param>
        /// <returns>The set of matching file names.</returns>
        public static string[] ExpandWildcards(string path)
        {
            Covenant.Requires <ArgumentNullException>(path != null);

            int    pos;
            string dir;
            string pattern;

            if (path.IndexOfAny(NeonHelper.FileWildcards) == -1)
            {
                return(new string[] { path });
            }

            pos = path.LastIndexOfAny(new char[] { '\\', '/', ':' });
            if (pos == -1)
            {
                return(Directory.GetFiles(".", path));
            }

            dir     = path.Substring(0, pos);
            pattern = path.Substring(pos + 1);

            return(Directory.GetFiles(dir, pattern));
        }
Exemple #15
0
        /// <summary>
        /// Builds and publishes a project locally to prepare it for being uploaded to the Raspberry.  This method
        /// will display an error message box to the user on failures
        /// </summary>
        /// <param name="dte"></param>
        /// <param name="solution"></param>
        /// <param name="project"></param>
        /// <param name="projectProperties"></param>
        /// <returns></returns>
        public static async Task <bool> PublishProjectWithUIAsync(DTE2 dte, Solution solution, Project project, ProjectProperties projectProperties)
        {
            Covenant.Requires <ArgumentNullException>(dte != null, nameof(dte));
            Covenant.Requires <ArgumentNullException>(solution != null, nameof(solution));
            Covenant.Requires <ArgumentNullException>(project != null, nameof(project));
            Covenant.Requires <ArgumentNullException>(projectProperties != null, nameof(projectProperties));

            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            if (!await PublishProjectAsync(dte, solution, project, projectProperties))
            {
                MessageBox.Show(
                    "[dotnet publish] failed for the project.\r\n\r\nLook at the Output/Debug panel for more details.",
                    "Publish Failed",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return(false);
            }
            else
            {
                return(true);
            }
        }
Exemple #16
0
        /// <summary>
        /// Performs an HTTP <b>GET</b> using a specific <see cref="IRetryPolicy"/> and
        /// without ensuring that a success code was returned.
        /// </summary>
        /// <param name="retryPolicy">The retry policy or <c>null</c> to disable retries.</param>
        /// <param name="uri">The URI</param>
        /// <param name="args">The optional query arguments.</param>
        /// <param name="headers">The Optional HTTP headers.</param>
        /// <param name="cancellationToken">The optional <see cref="CancellationToken"/>.</param>
        /// <param name="logActivity">The optional <see cref="LogActivity"/> whose ID is to be included in the request.</param>
        /// <returns>The <see cref="JsonResponse"/>.</returns>
        /// <exception cref="SocketException">Thrown for network connectivity issues.</exception>
        public async Task <JsonResponse> GetUnsafeAsync(
            IRetryPolicy retryPolicy,
            string uri,
            ArgDictionary args    = null,
            ArgDictionary headers = null,
            CancellationToken cancellationToken = default,
            LogActivity logActivity             = default)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(uri));

            retryPolicy = retryPolicy ?? NoRetryPolicy.Instance;

            return(await retryPolicy.InvokeAsync(
                       async() =>
            {
                var requestUri = FormatUri(uri, args);

                try
                {
                    var client = this.HttpClient;

                    if (client == null)
                    {
                        throw new ObjectDisposedException(nameof(JsonClient));
                    }

                    var httpResponse = await client.GetAsync(requestUri, cancellationToken: cancellationToken, headers: headers, activity: logActivity);

                    return new JsonResponse(requestUri, httpResponse, await httpResponse.Content.ReadAsStringAsync());
                }
                catch (HttpRequestException e)
                {
                    throw new HttpException(e, requestUri);
                }
            }));
        }
Exemple #17
0
        /// <summary>
        /// Asynchronously publishes a message to an exchange.
        /// </summary>
        /// <typeparam name="TMessage">The message type.</typeparam>
        /// <param name="exchange">The target exchane.</param>
        /// <param name="message">The message.</param>
        /// <param name="routingKey">The routing key.</param>
        /// <param name="headers">Optional message headers.</param>
        protected async Task PublishAsync <TMessage>(IExchange exchange, TMessage message, string routingKey, params KeyValuePair <string, string>[] headers)
            where TMessage : class, new()
        {
            Covenant.Requires <ArgumentNullException>(exchange != null);
            Covenant.Requires <ArgumentNullException>(message != null);

            var body       = Serialize(message, Encoding.UTF8);
            var properties = new MessageProperties()
            {
                Type            = typeof(TMessage).FullName,
                ContentType     = "application/json",
                ContentEncoding = "utf-8"
            };

            if (headers != null && headers.Length > 0)
            {
                foreach (var header in headers)
                {
                    properties.Headers.Add(header.Key, header.Value);
                }
            }

            await EasyBus.PublishAsync(exchange, routingKey, mandatory : false, properties, body);
        }
Exemple #18
0
        /// <summary>
        /// <para>
        /// Retrieves all of the extensions methods in an assembly targeting a specific type.
        /// </para>
        /// <note>
        /// This doesn't currently support nested or generic target types.
        /// </note>
        /// </summary>
        /// <param name="assembly">The source assembly.</param>
        /// <param name="targetType">The type being extended.</param>
        /// <param name="allowPrivate">Optionally specifies that only private methods are to be returned as well as public ones.</param>
        /// <returns>The extension methods.</returns>
        public static IEnumerable <MethodInfo> GetExtensionMethodsFor(this Assembly assembly, Type targetType, bool allowPrivate = false)
        {
            Covenant.Requires <ArgumentNullException>(assembly != null, nameof(assembly));
            Covenant.Requires <ArgumentNullException>(targetType != null, nameof(targetType));
            Covenant.Requires <ArgumentException>(!targetType.IsNested, nameof(targetType), "Nested target types are not currently supported.");
            Covenant.Requires <ArgumentException>(!targetType.IsGenericType, nameof(targetType), "Generic target types are not currently supported.");

            var bindingFlags = BindingFlags.Static | BindingFlags.Public;

            if (allowPrivate)
            {
                bindingFlags |= BindingFlags.NonPublic;
            }

            var query =
                from type in assembly.GetTypes()
                where !type.IsGenericType && !type.IsNested
                from method in type.GetMethods(bindingFlags)
                where method.IsDefined(typeof(ExtensionAttribute), false)
                where method.GetParameters()[0].ParameterType == targetType
                select method;

            return(query);
        }
Exemple #19
0
        /// <summary>
        /// Removes a named virtual machine.
        /// </summary>
        /// <param name="machineName">The machine name.</param>
        public void RemoveVM(string machineName)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(machineName));
            CheckDisposed();

            var machine = GetVM(machineName);
            var drives  = GetVMDrives(machineName);

            // Remove the machine along with any of of its virtual hard drive files.

            try
            {
                powershell.Execute($"Remove-VM -Name \"{machineName}\" -Force");
            }
            catch (Exception e)
            {
                throw new HyperVException(e.Message, e);
            }

            foreach (var drivePath in drives)
            {
                File.Delete(drivePath);
            }
        }
Exemple #20
0
        /// <summary>
        /// Signals the workflow.
        /// </summary>
        /// <param name="signalName">The signal name.</param>
        /// <param name="args">The signal arguments.</param>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        /// <exception cref="InvalidOperationException">Thrown if the child workflow has not been started.</exception>
        /// <remarks>
        /// <note>
        /// <b>IMPORTANT:</b> You need to take care to ensure that the parameters passed
        /// are compatible with the target workflow arguments.
        /// </note>
        /// </remarks>
        public async Task SignalAsync(string signalName, params object[] args)
        {
            await SyncContext.ClearAsync;

            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(signalName), nameof(signalName));
            Covenant.Requires <ArgumentNullException>(args != null, nameof(args));

            if (Execution == null)
            {
                throw new InvalidOperationException("The stub must be started first.");
            }

            var reply = (WorkflowSignalReply)await client.CallProxyAsync(
                new WorkflowSignalRequest()
            {
                WorkflowId = execution.WorkflowId,
                RunId      = execution.RunId,
                Domain     = options.Domain,
                SignalName = signalName,
                SignalArgs = client.DataConverter.ToData(args)
            });

            reply.ThrowOnError();
        }
Exemple #21
0
        /// <summary>
        /// Lists the Cadence domains.
        /// </summary>
        /// <param name="pageSize">
        /// The maximum number of domains to be returned.  This must be
        /// greater than or equal to one.
        /// </param>
        /// <param name="nextPageToken">
        /// Optionally specifies an opaque token that can be used to retrieve subsequent
        /// pages of domains.
        /// </param>
        /// <returns>A <see cref="DomainListPage"/> with the domains.</returns>
        /// <remarks>
        /// <para>
        /// This method can be used to retrieve one or more pages of domain
        /// results.  You'll pass <paramref name="pageSize"/> as the maximum number
        /// of domains to be returned per page.  The <see cref="DomainListPage"/>
        /// returned will list the domains and if there are more domains waiting
        /// to be returned, will return token that can be used in a subsequent
        /// call to retrieve the next page of results.
        /// </para>
        /// <note>
        /// <see cref="DomainListPage.NextPageToken"/> will be set to <c>null</c>
        /// when there are no more result pages remaining.
        /// </note>
        /// </remarks>
        public async Task <DomainListPage> ListDomainsAsync(int pageSize, byte[] nextPageToken = null)
        {
            await SyncContext.Clear;

            Covenant.Requires <ArgumentException>(pageSize >= 1, nameof(pageSize));
            EnsureNotDisposed();

            var reply = (DomainListReply) await CallProxyAsync(
                new DomainListRequest()
            {
                PageSize      = pageSize,
                NextPageToken = nextPageToken
            });

            reply.ThrowOnError();

            var domains = new List <DomainDescription>(reply.Domains.Count);

            foreach (var domain in reply.Domains)
            {
                domains.Add(domain.ToPublic());
            }

            nextPageToken = reply.NextPageToken;

            if (nextPageToken != null && nextPageToken.Length == 0)
            {
                nextPageToken = null;
            }

            return(new DomainListPage()
            {
                Domains = domains,
                NextPageToken = nextPageToken
            });
        }
Exemple #22
0
        //---------------------------------------------------------------------
        // Implementation

        /// <summary>
        /// Performs development related cluster checks with information on potential
        /// problems being written to STDOUT.
        /// </summary>
        /// <param name="clusterLogin">Specifies the target cluster login.</param>
        /// <param name="k8s">Specifies the cluster's Kubernertes client.</param>
        /// <returns><c>true</c> when there are no problems, <c>false</c> otherwise.</returns>
        public static async Task <bool> CheckAsync(ClusterLogin clusterLogin, IKubernetes k8s)
        {
            Covenant.Requires <ArgumentNullException>(clusterLogin != null, nameof(clusterLogin));
            Covenant.Requires <ArgumentNullException>(k8s != null, nameof(k8s));

            var error = false;

            if (!await CheckNodeContainerImagesAsync(clusterLogin, k8s))
            {
                error = true;
            }

            if (!await CheckPodPrioritiesAsync(clusterLogin, k8s))
            {
                error = true;
            }

            if (!await CheckResourcesAsync(clusterLogin, k8s))
            {
                error = true;
            }

            return(error);
        }
Exemple #23
0
        /// <summary>
        /// Constructs a reverse proxy.
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="localPort">The local port.</param>
        /// <param name="remotePort">The remote port.</param>
        /// <param name="@namespace"></param>

        /// Optionally specifies an acceptable server certificate.  This can be used
        /// as a way to allow access for a specific self-signed certificate.  Passing
        /// a certificate implies <paramref name="remoteTls"/><c>=true</c>.
        /// </param>
        /// <param name="clientCertificate">
        /// Optionally specifies a client certificate.  Passing a certificate implies
        /// <paramref name="remoteTls"/><c>=true</c>.
        /// </param>
        /// <param name="requestHandler">Optional request hook.</param>
        /// <param name="responseHandler">Optional response hook.</param>
        public PortForward(
            string serviceName,
            int localPort,
            int remotePort,
            string @namespace = "default")
        {
            Covenant.Requires <ArgumentException>(NetHelper.IsValidPort(localPort));
            Covenant.Requires <ArgumentException>(NetHelper.IsValidPort(remotePort));

            if (!NeonHelper.IsWindows)
            {
                throw new NotSupportedException($"[{nameof(PortForward)}] is supported only on Windows.");
            }

            this.serviceName         = serviceName;
            this.localPort           = localPort;
            this.remotePort          = remotePort;
            this.@namespace          = @namespace;
            this.kubectlProxyProcess = new Process();
            // Create the client.


            KubeHelper.PortForward(serviceName, remotePort, localPort, @namespace, kubectlProxyProcess);
        }
Exemple #24
0
        /// <summary>
        /// Uploads a file as multilpe assets to a release, publishes the release and then
        /// return the <see cref="Download"/> details.
        /// </summary>
        /// <param name="release">The target brelease.</param>
        /// <param name="name">The download name.</param>
        /// <param name="version">The download version.</param>
        /// <param name="partCount">The number of parts to be uploaded.</param>
        /// <param name="partSize">The size of each part.</param>
        /// <returns>The <see cref="Download"/> describing how to download the parts.</returns>
        /// <remarks>
        /// Each part will be filled with bytes where the byte of each part will start
        /// with the part number and the following bytes will increment the previous byte
        /// value.
        /// </remarks>
        private DownloadManifest PublishMultipartAsset(Release release, string name, string version, int partCount, long partSize)
        {
            Covenant.Requires <ArgumentNullException>(release != null, nameof(release));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(name), nameof(name));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(version), nameof(version));
            Covenant.Requires <ArgumentException>(partCount > 0, nameof(release));
            Covenant.Requires <ArgumentException>(partSize > 0, nameof(release));

            using (var tempFile = new TempFile())
            {
                using (var output = new FileStream(tempFile.Path, System.IO.FileMode.Create, FileAccess.ReadWrite))
                {
                    for (int partNumber = 0; partNumber < partCount; partNumber++)
                    {
                        for (long i = 0; i < partSize; i++)
                        {
                            output.WriteByte((byte)i);
                        }
                    }
                }

                return(GitHub.Releases.UploadMultipartAsset(repo, release, tempFile.Path, version: version, name: name, maxPartSize: partSize));
            }
        }
Exemple #25
0
        /// <summary>
        /// Registers an activity type.
        /// </summary>
        /// <param name="client">The associated client.</param>
        /// <param name="activityType">The activity type.</param>
        /// <param name="activityTypeName">The name used to identify the implementation.</param>
        /// <returns><c>true</c> if the activity was already registered.</returns>
        /// <exception cref="InvalidOperationException">Thrown if a different activity class has already been registered for <paramref name="activityTypeName"/>.</exception>
        internal static bool Register(CadenceClient client, Type activityType, string activityTypeName)
        {
            Covenant.Requires <ArgumentNullException>(client != null);
            CadenceHelper.ValidateActivityImplementation(activityType);

            activityTypeName = GetActivityTypeKey(client, activityTypeName);

            var constructInfo = new ActivityInvokeInfo();

            constructInfo.ActivityType = activityType;
            constructInfo.Constructor  = constructInfo.ActivityType.GetConstructor(noTypeArgs);

            if (constructInfo.Constructor == null)
            {
                throw new ArgumentException($"Activity type [{constructInfo.ActivityType.FullName}] does not have a default constructor.");
            }

            lock (syncLock)
            {
                if (nameToInvokeInfo.TryGetValue(activityTypeName, out var existingEntry))
                {
                    if (!object.ReferenceEquals(existingEntry.ActivityType, constructInfo.ActivityType))
                    {
                        throw new InvalidOperationException($"Conflicting activity type registration: Activity type [{activityType.FullName}] is already registered for workflow type name [{activityTypeName}].");
                    }

                    return(true);
                }
                else
                {
                    nameToInvokeInfo[activityTypeName] = constructInfo;

                    return(false);
                }
            }
        }
Exemple #26
0
        /// <summary>
        /// Gets a list of taints that are currently applied to all nodes matching the given node label/value pair.
        /// </summary>
        /// <param name="controller">The setup controller.</param>
        /// <param name="labelKey">The target nodes label key.</param>
        /// <param name="labelValue">The target nodes label value.</param>
        /// <returns>The taint list.</returns>
        public static async Task <List <V1Taint> > GetTaintsAsync(ISetupController controller, string labelKey, string labelValue)
        {
            await SyncContext.Clear;

            Covenant.Requires <ArgumentNullException>(controller != null, nameof(controller));

            var taints = new List <V1Taint>();

            foreach (var node in (await GetK8sClient(controller).ListNodeAsync()).Items.Where(node => node.Metadata.Labels.Any(label => label.Key == labelKey && label.Value == labelValue)))
            {
                if (node.Spec.Taints?.Count() > 0)
                {
                    foreach (var taint in node.Spec.Taints)
                    {
                        if (!taints.Any(t => t.Key == taint.Key && t.Effect == taint.Effect && t.Value == taint.Value))
                        {
                            taints.Add(taint);
                        }
                    }
                }
            }

            return(taints);
        }
Exemple #27
0
        /// <summary>
        /// Sets the Linux file permissions.
        /// </summary>
        /// <param name="path">Path to the target file or directory.</param>
        /// <param name="mode">Linux file permissions.</param>
        /// <param name="recursive">Optionally apply the permissions recursively.</param>
        public static void Set(string path, string mode, bool recursive = false)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(path));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(mode));

            // $todo(jeff.lill):
            //
            // We're going to hack this by running [chmod MODE PATH].  Eventually,
            // we could convert this to using a low-level package but I didn't
            // want to spend time trying to figure that out right now.
            //
            //      https://www.nuget.org/packages/Mono.Posix.NETStandard/1.0.0

            if (!NeonHelper.IsLinux)
            {
                throw new NotSupportedException("This method requires Linux.");
            }

            object[] args;

            if (recursive)
            {
                args = new object[] { "-R", mode, path };
            }
            else
            {
                args = new object[] { mode, path };
            }

            var response = NeonHelper.ExecuteCaptureAsync("chmod", new object[] { mode, path }).Result;

            if (response.ExitCode != 0)
            {
                throw new IOException(response.ErrorText);
            }
        }
Exemple #28
0
        /// <summary>
        /// Executes a SQL query and returns the data reader to be used to process the results.
        /// </summary>
        /// <param name="connection">The database connection.</param>
        /// <param name="cmdText">The SQL command.</param>
        /// <param name="behavior">Optionally specifies the command behavior.</param>
        /// <param name="transaction">Optionally specifies the transaction.</param>
        /// <returns>The <see cref="NpgsqlDataReader"/>.</returns>
        /// <remarks>
        /// <note>
        /// Although this method is convenient, consider explictly creating and
        /// preparing <see cref="NpgsqlCommand"/> for frequently executed commands
        /// for better performance.
        /// </note>
        /// </remarks>
        public static NpgsqlDataReader ExecuteReader(
            this NpgsqlConnection connection,
            string cmdText,
            CommandBehavior behavior      = CommandBehavior.Default,
            NpgsqlTransaction transaction = null)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(cmdText), nameof(cmdText));

            NpgsqlCommand command;

            if (transaction == null)
            {
                command = new NpgsqlCommand(cmdText, connection);
            }
            else
            {
                command = new NpgsqlCommand(cmdText, connection, transaction);
            }

            using (command)
            {
                return(command.ExecuteReader(behavior));
            }
        }
Exemple #29
0
        /// <summary>
        /// Returns the value associated with a command line option if the option was present
        /// on the command line otherwise, the specified default value will be returned.
        /// </summary>
        /// <param name="optionName">The case sensitive option name (including the leading dashes (<b>-</b>).</param>
        /// <param name="def">The default value.</param>
        /// <returns>The option value if present, the specified default value otherwise.</returns>
        /// <remarks>
        /// <para>
        /// If the <paramref name="optionName"/> was included in a previous <see cref="DefineOption"/>
        /// call, then all aliases for the option will be searched.  If the option is not
        /// present on the command line and <paramref name="def"/> is <c>null</c>, then the default
        /// defined default value will be returned otherwise <paramref name="def"/> will override
        /// the definition.
        /// </para>
        /// </remarks>
        public string GetOption(string optionName, string def = null)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(optionName));

            OptionDefinition definition;
            string           value;

            if (optionDefinitions.TryGetValue(optionName, out definition))
            {
                foreach (var name in definition.Names)
                {
                    if (options.TryGetValue(name, out value) && !string.IsNullOrEmpty(value))
                    {
                        return(value);
                    }
                }

                if (def != null)
                {
                    return(def);
                }
                else
                {
                    return(definition.Default);
                }
            }
            else
            {
                if (options.TryGetValue(optionName, out value))
                {
                    return(value);
                }

                return(def);
            }
        }
Exemple #30
0
        /// <summary>
        /// Performs common node configuration.
        /// </summary>
        /// <param name="controller">The setup controller.</param>
        /// <param name="clusterManifest">The cluster manifest.</param>
        public void SetupNode(ISetupController controller, ClusterManifest clusterManifest)
        {
            Covenant.Requires <ArgumentNullException>(controller != null, nameof(controller));
            Covenant.Requires <ArgumentNullException>(clusterManifest != null, nameof(clusterManifest));

            var nodeDefinition    = NeonHelper.CastTo <NodeDefinition>(Metadata);
            var clusterDefinition = Cluster.Definition;
            var hostingManager    = controller.Get <IHostingManager>(KubeSetupProperty.HostingManager);

            InvokeIdempotent("setup/node",
                             () =>
            {
                PrepareNode(controller);
                ConfigureEnvironmentVariables(controller);
                SetupPackageProxy(controller);
                UpdateHostname(controller);
                NodeInitialize(controller);
                NodeInstallCriO(controller, clusterManifest);
                NodeInstallIPVS(controller);
                NodeInstallPodman(controller);
                NodeInstallKubernetes(controller);
                SetupKublet(controller);
            });
        }