Beispiel #1
0
        // determines whether the step requires execution
        internal bool StepRequiresExecution(InstallSetupStep step, object instruction)
        {
            if (step == null)
            {
                throw new ArgumentNullException(nameof(step));
            }

            var modelAttempt = instruction.TryConvertTo(step.StepType);

            if (!modelAttempt.Success)
            {
                throw new InvalidCastException(
                          $"Cannot cast/convert {step.GetType().FullName} into {step.StepType.FullName}");
            }

            var model           = modelAttempt.Result;
            var genericStepType = typeof(InstallSetupStep <>);

            Type[] typeArgs      = { step.StepType };
            var    typedStepType = genericStepType.MakeGenericType(typeArgs);

            try
            {
                var method = typedStepType.GetMethods().Single(x => x.Name == "RequiresExecution");
                return((bool)method.Invoke(step, new[] { model }));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Checking if step requires execution ({Step}) failed.",
                                 step.Name);
                throw;
            }
        }
Beispiel #2
0
        // executes the step
        internal async Task <InstallSetupResult> ExecuteStepAsync(InstallSetupStep step, object instruction)
        {
            using (_proflog.TraceDuration <InstallApiController>($"Executing installation step: '{step.Name}'.",
                                                                 "Step completed"))
            {
                var modelAttempt = instruction.TryConvertTo(step.StepType);
                if (!modelAttempt.Success)
                {
                    throw new InvalidCastException(
                              $"Cannot cast/convert {step.GetType().FullName} into {step.StepType.FullName}");
                }

                var    model           = modelAttempt.Result;
                var    genericStepType = typeof(InstallSetupStep <>);
                Type[] typeArgs        = { step.StepType };
                var    typedStepType   = genericStepType.MakeGenericType(typeArgs);
                try
                {
                    var method = typedStepType.GetMethods().Single(x => x.Name == "ExecuteAsync");
                    var task   = (Task <InstallSetupResult>)method.Invoke(step, new[] { model });
                    return(await task);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Installation step {Step} failed.", step.Name);
                    throw;
                }
            }
        }
Beispiel #3
0
        private static object GetInstruction(InstallInstructions installModel, InstallTrackingItem item,
                                             InstallSetupStep step)
        {
            installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null

            if (instruction is JObject jObject)
            {
                instruction = jObject?.ToObject(step.StepType);
            }

            return(instruction);
        }
Beispiel #4
0
        // determines whether the step requires execution
        internal bool StepRequiresExecution(InstallSetupStep step, JToken instruction)
        {
            var model           = instruction?.ToObject(step.StepType);
            var genericStepType = typeof(InstallSetupStep <>);

            Type[] typeArgs      = { step.StepType };
            var    typedStepType = genericStepType.MakeGenericType(typeArgs);

            try
            {
                var method = typedStepType.GetMethods().Single(x => x.Name == "RequiresExecution");
                return((bool)method.Invoke(step, new[] { model }));
            }
            catch (Exception ex)
            {
                _logger.Error <InstallApiController>(ex, "Checking if step requires execution ({Step}) failed.", step.Name);
                throw;
            }
        }
Beispiel #5
0
        /// <summary>
        /// We'll peek ahead and check if it's RequiresExecution is returning true. If it
        /// is not, we'll dequeue that step and peek ahead again (recurse)
        /// </summary>
        /// <param name="current"></param>
        /// <param name="queue"></param>
        /// <param name="installId"></param>
        /// <param name="installModel"></param>
        /// <returns></returns>
        private string IterateNextRequiredStep(InstallSetupStep current, Queue <InstallTrackingItem> queue, Guid installId, InstallInstructions installModel)
        {
            if (queue.Count > 0)
            {
                var next = queue.Peek();
                var step = InstallHelper.GetAllSteps().Single(x => x.Name == next.Name);

                //If the current step restarts the app pool then we must simply return the next one in the queue,
                // we cannot peek ahead as the next step might rely on the app restart and therefore RequiresExecution
                // will rely on that too.
                if (current.PerformsAppRestart)
                {
                    return(step.Name);
                }

                JToken instruction = null;
                //If this step has any instructions then extract them
                if (installModel.Instructions.Any(x => x.Key == step.Name))
                {
                    instruction = installModel.Instructions[step.Name];
                }

                //if the step requires execution then return it's name
                if (StepRequiresExecution(step, instruction))
                {
                    return(step.Name);
                }

                //this step no longer requires execution, this could be due to a new config change during installation,
                // so we'll dequeue this one from the queue and recurse
                queue.Dequeue();

                //set this as complete
                InstallStatusTracker.SetComplete(installId, step.Name, null);

                //recurse
                return(IterateNextRequiredStep(step, queue, installId, installModel));
            }

            //there is no more steps
            return(string.Empty);
        }
Beispiel #6
0
 internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction)
 {
     using (ApplicationContext.ProfilingLogger.TraceDuration <InstallApiController>("Executing installation step: " + step.Name, "Step completed"))
     {
         var    model           = instruction == null ? null : instruction.ToObject(step.StepType);
         var    genericStepType = typeof(InstallSetupStep <>);
         Type[] typeArgs        = { step.StepType };
         var    typedStepType   = genericStepType.MakeGenericType(typeArgs);
         try
         {
             var method = typedStepType.GetMethods().Single(x => x.Name == "Execute");
             return((InstallSetupResult)method.Invoke(step, new object[] { model }));
         }
         catch (Exception ex)
         {
             LogHelper.Error <InstallApiController>("Installation step " + step.Name + " failed.", ex);
             throw;
         }
     }
 }
Beispiel #7
0
 // executes the step
 internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction)
 {
     using (_proflog.TraceDuration <InstallApiController>($"Executing installation step: '{step.Name}'.", "Step completed"))
     {
         var    model           = instruction?.ToObject(step.StepType);
         var    genericStepType = typeof(InstallSetupStep <>);
         Type[] typeArgs        = { step.StepType };
         var    typedStepType   = genericStepType.MakeGenericType(typeArgs);
         try
         {
             var method = typedStepType.GetMethods().Single(x => x.Name == "Execute");
             return((InstallSetupResult)method.Invoke(step, new[] { model }));
         }
         catch (Exception ex)
         {
             _logger.Error <InstallApiController>(ex, "Installation step {Step} failed.", step.Name);
             throw;
         }
     }
 }
Beispiel #8
0
        /// <summary>
        ///     We'll peek ahead and check if it's RequiresExecution is returning true. If it
        ///     is not, we'll dequeue that step and peek ahead again (recurse)
        /// </summary>
        /// <param name="current"></param>
        /// <param name="queue"></param>
        /// <param name="installId"></param>
        /// <param name="installModel"></param>
        /// <returns></returns>
        private string IterateSteps(InstallSetupStep current, Queue <InstallTrackingItem> queue, Guid installId,
                                    InstallInstructions installModel)
        {
            while (queue.Count > 0)
            {
                var item = queue.Peek();

                // if the current step restarts the app pool then we must simply return the next one in the queue,
                // we cannot peek ahead as the next step might rely on the app restart and therefore RequiresExecution
                // will rely on that too.
                if (current.PerformsAppRestart)
                {
                    return(item.Name);
                }

                var step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name);

                // if this step has any instructions then extract them
                var instruction = GetInstruction(installModel, item, step);

                // if the step requires execution then return its name
                if (StepRequiresExecution(step, instruction))
                {
                    return(step.Name);
                }

                // no longer requires execution, could be due to a new config change during installation
                // dequeue
                queue.Dequeue();

                // complete
                _installStatusTracker.SetComplete(installId, step.Name);

                // and continue
                current = step;
            }

            return(string.Empty);
        }
Beispiel #9
0
    /// <summary>
    ///     Installs.
    /// </summary>
    public async Task <ActionResult <InstallProgressResultModel> > PostPerformInstall(InstallInstructions installModel)
    {
        if (installModel == null)
        {
            throw new ArgumentNullException(nameof(installModel));
        }

        InstallTrackingItem[] status = InstallStatusTracker.GetStatus().ToArray();
        //there won't be any statuses returned if the app pool has restarted so we need to re-read from file.
        if (status.Any() == false)
        {
            status = _installStatusTracker.InitializeFromFile(installModel.InstallId).ToArray();
        }

        //create a new queue of the non-finished ones
        var queue = new Queue <InstallTrackingItem>(status.Where(x => x.IsComplete == false));

        while (queue.Count > 0)
        {
            InstallTrackingItem item = queue.Dequeue();
            InstallSetupStep    step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name);

            // if this step has any instructions then extract them
            var instruction = GetInstruction(installModel, item, step);

            // if this step doesn't require execution then continue to the next one, this is just a fail-safe check.
            if (StepRequiresExecution(step, instruction) == false)
            {
                // set this as complete and continue
                _installStatusTracker.SetComplete(installModel.InstallId, item.Name);
                continue;
            }

            try
            {
                InstallSetupResult?setupData = await ExecuteStepAsync(step, instruction);

                // update the status
                _installStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData?.SavedStepData);

                // determine's the next step in the queue and dequeue's any items that don't need to execute
                var nextStep = IterateSteps(step, queue, installModel.InstallId, installModel);

                // check if there's a custom view to return for this step
                if (setupData != null && setupData.View.IsNullOrWhiteSpace() == false)
                {
                    return(new InstallProgressResultModel(false, step.Name, nextStep, setupData.View, setupData.ViewModel));
                }

                return(new InstallProgressResultModel(false, step.Name, nextStep));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occurred during installation step {Step}", step.Name);

                if (ex is TargetInvocationException && ex.InnerException != null)
                {
                    ex = ex.InnerException;
                }

                if (ex is InstallException installException)
                {
                    return(new ValidationErrorResult(new
                    {
                        view = installException.View,
                        model = installException.ViewModel,
                        message = installException.Message
                    }));
                }

                return(new ValidationErrorResult(new { step = step.Name, view = "error", message = ex.Message }));
            }
        }

        _installStatusTracker.Reset();
        return(new InstallProgressResultModel(true, string.Empty, string.Empty));
    }