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); }
public ProviderCommandException(string message, IProviderCommand providerCommand, Exception inner) : base(message, providerCommand, inner) { }
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)); }
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); }
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); }
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); }
public ProviderCommandMessage(IProviderCommand command, string callbackUrl) { Command = command ?? throw new ArgumentNullException(nameof(command)); CallbackUrl = callbackUrl ?? throw new ArgumentNullException(nameof(callbackUrl)); }
public string GetCallbackUrl(IProviderCommand providerCommand) => BaseUrl.AppendPathSegment($"callback/{providerCommand.CommandId}");
public ProviderCommandMessage(IProviderCommand command, string callbackUrl = null) : base(command) { CallbackUrl = callbackUrl; }