Beispiel #1
0
        public static async Task RunOrchestration(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            if (log is null)
            {
                throw new ArgumentNullException(nameof(log));
            }

            var command       = functionContext.GetInput <OrchestratorProjectDeleteCommand>();
            var commandResult = command.CreateResult();

            using (log.BeginCommandScope(command))
            {
                functionContext.SetCustomStatus($"Refreshing project", log);

                var project = commandResult.Result = (await functionContext
                                                      .GetProjectAsync(command.ProjectId, allowUnsafe: true)
                                                      .ConfigureAwait(true)) ?? command.Payload;

                try
                {
                    try
                    {
                        functionContext.SetCustomStatus("Sending commands", log);

                        var providerCommand = new ProviderProjectDeleteCommand
                                              (
                            command.User.PopulateExternalModel(),
                            project.PopulateExternalModel(),
                            command.CommandId
                                              );

                        var providerResults = await functionContext
                                              .SendProviderCommandAsync <ProviderProjectDeleteCommand, ProviderProjectDeleteCommandResult>(providerCommand, project)
                                              .ConfigureAwait(true);
                    }
                    finally
                    {
                        functionContext.SetCustomStatus("Deleting project", log);

                        await functionContext
                        .DeleteProjectAsync(project)
                        .ConfigureAwait(true);

                        functionContext.SetCustomStatus($"Deleting project identity", log);

                        await functionContext
                        .CallActivityWithRetryAsync(nameof(ProjectIdentityDeleteActivity), project)
                        .ConfigureAwait(true);

                        functionContext.SetCustomStatus("Deleting resources", log);

                        await functionContext.DeleteResourcesAsync
                        (
                            false, // we are not going to wait for this operation
                            GetResourceGroupId(project?.ResourceGroup?.Id)
                        )
                        .ConfigureAwait(true);
                    }
                }
                catch (Exception exc)
                {
                    commandResult ??= command.CreateResult();
                    commandResult.Errors.Add(exc);

                    throw;
                }
                finally
                {
                    var commandException = commandResult.Errors?.ToException();

                    if (commandException is null)
                    {
                        functionContext.SetCustomStatus($"Command succeeded", log);
                    }
                    else
                    {
                        functionContext.SetCustomStatus($"Command failed: {commandException.Message}", log, commandException);
                    }

                    functionContext.SetOutput(commandResult);
                }
            }
        }
Beispiel #2
0
    public override async Task <ICommandResult> HandleAsync(ProjectDestroyCommand command, IAsyncCollector <ICommand> commandQueue, IDurableOrchestrationContext orchestrationContext, ILogger log)
    {
        if (command is null)
        {
            throw new ArgumentNullException(nameof(command));
        }

        if (orchestrationContext is null)
        {
            throw new ArgumentNullException(nameof(orchestrationContext));
        }

        if (log is null)
        {
            throw new ArgumentNullException(nameof(log));
        }

        var commandResult = command.CreateResult();

        using (await orchestrationContext.LockContainerDocumentAsync(command.Payload).ConfigureAwait(true))
        {
            // just to make sure we are dealing with the latest version
            // of the Project entity, we re-fetch the entity and
            // use the passed in one as a potential fallback.

            commandResult.Result = (await orchestrationContext
                                    .CallActivityWithRetryAsync <Project>(nameof(ProjectGetActivity), new ProjectGetActivity.Input()
            {
                Id = command.Payload.Id, Organization = command.Payload.Organization
            })
                                    .ConfigureAwait(true)) ?? command.Payload;

            if (commandResult.Result.Deleted.HasValue)
            {
                try
                {
                    commandResult.Result = await UpdateProjectAsync(commandResult.Result, ResourceState.Deprovisioning)
                                           .ConfigureAwait(true);

                    var components = await componentRepository
                                     .ListAsync(commandResult.Result.Id, includeDeleted : true)
                                     .ToArrayAsync()
                                     .ConfigureAwait(true);

                    if (components.Any(c => c.ResourceState.IsActive()))
                    {
                        // at least one of the component in the context of this
                        // project are in an active state. we postpone the project
                        // destroy operation until all components are gone or
                        // in a deprovisioned state.

                        await orchestrationContext
                        .ContinueAsNew(command, TimeSpan.FromMinutes(1))
                        .ConfigureAwait(true);
                    }
                    else if (components.Any(c => c.ResourceState != ResourceState.Deprovisioned))
                    {
                        // at least one of the components reached a final state
                        // other than deprovisioned. we simple cant destroy the
                        // project in this situation but need to switch back into
                        // a provisioned state to give to user to fix issues
                        // on the component level

                        commandResult.Result = await UpdateProjectAsync(commandResult.Result, ResourceState.Provisioned, restore : true)
                                               .ConfigureAwait(true);
                    }
                    else
                    {
                        // we are good to delete project related resources and
                        // finally delete the project itself in our data store

                        await orchestrationContext
                        .CallActivityWithRetryAsync(nameof(ProjectDestroyActivity), new ProjectDestroyActivity.Input()
                        {
                            Project = commandResult.Result
                        })
                        .ConfigureAwait(true);

                        commandResult.Result = await UpdateProjectAsync(commandResult.Result, ResourceState.Deprovisioned)
                                               .ConfigureAwait(true);
                    }
                }
                catch
                {
                    commandResult.Result = await UpdateProjectAsync(commandResult.Result, ResourceState.Failed, restore : true)
                                           .ConfigureAwait(true);

                    throw;
                }
            }
        }

        return(commandResult);

        Task <Project> UpdateProjectAsync(Project project, ResourceState?resourceState = null, bool restore = false)
        {
            project.ResourceState = resourceState ?? project.ResourceState;

            if (restore)
            {
                project.Deleted = null;
                project.TTL     = null;
            }

            return(orchestrationContext.CallActivityWithRetryAsync <Project>(nameof(ProjectSetActivity), new ProjectSetActivity.Input()
            {
                Project = commandResult.Result, ResourceState = ResourceState.Deprovisioned
            }));
        }
    }
 public static Task <IEnumerable <Project> > ListProjectsAsync(this IDurableOrchestrationContext durableOrchestrationContext, IList <string> projectIds)
 => durableOrchestrationContext.CallActivityWithRetryAsync <IEnumerable <Project> >(nameof(ProjectListByIdActivity), projectIds);
 public static Task <UserDocument> DeleteUserAsync(this IDurableOrchestrationContext orchestrationContext, UserDocument user, bool allowUnsafe = false)
 => orchestrationContext.IsLockedBy <UserDocument>(user.Id.ToString()) || allowUnsafe
         ? orchestrationContext.CallActivityWithRetryAsync <UserDocument>(nameof(UserDeleteActivity), user)
         : throw new NotSupportedException($"Unable to delete user '{user.Id}' without acquired lock");
        public static async Task RunOrchestration(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            var command       = functionContext.GetInput <ProviderProjectCreateCommand>();
            var commandResult = command.CreateResult();
            var commandLog    = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            using (log.BeginCommandScope(command))
            {
                try
                {
                    functionContext.SetCustomStatus("Deploy resources", commandLog);

                    var deploymentOutput = await functionContext
                                           .CallDeploymentAsync(nameof(ProjectCreateActivity), command.Payload)
                                           .ConfigureAwait(true);

                    if (deploymentOutput.TryGetValue("resourceId", out var resourceId))
                    {
                        functionContext.SetCustomStatus("Updating user permissions", commandLog);

                        await functionContext
                        .CallActivityWithRetryAsync(nameof(ProjectUsersActivity), (command.Payload, resourceId?.ToString()))
                        .ConfigureAwait(true);
                    }

                    commandResult.Result = new ProviderOutput
                    {
                        Properties = deploymentOutput.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToString())
                    };
                }
                catch (Exception exc)
                {
                    commandResult ??= command.CreateResult();
                    commandResult.Errors.Add(exc);

                    throw exc.AsSerializable();
                }
                finally
                {
                    var commandException = commandResult.Errors?.ToException();

                    if (commandException is null)
                    {
                        functionContext.SetCustomStatus($"Command succeeded", commandLog);
                    }
                    else
                    {
                        functionContext.SetCustomStatus($"Command failed: {commandException.Message}", commandLog, commandException);
                    }

                    functionContext.SetOutput(commandResult);
                }
            }
        }
 public static Task <ProviderDocument> DeleteProviderAsync(this IDurableOrchestrationContext orchestrationContext, ProviderDocument provider, bool allowUnsafe = false)
 => orchestrationContext.IsLockedBy <ProviderDocument>(provider.Id) || allowUnsafe
     ? orchestrationContext.CallActivityWithRetryAsync <ProviderDocument>(nameof(ProviderDeleteActivity), provider)
     : throw new NotSupportedException($"Unable to delete provider '{provider.Id}' without acquired lock");
        public static async Task <object> Run(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            var(maximumRunDuration, aciRequirements) = context.GetInput <(DateTime, ACIRequirements)>();
            var(containerGroupInfo, pollingInterval, maxProcessingTime) = aciRequirements;
            var(resourceGroupName, containerGroupName) = (containerGroupInfo.ResourceGroupName, containerGroupInfo.ContainerGroupName);

            var containerGroupStatus =
                await context.CallActivityWithRetryAsync <ContainerGroupStatus>(
                    nameof(Activity_GetACIStatus),
                    new RetryOptions(TimeSpan.FromSeconds(30), 3)
            {
                RetryTimeout = TimeSpan.FromSeconds(30)
            },
                    containerGroupInfo);

            // if the container group has finished, return success status
            if (containerGroupStatus.Containers[0]?.CurrentState?.State == "Terminated")
            {
                var logContent = await context.CallActivityAsync <string>(nameof(Activity_GetACILogs), containerGroupInfo);

                bool isSuccess = CheckSuccessCode(logContent);
                if (isSuccess)
                {
                    return(new { Success = true });
                }

                if (!context.IsReplaying)
                {
                    log.LogError($"logs: {logContent}");
                }
                //return new { Success = false, Message = logContent };
                throw new ApplicationException($"logs: {logContent}");
            }

            // the container group has not finished - sleep for N duration
            using (var cts = new CancellationTokenSource())
            {
                await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(pollingInterval), cts.Token);
            }

            // end workflow if we've been waiting too long
            if (context.CurrentUtcDateTime > maximumRunDuration)
            {
                if (!context.IsReplaying)
                {
                    log.LogWarning($"Exceeded processing time { maxProcessingTime } minutes.");
                }

                //return new { Success = false, Message = "Exceeded processing time." } ;
                throw new TimeoutException($"Exceeded processing time { maxProcessingTime } minutes.");
            }

            // container group is still working, restart this sub-orchestration with
            // the same input data
            context.ContinueAsNew((maximumRunDuration, aciRequirements));


            // return some values if needed
            return(new { });
        }
Beispiel #8
0
        private static async Task <ICommandResult> ProcessCommandAsync(IDurableOrchestrationContext orchestrationContext, Input functionContext, ICommandResult commandResult, ILogger log)
        {
            var commandMessage  = default(ICommandMessage);
            var commandCallback = default(string);

            try
            {
                orchestrationContext.SetCustomStatus($"Acquire callback url", log);

                commandCallback = await orchestrationContext
                                  .CallActivityWithRetryAsync <string>(nameof(CallbackUrlGetActivity), new CallbackUrlGetActivity.Input()
                {
                    InstanceId = orchestrationContext.InstanceId, Command = functionContext.Command
                })
                                  .ConfigureAwait(true);

                commandMessage = new ProviderCommandMessage(functionContext.Command, commandCallback);

                if (!(functionContext.Command is ProviderRegisterCommand) && !functionContext.Provider.Registered.HasValue)
                {
                    log.LogInformation($"Register provider {functionContext.Provider.Id} for command {functionContext.Command.CommandId}");

                    await orchestrationContext
                    .RegisterProviderAsync(functionContext.Provider, true)
                    .ConfigureAwait(true);
                }

                if (!string.IsNullOrEmpty(functionContext.Command.ProjectId) && functionContext.Provider.PrincipalId.HasValue)
                {
                    log.LogInformation($"Enable provider {functionContext.Provider.Id} for command {functionContext.Command.CommandId}");

                    await orchestrationContext
                    .CallActivityWithRetryAsync(nameof(ProjectResourcesAccessActivity), new ProjectResourcesAccessActivity.Input()
                    {
                        ProjectId = functionContext.Command.ProjectId, PrincipalId = functionContext.Provider.PrincipalId.Value
                    })
                    .ConfigureAwait(true);
                }

                orchestrationContext.SetCustomStatus($"Augmenting command", log);

                functionContext.Command = await AugmentCommandAsync(orchestrationContext, functionContext)
                                          .ConfigureAwait(true);

                await orchestrationContext
                .AuditAsync(functionContext.Command, commandResult, functionContext.Provider)
                .ConfigureAwait(true);

                try
                {
                    orchestrationContext.SetCustomStatus($"Sending command", log);

                    commandResult = await orchestrationContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), new CommandSendActivity.Input()
                    {
                        Provider = functionContext.Provider, CommandMessage = commandMessage
                    })
                                    .ConfigureAwait(true);
                }
                catch (RetryCanceledException)
                {
                    commandResult = await orchestrationContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), new CommandResultFetchActivity.Input()
                    {
                        Provider = functionContext.Provider, CommandMessage = commandMessage
                    })
                                    .ConfigureAwait(true);
                }
                finally
                {
                    await orchestrationContext
                    .AuditAsync(functionContext.Command, commandResult, functionContext.Provider)
                    .ConfigureAwait(true);
                }

                if (!commandResult.RuntimeStatus.IsFinal())
                {
                    var commandTimeout = (commandResult.Timeout > TimeSpan.Zero && commandResult.Timeout < CommandResult.MaximumTimeout)
                        ? commandResult.Timeout         // use the timeout reported back by the provider
                        : CommandResult.MaximumTimeout; // use the defined maximum timeout

                    orchestrationContext.SetCustomStatus($"Waiting for command result", log);

                    commandResult = await orchestrationContext
                                    .WaitForExternalEvent <ICommandResult>(functionContext.Command.CommandId.ToString(), commandTimeout, null)
                                    .ConfigureAwait(true);

                    if (commandResult is null)
                    {
                        // provider ran into a timeout
                        // lets give our provider a last
                        // chance to return a command result

                        commandResult = await orchestrationContext
                                        .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), new CommandResultFetchActivity.Input()
                        {
                            Provider = functionContext.Provider, CommandMessage = commandMessage
                        })
                                        .ConfigureAwait(true);

                        if (commandResult.RuntimeStatus.IsActive())
                        {
                            // the last change result still doesn't report a final runtime status
                            // escalate the timeout by throwing an appropriate exception

                            throw new TimeoutException($"Provider '{functionContext.Provider.Id}' ran into timeout ({commandTimeout})");
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                orchestrationContext.SetCustomStatus($"Sending command failed: {exc.Message}", log, exc);

                commandResult ??= functionContext.Command.CreateResult();
                commandResult.Errors.Add(exc.AsSerializable());
            }
            finally
            {
                await orchestrationContext
                .AuditAsync(functionContext.Command, commandResult, functionContext.Provider)
                .ConfigureAwait(true);

                await ProcessOutputAsync(orchestrationContext, functionContext.Provider, functionContext.Command, commandResult)
                .ConfigureAwait(true);
            }

            return(commandResult);
        }
        public virtual async Task <Status> RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
        {
            string entLookup;

            try
            {
                var actionArgs = context.GetInput <ExecuteActionArguments>();

                entLookup = actionArgs.StateDetails.EnterpriseLookup;
            }
            catch
            {
                entLookup = context.GetInput <string>();
            }

            if (!context.IsReplaying)
            {
                log.LogInformation($"ValidatePointQueryLimits");
            }

            var genericRetryOptions = new RetryOptions(TimeSpan.FromSeconds(1), 3)
            {
                BackoffCoefficient = 1.5,
                Handle             = handleRetryException
            };

            var status = Status.Initialized;

            var entsWithLicense = await context.CallActivityWithRetryAsync <List <string> >(
                "ValidatePointQueryLimitsOrchestration_FindEnterprisesWithForecastLicense", genericRetryOptions, entLookup);

            var invalidEnts = new List <Tuple <string, Status> >();

            if (!entsWithLicense.IsNullOrEmpty())
            {
                // if (!context.IsReplaying)
                //     log.LogInformation($"...: {stateCtxt.ToJSON()}");

                var entValidateTasks = entsWithLicense.Select(entLookup =>
                {
                    return(context.CallActivityWithRetryAsync <Tuple <string, Status> >(
                               "ValidatePointQueryLimitsOrchestration_ValidateEnterprisesForecastPointQueries", genericRetryOptions, entLookup));
                }).ToList();

                invalidEnts = (await Task.WhenAll(entValidateTasks)).Where(s => !s.Item2).ToList();
            }
            // else if (!context.IsReplaying)
            //     log.LogError($"...: {stateCtxt.ToJSON()}");

            if (invalidEnts.Any())
            {
                // if (!context.IsReplaying)
                //     log.LogInformation($"...: {stateCtxt.ToJSON()}");

                var revokeTasks = invalidEnts.Select(invalidEnt =>
                {
                    return(context.CallActivityWithRetryAsync <Status>("ValidatePointQueryLimitsOrchestration_RevokeEnterpriseForecastLicense",
                                                                       genericRetryOptions, invalidEnt.Item1));
                }).ToList();

                var revokeStati = await Task.WhenAll(revokeTasks);

                status = revokeStati.All(rs => rs) ? Status.Success : Status.GeneralError.Clone("Not all licenses properly revoked");

                if (!status)
                {
                    status.Metadata["RevokeStati"] = revokeStati.JSONConvert <JArray>();
                }
            }
            // else if (!context.IsReplaying)
            //     log.LogError($"...: {stateCtxt.ToJSON()}");

            return(status);
        }
        public static async Task RunOrchestration(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            var(provider, command) = functionContext
                                     .GetInput <(ProviderDocument, ProviderRegisterCommand)>();

            try
            {
                if (command is null)
                {
                    // no command was given !!!
                    // restart the orchestration
                    // with a new command instance.

                    functionContext.SetCustomStatus("Creating command", log);

                    var systemUser = await functionContext
                                     .CallActivityWithRetryAsync <UserDocument>(nameof(TeamCloudSystemUserActivity), null)
                                     .ConfigureAwait(true);

                    var providerCommand = new ProviderRegisterCommand
                                          (
                        systemUser.PopulateExternalModel(),
                        new ProviderConfiguration()
                                          );

                    functionContext
                    .ContinueAsNew((provider, providerCommand));
                }
                else if (provider is null)
                {
                    // no provider was given !!!
                    // fan out registration with
                    // one orchestration per provider.

                    var providers = await functionContext
                                    .ListProvidersAsync()
                                    .ConfigureAwait(true);

                    if (providers.Any())
                    {
                        functionContext.SetCustomStatus($"Register provider/s", log);

                        var tasks = providers
                                    .Select(provider => functionContext.CallSubOrchestratorWithRetryAsync(nameof(ProviderRegisterOrchestration), (provider, command)));

                        await Task
                        .WhenAll(tasks)
                        .ConfigureAwait(true);
                    }
                }
                else
                {
                    command.Payload
                    .TeamCloudApplicationInsightsKey = await functionContext
                                                       .GetInstrumentationKeyAsync()
                                                       .ConfigureAwait(true);

                    command.Payload
                    .Properties = provider.Properties;

                    functionContext.SetCustomStatus($"Sending command", log);

                    var commandResult = await functionContext
                                        .SendProviderCommandAsync <ProviderRegisterCommand, ProviderRegisterCommandResult>(command, provider)
                                        .ConfigureAwait(true);

                    using (await functionContext.LockContainerDocumentAsync(provider).ConfigureAwait(true))
                    {
                        provider = await functionContext
                                   .GetProviderAsync(provider.Id)
                                   .ConfigureAwait(true);

                        if (provider is null)
                        {
                            log.LogWarning($"Provider '{provider.Id}' registration skipped - provider no longer exists");
                        }
                        else
                        {
                            provider.PrincipalId       = commandResult.Result.PrincipalId;
                            provider.CommandMode       = commandResult.Result.CommandMode;
                            provider.Registered        = functionContext.CurrentUtcDateTime;
                            provider.ResourceProviders = commandResult.Result.ResourceProviders;
                            provider.Properties        = provider.Properties.Override(commandResult.Result.Properties);

                            functionContext.SetCustomStatus($"Updating provider", log);

                            provider = await functionContext
                                       .SetProviderAsync(provider)
                                       .ConfigureAwait(true);

                            if (provider.PrincipalId.HasValue)
                            {
                                functionContext.SetCustomStatus($"Resolving provider identity", log);

                                var providerUser = await functionContext
                                                   .GetUserAsync(provider.PrincipalId.Value.ToString(), allowUnsafe : true)
                                                   .ConfigureAwait(true);

                                if (providerUser is null)
                                {
                                    providerUser = new UserDocument
                                    {
                                        Id       = provider.PrincipalId.Value.ToString(),
                                        Role     = TeamCloudUserRole.Provider,
                                        UserType = UserType.Provider
                                    };

                                    functionContext.SetCustomStatus($"Granting provider access", log);

                                    _ = await functionContext
                                        .SetUserTeamCloudInfoAsync(providerUser, allowUnsafe : true)
                                        .ConfigureAwait(true);
                                }
                            }
                        }
                    }

                    functionContext.SetCustomStatus($"Provider registered", log);
                }
            }
            catch (Exception exc)
            {
                if (provider != null)
                {
                    functionContext.SetCustomStatus($"Failed to register provider '{provider.Id}' - {exc.Message}", log, exc);
                }
                else if (command != null)
                {
                    functionContext.SetCustomStatus($"Failed to register providers - {exc.Message}", log, exc);
                }
                else
                {
                    functionContext.SetCustomStatus($"Failed to initiate provider registration - {exc.Message}", log, exc);
                }
            }
        }
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            var exportRequiredData         = context.GetInput <ExportDataRequirement>();
            var sentNotificationDataEntity = exportRequiredData.NotificationDataEntity;
            var exportDataEntity           = exportRequiredData.ExportDataEntity;

            if (!context.IsReplaying)
            {
                log.LogInformation($"Start to export the notification {sentNotificationDataEntity.Id}!");
            }

            try
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation("About to update export is in progress.");
                }

                exportDataEntity.Status = ExportStatus.InProgress.ToString();
                await context.CallActivityWithRetryAsync(
                    FunctionNames.UpdateExportDataActivity,
                    FunctionSettings.DefaultRetryOptions,
                    exportDataEntity);

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to get the metadata information.");
                }

                var metaData = await context.CallActivityWithRetryAsync <Metadata>(
                    FunctionNames.GetMetadataActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (sentNotificationDataEntity, exportDataEntity));

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to start file upload.");
                }

                await context.CallActivityWithRetryAsync(
                    FunctionNames.UploadActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (sentNotificationDataEntity, metaData, exportDataEntity.FileName));

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to send file card.");
                }

                var consentId = await context.CallActivityWithRetryAsync <string>(
                    FunctionNames.SendFileCardActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (exportRequiredData.UserId, exportRequiredData.NotificationDataEntity.Id, exportDataEntity.FileName));

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to update export is completed.");
                }

                exportDataEntity.FileConsentId = consentId;
                exportDataEntity.Status        = ExportStatus.Completed.ToString();
                await context.CallActivityWithRetryAsync(
                    FunctionNames.UpdateExportDataActivity,
                    FunctionSettings.DefaultRetryOptions,
                    exportDataEntity);

                log.LogInformation($"ExportOrchestration is successful for notification id:{sentNotificationDataEntity.Id}!");
            }
            catch (Exception ex)
            {
                log.LogError(ex, $"Failed to export notification {sentNotificationDataEntity.Id} : {ex.Message}");
                await context.CallActivityWithRetryAsync(
                    FunctionNames.HandleExportFailureActivity,
                    FunctionSettings.DefaultRetryOptions,
                    exportDataEntity);
            }
        }
Beispiel #12
0
        public static async Task RunOrchestration(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            (string operationActivityName, object operationActivityInput, string operationInstanceId)
                = functionContext.GetInput <(string, object, string)>();

            var operationLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            try
            {
                if (string.IsNullOrEmpty(operationInstanceId))
                {
                    operationInstanceId = await functionContext
                                          .CallActivityWithRetryAsync <string>(operationActivityName, operationActivityInput)
                                          .ConfigureAwait(true);

                    if (!string.IsNullOrEmpty(operationInstanceId))
                    {
                        operationLog.LogInformation($"Azure DevOps Operation '{operationInstanceId}' started");

                        functionContext.ContinueAsNew((operationActivityName, operationActivityInput, operationInstanceId));
                    }
                }
                else
                {
                    await functionContext
                    .CreateTimer(functionContext.CurrentUtcDateTime.AddSeconds(10), CancellationToken.None)
                    .ConfigureAwait(true);

                    var status = await functionContext
                                 .CallActivityWithRetryAsync <OperationStatus>(nameof(OperationStatusActivity), operationInstanceId)
                                 .ConfigureAwait(true);

                    if (status.IsProgressStatus())
                    {
                        operationLog.LogInformation($"Azure DevOps Operation '{operationInstanceId}' in progress");

                        functionContext
                        .ContinueAsNew((operationActivityName, operationActivityInput, operationInstanceId));
                    }
                    else if (status.IsErrorStatus())
                    {
                        var operationError = await functionContext
                                             .CallActivityWithRetryAsync <string>(nameof(OperationErrorActivity), operationInstanceId)
                                             .ConfigureAwait(false);

                        operationLog.LogWarning($"Azure DevOps Operation '{operationInstanceId}' failed: {operationError ?? "Unknown error"}");

                        if (string.IsNullOrEmpty(operationError))
                        {
                            throw new Exception($"Operation '{operationInstanceId}' failed");
                        }
                        else
                        {
                            throw new Exception(operationError);
                        }
                    }
                    else
                    {
                        operationLog.LogInformation($"Azure DevOps Operation '{operationInstanceId}' succeeded");
                    }
                }
            }
            catch (Exception exc)
            {
                operationLog.LogError(exc, $"Orchestration '{nameof(OperationOrchestration)}' failed: {exc.Message}");

                throw exc.AsSerializable();
            }
        }
Beispiel #13
0
 private async Task CallAsyncWithActivity(IDurableOrchestrationContext context, int maxNumberOfAttempts)
 {
     var retryOptions = new RetryOptions(TimeSpan.FromSeconds(5), maxNumberOfAttempts);
     await context.CallActivityWithRetryAsync(nameof(ActivityWithRetryAsync), retryOptions, context.GetInput <string>());
 }
Beispiel #14
0
 private async Task PassingNonSerializableModel(IDurableOrchestrationContext context, int maxNumberOfAttempts)
 {
     var retryOptions = new RetryOptions(TimeSpan.FromSeconds(5), maxNumberOfAttempts);
     await context.CallActivityWithRetryAsync(nameof(ActivityWithRetryAsyncNonSerializable), retryOptions, new MemoryStream(100));
 }
 public static Task <TeamCloudInstance> GetTeamCloudAsync(this IDurableOrchestrationContext durableOrchestrationContext)
 {
     return(durableOrchestrationContext
            .CallActivityWithRetryAsync <TeamCloudInstance>(nameof(TeamCloudGetActivity), null));
 }
Beispiel #16
0
 public static Task <UserDocument> GetUserAsync(this IDurableOrchestrationContext functionContext, string userId, bool allowUnsafe = false)
 => functionContext.IsLockedBy <UserDocument>(userId) || allowUnsafe
     ? functionContext.CallActivityWithRetryAsync <UserDocument>(nameof(UserGetActivity), userId)
     : throw new NotSupportedException($"Unable to get user '{userId}' without acquired lock");
Beispiel #17
0
 public static Task <UserDocument> SetUserProjectMembershipAsync(this IDurableOrchestrationContext functionContext, UserDocument user, string projectId, bool allowUnsafe = false)
 => functionContext.IsLockedBy <UserDocument>(user.Id) || allowUnsafe
     ? functionContext.CallActivityWithRetryAsync <UserDocument>(nameof(UserProjectMembershipSetActivity), (user, projectId))
     : throw new NotSupportedException($"Unable to create or update project membership without acquired lock for user {user.Id}");
Beispiel #18
0
 public static Task <IEnumerable <ProviderDocument> > ListProvidersAsync(this IDurableOrchestrationContext durableOrchestrationContext, bool includeServiceProviders = false)
 => durableOrchestrationContext.CallActivityWithRetryAsync <IEnumerable <ProviderDocument> >(nameof(ProviderListActivity), includeServiceProviders);
Beispiel #19
0
 public static Task SetAppSettingAsync(this IDurableOrchestrationContext functionContext, string key, string value = default)
 => functionContext.CallActivityWithRetryAsync(nameof(ProviderCommandAppSettingActivity), (key, value));
Beispiel #20
0
 public static Task <Project> CreateProjectAsync(this IDurableOrchestrationContext functionContext, Project project)
 => functionContext.CallActivityWithRetryAsync <Project>(nameof(ProjectCreateActivity), project);
    public override async Task <ICommandResult> HandleAsync(ComponentTaskRunCommand command, IAsyncCollector <ICommand> commandQueue, IDurableOrchestrationContext orchestrationContext, ILogger log)

    {
        if (command is null)
        {
            throw new ArgumentNullException(nameof(command));
        }

        if (commandQueue is null)
        {
            throw new ArgumentNullException(nameof(commandQueue));
        }

        if (orchestrationContext is null)
        {
            throw new ArgumentNullException(nameof(orchestrationContext));
        }

        if (log is null)
        {
            throw new ArgumentNullException(nameof(log));
        }

        var commandResult = command.CreateResult();

        var organization = await WaitForOrganizationInitAsync(orchestrationContext, command)
                           .ConfigureAwait(true);

        var project = await WaitForProjectInitAsync(orchestrationContext, command)
                      .ConfigureAwait(true);

        var component = await orchestrationContext
                        .CallActivityWithRetryAsync <Component>(nameof(ComponentGetActivity), new ComponentGetActivity.Input()
        {
            ComponentId = command.Payload.ComponentId, ProjectId = command.Payload.ProjectId
        })
                        .ConfigureAwait(true);

        using (await orchestrationContext.LockContainerDocumentAsync(component).ConfigureAwait(true))
        {
            commandResult.Result = await orchestrationContext
                                   .CallActivityWithRetryAsync <ComponentTask>(nameof(ComponentTaskGetActivity), new ComponentTaskGetActivity.Input()
            {
                ComponentTaskId = command.Payload.Id, ComponentId = command.Payload.ComponentId
            })
                                   .ConfigureAwait(true) ?? command.Payload;

            if (commandResult.Result.TaskState != TaskState.Canceled)
            {
                try
                {
                    commandResult.Result = await UpdateComponentTaskAsync(orchestrationContext, commandResult.Result, TaskState.Initializing)
                                           .ConfigureAwait(true);

                    if (!AzureResourceIdentifier.TryParse(component.IdentityId, out var _))
                    {
                        // ensure every component has an identity assigned that can be used
                        // as the identity of the task runner container to access azure or
                        // call back into teamcloud using the azure cli extension

                        component = await orchestrationContext
                                    .CallActivityWithRetryAsync <Component>(nameof(ComponentIdentityActivity), new ComponentIdentityActivity.Input()
                        {
                            Component = component
                        })
                                    .ConfigureAwait(true);
                    }

                    commandResult.Result = await UpdateComponentTaskAsync(orchestrationContext, commandResult.Result, TaskState.Processing)
                                           .ConfigureAwait(true);

                    await(command.Payload.Type switch
                    {
                        ComponentTaskType.Create => ProcessCreateCommandAsync(),
                        ComponentTaskType.Delete => ProcessDeleteCommandAsync(),
                        ComponentTaskType.Custom => ProcessCustomCommandAsync(),

                        _ => throw new NotSupportedException($"The command type '{command.Payload.Type}' is not supported")
                    }).ConfigureAwait(true);

                    commandResult.Result = await UpdateComponentTaskAsync(orchestrationContext, commandResult.Result, TaskState.Succeeded)
                                           .ConfigureAwait(true);
                }
 public async Task RunOrchestrator(
     [OrchestrationTrigger] IDurableOrchestrationContext context)
 {
     await context.CallActivityWithRetryAsync(DurableFunctionNameConstants.EMAIL_SEND, new RetryOptions(TimeSpan.FromMinutes(5), 3), null);
 }
Beispiel #23
0
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            var notificationDataEntity = context.GetInput <NotificationDataEntity>();

            if (!context.IsReplaying)
            {
                log.LogInformation($"Start to prepare to send the notification {notificationDataEntity.Id}!");
            }

            try
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation("About to store message content.");
                }

                await context.CallActivityWithRetryAsync(
                    FunctionNames.StoreMessageActivity,
                    FunctionSettings.DefaultRetryOptions,
                    notificationDataEntity);

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to sync recipients.");
                }

                await context.CallSubOrchestratorWithRetryAsync(
                    FunctionNames.SyncRecipientsOrchestrator,
                    FunctionSettings.DefaultRetryOptions,
                    notificationDataEntity);

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to create conversation for recipients if required.");
                }

                await context.CallSubOrchestratorWithRetryAsync(
                    FunctionNames.TeamsConversationOrchestrator,
                    FunctionSettings.DefaultRetryOptions,
                    notificationDataEntity);

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to send messages to send queue.");
                }

                await context.CallSubOrchestratorWithRetryAsync(
                    FunctionNames.SendQueueOrchestrator,
                    FunctionSettings.DefaultRetryOptions,
                    notificationDataEntity);

                log.LogInformation($"PrepareToSendOrchestrator successfully completed for notification: {notificationDataEntity.Id}!");
            }
            catch (Exception ex)
            {
                var errorMessage = $"PrepareToSendOrchestrator failed for notification: {notificationDataEntity.Id}. Exception Message: {ex.Message}";
                log.LogError(ex, errorMessage);

                await context.CallActivityWithRetryAsync(
                    FunctionNames.HandleFailureActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (notificationDataEntity, ex));
            }
        }
Beispiel #24
0
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            var notification = context.GetInput <NotificationDataEntity>();

            // Update notification status.
            await context.CallActivityWithRetryAsync(
                FunctionNames.UpdateNotificationStatusActivity,
                FunctionSettings.DefaultRetryOptions,
                (notification.Id, NotificationStatus.Sending));

            if (!context.IsReplaying)
            {
                log.LogInformation("About to get all recipients.");
            }

            var results = await context.CallActivityWithRetryAsync <(IEnumerable <SentNotificationDataEntity>, TableContinuationToken)>(
                FunctionNames.GetRecipientsActivity,
                FunctionSettings.DefaultRetryOptions,
                notification);

            var recipientsList = new List <SentNotificationDataEntity>();

            if (results.Item1 != null)
            {
                recipientsList.AddRange(results.Item1.ToList());
            }

            while (results.Item2 != null)
            {
                results = await context.CallActivityWithRetryAsync <(IEnumerable <SentNotificationDataEntity>, TableContinuationToken)>(
                    FunctionNames.GetRecipientsByTokenActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (notification.Id, results.Item2));

                if (results.Item1 != null)
                {
                    recipientsList.AddRange(results.Item1);
                }
            }

            if (!context.IsReplaying)
            {
                log.LogInformation("About to send data aggregration message to data queue.");
            }

            await context.CallActivityWithRetryAsync(
                FunctionNames.DataAggregationTriggerActivity,
                FunctionSettings.DefaultRetryOptions,
                (notification.Id, recipientsList.Count));

            var batches = SeparateIntoBatches(recipientsList);

            var totalBatchCount = batches.Count;

            if (!context.IsReplaying)
            {
                log.LogInformation($"About to process {totalBatchCount} batches.");
            }

            var tasks = new List <Task>();

            for (var batchIndex = 0; batchIndex < totalBatchCount; batchIndex++)
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation($"About to process batch {batchIndex + 1} / {totalBatchCount}");
                }

                var task = context.CallActivityWithRetryAsync(
                    FunctionNames.SendBatchMessagesActivity,
                    FunctionSettings.DefaultRetryOptions,
                    (notification, batches[batchIndex]));

                tasks.Add(task);
            }

            // Fan-out Fan-in
            await Task.WhenAll(tasks);
        }
 private static async Task PerformLearnerMatch(IDurableOrchestrationContext context, ApprenticeshipIncentiveOutput incentive)
 {
     await context.CallActivityWithRetryAsync(nameof(LearnerMatchAndUpdate),
                                              new RetryOptions(TimeSpan.FromSeconds(1), 3),
                                              new LearnerMatchInput { ApprenticeshipIncentiveId = incentive.Id });
 }
Beispiel #26
0
    public static async Task RunOrchestrator(
        [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext,

        ILogger log)
    {
        if (orchestrationContext is null)
        {
            throw new ArgumentNullException(nameof(orchestrationContext));
        }

        var functionInput = orchestrationContext.GetInput <Input>();
        var functionLog   = orchestrationContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

        try
        {
            if (string.IsNullOrEmpty(functionInput.DeploymentResourceId))
            {
                orchestrationContext.SetCustomStatus($"Starting deployment using activity '{functionInput.DeploymentActivityName}'", functionLog);

                functionInput.DeploymentResourceId = await orchestrationContext
                                                     .CallActivityWithRetryAsync <string>(functionInput.DeploymentActivityName, functionInput.DeploymentActivityInput)
                                                     .ConfigureAwait(true);

                if (!string.IsNullOrEmpty(functionInput.DeploymentResourceId))
                {
                    orchestrationContext.SetCustomStatus($"Monitoring deployment '{functionInput.DeploymentResourceId}'", functionLog);

                    orchestrationContext.ContinueAsNew(functionInput);
                }
            }
            else
            {
                await orchestrationContext
                .CreateTimer(orchestrationContext.CurrentUtcDateTime.AddSeconds(10), CancellationToken.None)
                .ConfigureAwait(true);

                var state = await orchestrationContext
                            .CallActivityWithRetryAsync <AzureDeploymentState>(nameof(AzureDeploymentStateActivity), functionInput.DeploymentResourceId)
                            .ConfigureAwait(true);

                if (state.IsProgressState())
                {
                    orchestrationContext
                    .ContinueAsNew(functionInput);
                }
                else if (state.IsErrorState())
                {
                    var errors = (await orchestrationContext
                                  .CallActivityWithRetryAsync <IEnumerable <string> >(nameof(AzureDeploymentErrorsActivity), functionInput.DeploymentResourceId)
                                  .ConfigureAwait(true)) ?? Enumerable.Empty <string>();

                    foreach (var error in errors)
                    {
                        functionLog.LogError($"Deployment '{functionInput.DeploymentResourceId}' reported error: {error}");
                    }

                    throw new AzureDeploymentException($"Deployment '{functionInput.DeploymentResourceId}' failed", functionInput.DeploymentResourceId, errors.ToArray());
                }
                else
                {
                    var output = await orchestrationContext
                                 .GetDeploymentOutputAsync(functionInput.DeploymentResourceId)
                                 .ConfigureAwait(true);

                    if (!string.IsNullOrEmpty(functionInput.DeploymentOutputEventName))
                    {
                        await orchestrationContext
                        .RaiseEventAsync(functionInput.DeploymentOwnerInstanceId, functionInput.DeploymentOutputEventName, output)
                        .ConfigureAwait(true);
                    }

                    orchestrationContext.SetOutput(output);
                }
            }
        }
        catch (Exception exc)
        {
            functionLog.LogError(exc, $"Orchestration '{nameof(AzureDeploymentOrchestration)}' failed: {exc.Message}");

            throw exc.AsSerializable();
        }
    }
 public static Task <IEnumerable <Project> > ListProjectsAsync(this IDurableOrchestrationContext durableOrchestrationContext)
 => durableOrchestrationContext.CallActivityWithRetryAsync <IEnumerable <Project> >(nameof(ProjectListActivity), null);
Beispiel #28
0
 public static Task <TeamCloudInstanceDocument> GetTeamCloudAsync(this IDurableOrchestrationContext durableOrchestrationContext)
 => durableOrchestrationContext.CallActivityWithRetryAsync <TeamCloudInstanceDocument>(nameof(TeamCloudGetActivity), null);
        private static async Task <OrchestratorProjectCreateCommandResult> ProvisionAsync(IDurableOrchestrationContext orchestrationContext, OrchestratorProjectCreateCommand command, ILogger log)
        {
            var teamCloud = await orchestrationContext
                            .GetTeamCloudAsync()
                            .ConfigureAwait(true);

            var commandResult = command.CreateResult();

            var project = commandResult.Result = command.Payload;

            project.Tags = teamCloud.Tags.Override(project.Tags);

            var projectUsers = project.Users.ToList();

            var providers = await orchestrationContext
                            .ListProvidersAsync(project.Type.Providers.Select(p => p.Id).ToList())
                            .ConfigureAwait(true);

            var providerUserTasks = providers
                                    .Where(p => p.PrincipalId.HasValue)
                                    .Select(p => orchestrationContext.GetUserAsync(p.PrincipalId.Value.ToString(), allowUnsafe: true));

            var providerUsers = await Task.WhenAll(providerUserTasks)
                                .ConfigureAwait(true);

            foreach (var u in providerUsers)
            {
                u.EnsureProjectMembership(project.Id, ProjectUserRole.Provider);
            }

            projectUsers.AddRange(providerUsers);

            using (await orchestrationContext.LockContainerDocumentAsync(project).ConfigureAwait(true))
            {
                orchestrationContext.SetCustomStatus($"Creating project", log);

                project = commandResult.Result = await orchestrationContext
                                                 .CreateProjectAsync(project)
                                                 .ConfigureAwait(true);

                orchestrationContext.SetCustomStatus($"Adding users", log);

                project.Users = await Task
                                .WhenAll(projectUsers.Select(user => orchestrationContext.SetUserProjectMembershipAsync(user, project.Id, allowUnsafe: true)))
                                .ConfigureAwait(true);
            }

            orchestrationContext.SetCustomStatus($"Allocating subscription", log);

            var subscriptionId = await orchestrationContext
                                 .CallActivityWithRetryAsync <Guid>(nameof(ProjectSubscriptionSelectActivity), project)
                                 .ConfigureAwait(true);

            orchestrationContext.SetCustomStatus($"Initializing subscription", log);

            await orchestrationContext
            .InitializeSubscriptionAsync(subscriptionId, waitFor : false)
            .ConfigureAwait(true);

            orchestrationContext.SetCustomStatus($"Provisioning resources", log);

            var deploymentOutput = await orchestrationContext
                                   .CallDeploymentAsync(nameof(ProjectResourcesCreateActivity), new ProjectResourcesCreateActivity.Input()
            {
                Project = project, SubscriptionId = subscriptionId
            })
                                   .ConfigureAwait(true);

            using (await orchestrationContext.LockContainerDocumentAsync(project).ConfigureAwait(true))
            {
                project.ResourceGroup = new AzureResourceGroup()
                {
                    SubscriptionId = subscriptionId,
                    Region         = project.Type.Region,
                    Id             = (string)deploymentOutput.GetValueOrDefault("resourceGroupId", default(string)),
                    Name           = (string)deploymentOutput.GetValueOrDefault("resourceGroupName", default(string))
                };

                orchestrationContext.SetCustomStatus($"Provisioning identity", log);

                project.Identity = await orchestrationContext
                                   .CallActivityWithRetryAsync <ProjectIdentity>(nameof(ProjectIdentityCreateActivity), project)
                                   .ConfigureAwait(true);

                project = commandResult.Result = await orchestrationContext
                                                 .SetProjectAsync(project)
                                                 .ConfigureAwait(true);
            }

            orchestrationContext.SetCustomStatus($"Tagging resources", log);

            await orchestrationContext
            .CallActivityWithRetryAsync(nameof(ProjectResourcesTagActivity), project)
            .ConfigureAwait(true);

            orchestrationContext.SetCustomStatus($"Registering required resource providers", log);

            await orchestrationContext
            .RegisterResourceProvidersAsync(project)
            .ConfigureAwait(true);

            orchestrationContext.SetCustomStatus($"Sending provider commands", log);

            var providerCommand = new ProviderProjectCreateCommand
                                  (
                command.User.PopulateExternalModel(),
                project.PopulateExternalModel(),
                command.CommandId
                                  );

            var providerResults = await orchestrationContext
                                  .SendProviderCommandAsync <ProviderProjectCreateCommand, ProviderProjectCreateCommandResult>(providerCommand, project, failFast : true)
                                  .ConfigureAwait(true);

            var providerException = providerResults.Values?
                                    .SelectMany(result => result.Errors ?? new List <CommandError>())
                                    .ToException();

            if (providerException != null)
            {
                throw providerException;
            }

            return(commandResult);
        }
Beispiel #30
0
 /// <summary>
 /// Calls an activity function with the default retry options.
 /// </summary>
 /// <typeparam name="TResult">The type for the result returned by the activity function.</typeparam>
 /// <param name="functionName">The name of the function to call.</param>
 /// <param name="input">An optional input object to send to the function.</param>
 /// <returns>Returns the object returned by the activity function.</returns>
 public static async Task <TResult> CallActivityWithDefaultRetryAsync <TResult>(this IDurableOrchestrationContext context, string functionName, object input = null)
 {
     return(await context.CallActivityWithRetryAsync <TResult>(functionName, DefaultRetryOptions, input));
 }