Exemplo n.º 1
0
        public void Validate_Error()
        {
            var command = new OrchestratorProjectCreateCommand(new Uri("http://localhost/"), null, new ProjectDocument());

            var result = command.Validate();

            Assert.False(result.IsValid);
        }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        public async Task ValidateAsync_Error()
        {
            var command = new OrchestratorProjectCreateCommand(null, new Project());

            var result = await command.ValidateAsync().ConfigureAwait(false);

            Assert.False(result.IsValid);
        }
Exemplo n.º 4
0
        public void Validate_Success()
        {
            var command = new OrchestratorProjectCreateCommand(new Uri("http://localhost/"), new UserDocument(), new ProjectDocument());

            var result = command.Validate();

            Assert.True(result.IsValid);
        }
Exemplo n.º 5
0
        public void Validate_Error()
        {
            var command = new OrchestratorProjectCreateCommand(null, new Project());

            var result = command.Validate();

            Assert.False(result.IsValid);
        }
Exemplo n.º 6
0
        public async Task ValidateAsync_Success()
        {
            var command = new OrchestratorProjectCreateCommand(new User(), new Project());

            var result = await command.ValidateAsync().ConfigureAwait(false);

            Assert.True(result.IsValid);
        }
Exemplo n.º 7
0
        public void Validate_Success()
        {
            var command = new OrchestratorProjectCreateCommand(new User(), new Project());

            var result = command.Validate();

            Assert.True(result.IsValid);
        }
Exemplo n.º 8
0
        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));
        }
Exemplo n.º 9
0
        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);
        }
Exemplo n.º 10
0
        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);
        }
Exemplo n.º 11
0
        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;
        }
Exemplo n.º 12
0
        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);
        }
Exemplo n.º 13
0
        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);
        }
Exemplo n.º 14
0
        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.");
        }