/// <summary> /// Transition to next state and update the subscription in database. /// </summary> /// <param name="subscription">The subscription</param> /// <param name="targetState">The target state</param> /// <param name="callerName">The caller name</param> /// <returns></returns> private async Task <Subscription> TransitToNextState(Subscription subscription, ProvisioningState targetState, [CallerMemberName] string callerName = "") { MethodInfo method = typeof(ProvisioningService).GetMethod(callerName); OutputStatesAttribute attribute = (OutputStatesAttribute)Attribute.GetCustomAttribute(method, typeof(OutputStatesAttribute)); if (!attribute.InputStates.Contains(targetState.ToString())) { throw new LunaProvisioningException( $"Cannot transit to {targetState.ToString()} state from method {callerName}.", false); } //reset retry count subscription.RetryCount = 0; subscription.ProvisioningStatus = targetState.ToString(); subscription.LastUpdatedTime = DateTime.UtcNow; _context.Subscriptions.Update(subscription); await _context._SaveChangesAsync(); return(subscription); }
/// <summary> /// Handle the provisioning exceptions /// </summary> /// <param name="subscription">The subscription</param> /// <param name="ex">The exception</param> /// <param name="callerName">The caller name</param> /// <returns>The updated subscription</returns> private async Task <Subscription> HandleExceptions(Subscription subscription, Exception ex, [CallerMemberName] string callerName = "") { _logger.LogError(ex, ex.Message); // Transit to error state if: // 1. It is not a LunaException // 2. It is not retry-able // 3. The retry count exceeded the threshold if (ex.GetType() != typeof(LunaServerException) || !((LunaServerException)ex).IsRetryable || subscription.RetryCount >= _maxRetry) { ProvisioningState errorState; switch (subscription.ProvisioningStatus) { case nameof(ProvisioningState.NotificationPending): errorState = ProvisioningState.NotificationFailed; break; case nameof(ProvisioningState.ArmTemplatePending): case nameof(ProvisioningState.ArmTemplateRunning): errorState = ProvisioningState.ArmTemplateFailed; break; case nameof(ProvisioningState.ProvisioningPending): case nameof(ProvisioningState.DeployResourceGroupRunning): errorState = ProvisioningState.DeployResourceGroupFailed; break; case nameof(ProvisioningState.WebhookPending): errorState = ProvisioningState.WebhookFailed; break; default: errorState = ProvisioningState.NotSpecified; break; } // Transit to error state and reset retry count subscription.ProvisioningStatus = errorState.ToString(); subscription.RetryCount = 0; } else { // Failback to an earlier state if it is specified in the LunaProvisioningException. // Otherwise, stay in the same state and retry if (ex.GetType() == typeof(LunaProvisioningException) && ((LunaProvisioningException)ex).FailbackState != ProvisioningState.NotSpecified) { var failbackState = ((LunaProvisioningException)ex).FailbackState; subscription.ProvisioningStatus = failbackState.ToString(); } } MethodInfo method = typeof(ProvisioningService).GetMethod(callerName); OutputStatesAttribute attribute = (OutputStatesAttribute)Attribute.GetCustomAttribute(method, typeof(OutputStatesAttribute)); if (!attribute.InputStates.Contains(subscription.ProvisioningStatus.ToString())) { _logger.LogError(ex, $"Can not transit to ${subscription.ProvisioningStatus.ToString()} state from method {callerName}."); subscription.ProvisioningStatus = nameof(ProvisioningState.NotSpecified); } subscription.LastUpdatedTime = DateTime.UtcNow; subscription.LastException = ex.Message; _context.Subscriptions.Update(subscription); await _context._SaveChangesAsync(); return(subscription); }