public void Validate_Error() { var command = new OrchestratorProjectCreateCommand(new Uri("http://localhost/"), null, new ProjectDocument()); var result = command.Validate(); Assert.False(result.IsValid); }
public async Task ValidateAsync_Error() { var command = new OrchestratorProjectCreateCommand(new Uri("http://localhost/"), null, new ProjectDocument()); var result = await command.ValidateAsync().ConfigureAwait(false); Assert.False(result.IsValid); }
public async Task ValidateAsync_Error() { var command = new OrchestratorProjectCreateCommand(null, new Project()); var result = await command.ValidateAsync().ConfigureAwait(false); Assert.False(result.IsValid); }
public void Validate_Success() { var command = new OrchestratorProjectCreateCommand(new Uri("http://localhost/"), new UserDocument(), new ProjectDocument()); var result = command.Validate(); Assert.True(result.IsValid); }
public void Validate_Error() { var command = new OrchestratorProjectCreateCommand(null, new Project()); var result = command.Validate(); Assert.False(result.IsValid); }
public async Task ValidateAsync_Success() { var command = new OrchestratorProjectCreateCommand(new User(), new Project()); var result = await command.ValidateAsync().ConfigureAwait(false); Assert.True(result.IsValid); }
public void Validate_Success() { var command = new OrchestratorProjectCreateCommand(new User(), new Project()); var result = command.Validate(); Assert.True(result.IsValid); }
public async Task <IActionResult> Post([FromBody] ProjectDefinition projectDefinition) { if (projectDefinition is null) { throw new ArgumentNullException(nameof(projectDefinition)); } var validation = new ProjectDefinitionValidator().Validate(projectDefinition); if (!validation.IsValid) { return(ErrorResult .BadRequest(validation) .ToActionResult()); } var nameExists = await ProjectRepository .NameExistsAsync(projectDefinition.Name) .ConfigureAwait(false); if (nameExists) { return(ErrorResult .Conflict($"A Project with name '{projectDefinition.Name}' already exists. Project names must be unique. Please try your request again with a unique name.") .ToActionResult()); } var projectId = Guid.NewGuid().ToString(); var users = await ResolveUsersAsync(projectDefinition, projectId) .ConfigureAwait(false); var project = new ProjectDocument { Id = projectId, Users = users, Name = projectDefinition.Name, Tags = projectDefinition.Tags, Properties = projectDefinition.Properties }; if (!string.IsNullOrEmpty(projectDefinition.ProjectType)) { project.Type = await projectTypeRepository .GetAsync(projectDefinition.ProjectType) .ConfigureAwait(false); if (project.Type is null) { return(ErrorResult .BadRequest(new ValidationError { Field = "projectType", Message = $"A Project Type with the ID '{projectDefinition.ProjectType}' could not be found in this TeamCloud Instance. Please try your request again with a valid Project Type ID for 'projectType'." }) .ToActionResult()); } } else { project.Type = await projectTypeRepository .GetDefaultAsync() .ConfigureAwait(false); if (project.Type is null) { return(ErrorResult .BadRequest(new ValidationError { Field = "projectType", Message = $"No value was provided for 'projectType' and there is no a default Project Type set for this TeamCloud Instance. Please try your request again with a valid Project Type ID for 'projectType'." }) .ToActionResult()); } } var currentUser = users.FirstOrDefault(u => u.Id == UserService.CurrentUserId); var command = new OrchestratorProjectCreateCommand(currentUser, project); return(await Orchestrator .InvokeAndReturnActionResultAsync <ProjectDocument, Project>(command, Request) .ConfigureAwait(false)); }
private static async Task <OrchestratorProjectCreateCommandResult> ProvisionAsync(IDurableOrchestrationContext functionContext, OrchestratorProjectCreateCommand command, ILogger log) { var teamCloud = await functionContext .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 functionContext .ListProvidersAsync(project.Type.Providers.Select(p => p.Id).ToList()) .ConfigureAwait(true); var providerUserTasks = providers .Where(p => p.PrincipalId.HasValue) .Select(p => functionContext.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 functionContext.LockContainerDocumentAsync(project).ConfigureAwait(true)) { functionContext.SetCustomStatus($"Creating project", log); project = commandResult.Result = await functionContext .CreateProjectAsync(project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Adding users", log); project.Users = await Task .WhenAll(projectUsers.Select(user => functionContext.SetUserProjectMembershipAsync(user, project.Id, allowUnsafe: true))) .ConfigureAwait(true); } functionContext.SetCustomStatus($"Allocating subscription", log); var subscriptionId = await functionContext .CallActivityWithRetryAsync <Guid>(nameof(ProjectSubscriptionSelectActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Initializing subscription", log); await functionContext .InitializeSubscriptionAsync(subscriptionId, waitFor : false) .ConfigureAwait(true); functionContext.SetCustomStatus($"Provisioning resources", log); var deploymentOutput = await functionContext .CallDeploymentAsync(nameof(ProjectResourcesCreateActivity), (project, subscriptionId)) .ConfigureAwait(true); using (await functionContext.LockContainerDocumentAsync(project).ConfigureAwait(true)) { functionContext.SetCustomStatus($"Provisioning identity", log); project.Identity = await functionContext .CallActivityWithRetryAsync <ProjectIdentity>(nameof(ProjectIdentityCreateActivity), 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)) }; project = commandResult.Result = await functionContext .SetProjectAsync(project) .ConfigureAwait(true); } functionContext.SetCustomStatus($"Tagging resources", log); await functionContext .CallActivityWithRetryAsync(nameof(ProjectResourcesTagActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Registering required resource providers", log); await functionContext .RegisterResourceProvidersAsync(project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Sending provider commands", log); var providerCommand = new ProviderProjectCreateCommand ( command.User.PopulateExternalModel(), project.PopulateExternalModel(), command.CommandId ); var providerResults = await functionContext .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); }
private static async Task RollbackAsync(IDurableOrchestrationContext functionContext, OrchestratorProjectCreateCommand command, ILogger log) { functionContext.SetCustomStatus($"Refreshing project", log); var project = (await functionContext .GetProjectAsync(command.ProjectId, allowUnsafe: true) .ConfigureAwait(true)) ?? command.Payload; functionContext.SetCustomStatus($"Rolling back project", log); var systemUser = await functionContext .CallActivityWithRetryAsync <UserDocument>(nameof(TeamCloudSystemUserActivity), null) .ConfigureAwait(true); var deleteCommand = new OrchestratorProjectDeleteCommand(systemUser, project); await functionContext .CallSubOrchestratorWithRetryAsync(nameof(OrchestratorProjectDeleteCommandOrchestration), deleteCommand) .ConfigureAwait(true); }
private static async Task ProvisionAsync(IDurableOrchestrationContext functionContext, OrchestratorProjectCreateCommand command, OrchestratorProjectCreateCommandResult commandResult, ILogger log) { var teamCloud = await functionContext .GetTeamCloudAsync() .ConfigureAwait(true); var project = command.Payload; // initialize the new project with some data // from the teamcloud instance: // - TeamCloudId = ensure that the new project belongs to a team cloud instance // - Tags = ensure that every project starts with a set of tags defined by the team cloud instance // CAUTION: there is no need to populate any other data (e.g. properties) on the new project instance project.TeamCloudId = teamCloud.Id; project.Tags = teamCloud.Tags.Override(project.Tags); functionContext.SetCustomStatus($"Project {project.Id} - Creating ...", log); project = await functionContext .CallActivityWithRetryAsync <Project>(nameof(ProjectCreateActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Project {project.Id} - Allocating subscription ...", log); var subscriptionId = await functionContext .CallActivityWithRetryAsync <Guid>(nameof(AzureSubscriptionPoolSelectActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Project {project.Id} - Provisioning resource group ...", log); project.ResourceGroup = await functionContext .CallActivityWithRetryAsync <AzureResourceGroup>(nameof(AzureResourceGroupCreateActivity), (project, subscriptionId)) .ConfigureAwait(true); functionContext.SetCustomStatus($"Project {project.Id} - Updating resource group tags ...", log); await functionContext .CallActivityWithRetryAsync(nameof(AzureResourceGroupTagActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Project {project.Id} - Updating ...", log); project = await functionContext .CallActivityWithRetryAsync <Project>(nameof(ProjectSetActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Project {project.Id} - Sending provider command ...", log); var providerResults = await functionContext .SendCommandAsync <ProviderProjectCreateCommand, ProviderProjectCreateCommandResult>(new ProviderProjectCreateCommand(command.User, project, command.CommandId)) .ConfigureAwait(true); commandResult.Result = project; }
private static async Task RollbackAsync(IDurableOrchestrationContext functionContext, OrchestratorProjectCreateCommand command) { var systemUser = await functionContext .CallActivityWithRetryAsync <User>(nameof(TeamCloudUserActivity), null) .ConfigureAwait(true); var deleteCommand = new OrchestratorProjectDeleteCommand(systemUser, command.Payload); var deleteCommandMessage = new OrchestratorCommandMessage(deleteCommand); functionContext .StartNewOrchestration(nameof(OrchestratorProjectDeleteCommandOrchestration), deleteCommandMessage); }
private static async Task <OrchestratorProjectCreateCommandResult> ProvisionAsync(IDurableOrchestrationContext functionContext, OrchestratorProjectCreateCommand command, ILogger log) { var teamCloud = await functionContext .GetTeamCloudAsync() .ConfigureAwait(true); var commandResult = command.CreateResult(); var project = commandResult.Result = command.Payload; // initialize the new project with some data // from the teamcloud instance: // - TeamCloudId = ensure that the new project belongs to a team cloud instance // - Tags = ensure that every project starts with a set of tags defined by the team cloud instance // CAUTION: there is no need to populate any other data (e.g. properties) on the new project instance project.TeamCloudId = teamCloud.Id; project.Tags = teamCloud.Tags.Override(project.Tags); functionContext.SetCustomStatus($"Creating project", log); project = commandResult.Result = await functionContext .CallActivityWithRetryAsync <Project>(nameof(ProjectCreateActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Allocating subscription", log); var subscriptionId = await functionContext .CallActivityWithRetryAsync <Guid>(nameof(ProjectSubscriptionSelectActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Initializing subscription", log); await functionContext .InitializeSubscriptionAsync(subscriptionId) .ConfigureAwait(true); functionContext.SetCustomStatus($"Provisioning resources", log); var deploymentOutput = await functionContext .CallDeploymentAsync(nameof(ProjectResourcesCreateActivity), (project, subscriptionId)) .ConfigureAwait(true); using (await functionContext.LockContainerDocumentAsync(project).ConfigureAwait(true)) { functionContext.SetCustomStatus($"Updating project", log); project.ResourceGroup = new AzureResourceGroup() { SubscriptionId = subscriptionId, Region = project.Type.Region, ResourceGroupId = (string)deploymentOutput.GetValueOrDefault("resourceGroupId", default(string)), ResourceGroupName = (string)deploymentOutput.GetValueOrDefault("resourceGroupName", default(string)) }; project.KeyVault = new AzureKeyVault() { VaultId = (string)deploymentOutput.GetValueOrDefault("vaultId", default(string)), VaultName = (string)deploymentOutput.GetValueOrDefault("vaultName", default(string)), VaultUrl = (string)deploymentOutput.GetValueOrDefault("vaultUrl", default(string)) }; project = commandResult.Result = await functionContext .SetProjectAsync(project) .ConfigureAwait(true); } functionContext.SetCustomStatus($"Tagging resources", log); await functionContext .CallActivityWithRetryAsync(nameof(ProjectResourcesTagActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Creating project identity", log); await functionContext .CallActivityWithRetryAsync(nameof(ProjectIdentityCreateActivity), project) .ConfigureAwait(true); functionContext.SetCustomStatus($"Sending provider commands", log); var providerResults = await functionContext .SendCommandAsync <ProviderProjectCreateCommand, ProviderProjectCreateCommandResult>(new ProviderProjectCreateCommand(command.User, project, command.CommandId), failFast : true) .ConfigureAwait(true); var providerException = providerResults.Values? .SelectMany(result => result.Errors ?? new List <CommandError>()) .ToException(); if (providerException != null) { throw providerException; } return(commandResult); }
public async Task <IActionResult> Post([FromBody] ProjectDefinition projectDefinition) { if (projectDefinition is null) { throw new ArgumentNullException(nameof(projectDefinition)); } var validation = new ProjectDefinitionValidator().Validate(projectDefinition); if (!validation.IsValid) { return(ErrorResult .BadRequest(validation) .ActionResult()); } var users = await ResolveUsersAsync(projectDefinition) .ConfigureAwait(false); var nameExists = await projectsRepository .NameExistsAsync(projectDefinition.Name) .ConfigureAwait(false); if (nameExists) { return(ErrorResult .Conflict($"A Project with name '{projectDefinition.Name}' already exists. Project names must be unique. Please try your request again with a unique name.") .ActionResult()); } var project = new Project { Id = Guid.NewGuid(), Users = users, Name = projectDefinition.Name, Tags = projectDefinition.Tags }; if (!string.IsNullOrEmpty(projectDefinition.ProjectType)) { project.Type = await projectTypesRepository .GetAsync(projectDefinition.ProjectType) .ConfigureAwait(false); if (project.Type is null) { return(ErrorResult .BadRequest(new ValidationError { Field = "projectType", Message = $"A Project Type with the ID '{projectDefinition.ProjectType}' could not be found in this TeamCloud Instance. Please try your request again with a valid Project Type ID for 'projectType'." }) .ActionResult()); } } else { project.Type = await projectTypesRepository .GetDefaultAsync() .ConfigureAwait(false); if (project.Type is null) { return(ErrorResult .BadRequest(new ValidationError { Field = "projectType", Message = $"No value was provided for 'projectType' and there is no a default Project Type set for this TeamCloud Instance. Please try your request again with a valid Project Type ID for 'projectType'." }) .ActionResult()); } } var command = new OrchestratorProjectCreateCommand(CurrentUser, project); var commandResult = await orchestrator .InvokeAsync(command) .ConfigureAwait(false); if (commandResult.Links.TryGetValue("status", out var statusUrl)) { return(StatusResult .Accepted(commandResult.CommandId.ToString(), statusUrl, commandResult.RuntimeStatus.ToString(), commandResult.CustomStatus) .ActionResult()); } throw new Exception("This shouldn't happen, but we need to decide to do when it does."); }