Example #1
0
        protected async Task <ICommandResult> SendCommandAsync(IProviderCommand providerCommand, bool waitForCallbackResult = false, TimeSpan?waitForCallbackResultTimeout = default)
        {
            if (providerCommand is null)
            {
                throw new ArgumentNullException(nameof(providerCommand));
            }

            await providerCommand
            .ValidateAsync(throwOnValidationError : true)
            .ConfigureAwait(false);

            var message = new ProviderCommandMessage(
                providerCommand,
                OrchestratorService.Instance.GetCallbackUrl(providerCommand));

            Logger.LogInformation($"{nameof(SendCommandAsync)}:\n{JsonConvert.SerializeObject(providerCommand, Formatting.Indented)}");

            var response = await ProviderService.BaseUrl
                           .AppendPathSegment("api/command")
                           .PostJsonAsync(message)
                           .ConfigureAwait(false);

            var commandResult = await response.Content
                                .ReadAsAsync <ICommandResult>()
                                .ConfigureAwait(false);

            if (waitForCallbackResult)
            {
                commandResult = await GetCommandResultAsync(providerCommand.CommandId, waitForCallbackResult, waitForCallbackResultTimeout.GetValueOrDefault(commandResult.Timeout))
                                .ConfigureAwait(false);
            }
            else
            {
                ProviderServicePrincipalId = commandResult is ProviderRegisterCommandResult providerRegisterCommandResult
                    ? providerRegisterCommandResult.Result.PrincipalId
                    : ProviderServicePrincipalId;
            }

            return(commandResult);
        }
Example #2
0
 public ProviderCommandException(string message, IProviderCommand providerCommand, Exception inner) : base(message, providerCommand, inner)
 {
 }
Example #3
0
 public ProviderCommandException(string message, IProviderCommand providerCommand) : base(message, providerCommand)
 {
 }
        private static async Task ProcessOutputAsync(IDurableOrchestrationContext functionContext, Provider provider, IProviderCommand command, ICommandResult commandResult)
        {
            if (command.ProjectId.HasValue && commandResult is ICommandResult <ProviderOutput> providerOutputResult)
            {
                var project = await functionContext
                              .GetProjectAsync(command.ProjectId.Value)
                              .ConfigureAwait(true);

                using (await functionContext.LockAsync(project).ConfigureAwait(true))
                {
                    project = await functionContext
                              .GetProjectAsync(command.ProjectId.Value)
                              .ConfigureAwait(true);

                    var providerReference = project.Type.Providers
                                            .SingleOrDefault(pr => pr.Id == provider.Id);

                    if (providerReference != null)
                    {
                        var commandType = command.GetType().Name;

                        var resultProperties = providerOutputResult?.Result?.Properties ?? new Dictionary <string, string>();

                        if (!providerReference.Metadata.TryAdd(commandType, resultProperties))
                        {
                            providerReference.Metadata[commandType] =
                                (providerReference.Metadata[commandType] ?? new Dictionary <string, string>()).Override(resultProperties);
                        }

                        project = await functionContext
                                  .SetProjectAsync(project)
                                  .ConfigureAwait(true);
                    }
                }
            }
        }
        private static async Task <IProviderCommand> AugmentCommandAsync(IDurableOrchestrationContext functionContext, Provider provider, IProviderCommand command)
        {
            var teamCloud = await functionContext
                            .GetTeamCloudAsync()
                            .ConfigureAwait(true);

            var providerProperties = teamCloud.Properties;

            if (command.ProjectId.HasValue)
            {
                var project = await functionContext
                              .GetProjectAsync(command.ProjectId.Value)
                              .ConfigureAwait(true);

                var providerReference = project.Type.Providers
                                        .Single(pr => pr.Id == provider.Id);

                command.Properties = providerProperties.Resolve(project)
                                     .Override(project.Type.Properties.Resolve(project))
                                     .Override(project.Properties.Resolve(project))
                                     .Override(provider.Properties.Resolve(project))
                                     .Override(providerReference.Properties.Resolve(project));
            }
            else
            {
                command.Properties = providerProperties.Resolve()
                                     .Override(provider.Properties.Resolve());
            }

            if (command.Payload is IProperties payloadWithProperties)
            {
                payloadWithProperties.Properties = payloadWithProperties.Properties.Resolve(command.Payload);
            }

            return(command);
        }
        private static Task EnableProviderAsync(IDurableOrchestrationContext functionContext, IProviderCommand providerCommand, Provider provider)
        {
            if (!providerCommand.ProjectId.HasValue || !provider.PrincipalId.HasValue)
            {
                return(Task.CompletedTask);
            }

            return(functionContext
                   .CallActivityWithRetryAsync(nameof(AzureResourceGroupContributorActivity), (providerCommand.ProjectId.Value, provider.PrincipalId.Value)));
        }
        private static Task RegisterProviderAsync(IDurableOrchestrationContext functionContext, IProviderCommand providerCommand, Provider provider)
        {
            if (providerCommand is ProviderRegisterCommand || provider.Registered.HasValue)
            {
                return(Task.CompletedTask);
            }

            return(functionContext
                   .RegisterProviderAsync(provider, true));
        }
Example #8
0
        private static async Task <ICommandResult> ProcessCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log)
        {
            var commandMessage  = default(ICommandMessage);
            var commandCallback = default(string);

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

                commandCallback = await functionContext
                                  .CallActivityWithRetryAsync <string>(nameof(CallbackUrlGetActivity), (functionContext.InstanceId, command))
                                  .ConfigureAwait(true);

                commandMessage = new ProviderCommandMessage(command, commandCallback);

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

                    await functionContext
                    .RegisterProviderAsync(provider, true)
                    .ConfigureAwait(true);
                }

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

                    await functionContext
                    .CallActivityWithRetryAsync(nameof(ProjectResourcesAccessActivity), (command.ProjectId, provider.PrincipalId.Value))
                    .ConfigureAwait(true);
                }

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

                command = await AugmentCommandAsync(functionContext, provider, command)
                          .ConfigureAwait(true);

                await functionContext
                .AuditAsync(command, commandResult, provider)
                .ConfigureAwait(true);

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

                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                catch (RetryCanceledException)
                {
                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                finally
                {
                    await functionContext
                    .AuditAsync(command, commandResult, provider)
                    .ConfigureAwait(true);
                }

                if (commandResult.RuntimeStatus.IsActive())
                {
                    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

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

                    commandResult = await functionContext
                                    .WaitForExternalEvent <ICommandResult>(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 functionContext
                                        .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, 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 '{provider.Id}' ran into timeout ({commandTimeout})");
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                functionContext.SetCustomStatus($"Sending command failed: {exc.Message}", log, exc);

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

                await ProcessOutputAsync(functionContext, provider, command, commandResult)
                .ConfigureAwait(true);
            }

            return(commandResult);
        }
Example #9
0
        private static async Task <IProviderCommand> AugmentCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command)
        {
            if (!string.IsNullOrEmpty(command.ProjectId))
            {
                var project = await functionContext
                              .GetProjectAsync(command.ProjectId, allowUnsafe : true)
                              .ConfigureAwait(true);

                var providerReference = project.Type.Providers
                                        .Single(pr => pr.Id == provider.Id);

                command.Properties = provider.Properties.Resolve(project)
                                     .Override(project.Type.Properties.Resolve(project))
                                     .Override(project.Properties.Resolve(project))
                                     .Override(provider.Properties.Resolve(project))
                                     .Override(providerReference.Properties.Resolve(project));
            }
            else
            {
                command.Properties = provider.Properties.Resolve();
            }

            if (command.Payload is IProperties payloadWithProperties)
            {
                payloadWithProperties.Properties = payloadWithProperties.Properties.Resolve(command.Payload);
            }

            return(command);
        }
Example #10
0
        private static async Task <ICommandResult> SwitchCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log)
        {
            try
            {
                await functionContext
                .AuditAsync(command, commandResult, provider)
                .ConfigureAwait(true);

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

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

                functionContext.ContinueAsNew((
                                                  new ProviderProjectUpdateCommand
                                                  (
                                                      command.BaseApi,
                                                      command.User as Model.Data.User,
                                                      project.PopulateExternalModel(),
                                                      command.CommandId),
                                                  provider
                                                  )
                                              );
            }
            catch (Exception exc)
            {
                functionContext.SetCustomStatus($"Switching command failed: {exc.Message}", log, exc);

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }

            return(commandResult);
        }
Example #11
0
 public ProviderCommandMessage(IProviderCommand command, string callbackUrl)
 {
     Command     = command ?? throw new ArgumentNullException(nameof(command));
     CallbackUrl = callbackUrl ?? throw new ArgumentNullException(nameof(callbackUrl));
 }
Example #12
0
 public string GetCallbackUrl(IProviderCommand providerCommand)
 => BaseUrl.AppendPathSegment($"callback/{providerCommand.CommandId}");
Example #13
0
 public ProviderCommandMessage(IProviderCommand command, string callbackUrl = null) : base(command)
 {
     CallbackUrl = callbackUrl;
 }