Ejemplo n.º 1
0
        public static S3PlayerApiClient GetPlayerApiClient(IHttpClientFactory httpClientFactory, string apiUrl, TokenResponse tokenResponse)
        {
            var client    = ApiClientsExtensions.GetHttpClient(httpClientFactory, apiUrl, tokenResponse);
            var apiClient = new S3PlayerApiClient(client, true);

            apiClient.BaseUri = client.BaseAddress;
            return(apiClient);
        }
Ejemplo n.º 2
0
        private S3PlayerApiClient RefreshClient(S3PlayerApiClient clientObject, TokenResponse tokenResponse, CancellationToken ct)
        {
            // TODO: check for token expiration also
            if (clientObject == null)
            {
                clientObject = PlayerApiExtensions.GetPlayerApiClient(_httpClientFactory, _clientOptions.CurrentValue.urls.playerApi, tokenResponse);
            }

            return(clientObject);
        }
Ejemplo n.º 3
0
        public PlayerService(IHttpContextAccessor httpContextAccessor, ClientOptions clientSettings)
        {
            _userId = httpContextAccessor.HttpContext.User.GetId();

            // Workaround for token bug introduced in .NET Core 2.1.  Remove in 2.2
            string authHeader = httpContextAccessor.HttpContext.Request.Headers["Authorization"];
            string token      = null;

            if (!string.IsNullOrEmpty(authHeader))
            {
                token = authHeader.Replace("bearer ", string.Empty).Replace("Bearer ", string.Empty);
            }

            // Go back to this when bug is fixed in .NET Core 2.2
            //var token = httpContextAccessor.HttpContext.GetTokenAsync("access_token").Result;

            _s3PlayerApiClient = new S3PlayerApiClient(new Uri(clientSettings.urls.playerApi), new TokenCredentials(token, "Bearer"));
        }
Ejemplo n.º 4
0
        public static void AddS3PlayerApiClient(this IServiceCollection services)
        {
            services.AddScoped <IS3PlayerApiClient, S3PlayerApiClient>(p =>
            {
                var httpContextAccessor = p.GetRequiredService <IHttpContextAccessor>();
                var httpClientFactory   = p.GetRequiredService <IHttpClientFactory>();
                var clientOptions       = p.GetRequiredService <ClientOptions>();

                var playerUri = new Uri(clientOptions.urls.playerApi);

                string authHeader = httpContextAccessor.HttpContext.Request.Headers["Authorization"];

                var httpClient         = httpClientFactory.CreateClient();
                httpClient.BaseAddress = playerUri;
                httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

                var apiClient     = new S3PlayerApiClient(httpClient, true);
                apiClient.BaseUri = playerUri;

                return(apiClient);
            });
        }
Ejemplo n.º 5
0
        public static async Task <Guid?> CreatePlayerExerciseAsync(S3PlayerApiClient playerApiClient, ImplementationEntity implementationEntity, Guid parentExerciseId, CancellationToken ct)
        {
            try
            {
                var exercise = (await playerApiClient.CloneExerciseAsync(parentExerciseId, ct)) as S3.Player.Api.Models.Exercise;
                exercise.Name = $"{exercise.Name.Replace("Clone of ", "")} - {implementationEntity.Username}";
                await playerApiClient.UpdateExerciseAsync((Guid)exercise.Id, exercise, ct);

                // add user to first non-admin team
                var roles = await playerApiClient.GetRolesAsync(ct) as IEnumerable <Role>;

                var teams = (await playerApiClient.GetExerciseTeamsAsync((Guid)exercise.Id, ct)) as IEnumerable <Team>;
                foreach (var team in teams)
                {
                    if (team.Permissions.Where(p => p.Key == "ExerciseAdmin").Any())
                    {
                        continue;
                    }

                    if (team.RoleId.HasValue)
                    {
                        var role = roles.Where(r => r.Id == team.RoleId).FirstOrDefault();

                        if (role != null && role.Permissions.Where(p => p.Key == "ExerciseAdmin").Any())
                        {
                            continue;
                        }
                    }

                    await playerApiClient.AddUserToTeamAsync(team.Id.Value, implementationEntity.UserId, ct);
                }
                return(exercise.Id);
            }
            catch (Exception ex)
            {
                return(null);
            }
        }
Ejemplo n.º 6
0
        private async void ProcessTheImplementation(Object implementationEntityAsObject)
        {
            var ct = new CancellationToken();
            var implementationEntity = implementationEntityAsObject == null ? (ImplementationEntity)null : (ImplementationEntity)implementationEntityAsObject;

            _logger.LogInformation($"Processing Implementation {implementationEntity.Id} for status '{implementationEntity.Status}'.");

            try
            {
                using (var scope = _scopeFactory.CreateScope())
                {
                    using (var alloyContext = scope.ServiceProvider.GetRequiredService <AlloyContext>())
                    {
                        var retryCount         = 0;
                        var resourceCount      = int.MaxValue;
                        var resourceRetryCount = 0;
                        // get the alloy context entities required
                        implementationEntity = alloyContext.Implementations.First(x => x.Id == implementationEntity.Id);
                        var definitionEntity = alloyContext.Definitions.First(x => x.Id == implementationEntity.DefinitionId);
                        // get the auth token
                        var tokenResponse = await ApiClientsExtensions.GetToken(scope);

                        CasterApiClient      casterApiClient      = null;
                        S3PlayerApiClient    playerApiClient      = null;
                        SteamfitterApiClient steamfitterApiClient = null;
                        // LOOP until this thread's process is complete
                        while (implementationEntity.Status == ImplementationStatus.Creating ||
                               implementationEntity.Status == ImplementationStatus.Planning ||
                               implementationEntity.Status == ImplementationStatus.Applying ||
                               implementationEntity.Status == ImplementationStatus.Ending)
                        {
                            // the updateTheEntity flag is used to indicate if the implementation entity state should be updated at the end of this loop
                            var updateTheEntity = false;
                            // each time through the loop, one state (case) is handled based on Status and InternalStatus.  This allows for retries of a failed state.
                            switch (implementationEntity.Status)
                            {
                            // the "Creating" status means we are creating the initial player exercise, steamfitter session and caster workspace
                            case ImplementationStatus.Creating:
                            {
                                switch (implementationEntity.InternalStatus)
                                {
                                case InternalImplementationStatus.LaunchQueued:
                                case InternalImplementationStatus.CreatingExercise:
                                {
                                    if (definitionEntity.ExerciseId == null)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.CreatingSession;
                                        updateTheEntity = true;
                                    }
                                    else
                                    {
                                        try
                                        {
                                            playerApiClient = RefreshClient(playerApiClient, tokenResponse, ct);
                                            var exerciseId = await PlayerApiExtensions.CreatePlayerExerciseAsync(playerApiClient, implementationEntity, (Guid)definitionEntity.ExerciseId, ct);

                                            if (exerciseId != null)
                                            {
                                                implementationEntity.ExerciseId     = exerciseId;
                                                implementationEntity.InternalStatus = InternalImplementationStatus.CreatingSession;
                                                updateTheEntity = true;
                                            }
                                            else
                                            {
                                                retryCount++;
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            _logger.LogError($"Error creating the player exercise for Implementation {implementationEntity.Id}.", ex);
                                            retryCount++;
                                        }
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.CreatingSession:
                                {
                                    if (definitionEntity.ScenarioId == null)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.CreatingWorkspace;
                                        updateTheEntity = true;
                                    }
                                    else
                                    {
                                        steamfitterApiClient = RefreshClient(steamfitterApiClient, tokenResponse, ct);
                                        var session = await SteamfitterApiExtensions.CreateSteamfitterSessionAsync(steamfitterApiClient, implementationEntity, (Guid)definitionEntity.ScenarioId, ct);

                                        if (session != null)
                                        {
                                            implementationEntity.SessionId      = session.Id;
                                            implementationEntity.InternalStatus = InternalImplementationStatus.CreatingWorkspace;
                                            updateTheEntity = true;
                                        }
                                        else
                                        {
                                            retryCount++;
                                        }
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.CreatingWorkspace:
                                {
                                    if (definitionEntity.DirectoryId == null)
                                    {
                                        // There is no Caster directory, so start the session
                                        var launchDate = DateTime.UtcNow;
                                        implementationEntity.Name           = definitionEntity.Name;
                                        implementationEntity.Description    = definitionEntity.Description;
                                        implementationEntity.LaunchDate     = launchDate;
                                        implementationEntity.ExpirationDate = launchDate.AddHours(definitionEntity.DurationHours);
                                        implementationEntity.Status         = ImplementationStatus.Applying;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.StartingSession;
                                        updateTheEntity = true;
                                    }
                                    else
                                    {
                                        var varsFileContent = "";
                                        if (implementationEntity.ExerciseId != null)
                                        {
                                            playerApiClient = RefreshClient(playerApiClient, tokenResponse, ct);
                                            varsFileContent = await CasterApiExtensions.GetCasterVarsFileContentAsync(implementationEntity, playerApiClient, ct);
                                        }
                                        casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                        var workspaceId = await CasterApiExtensions.CreateCasterWorkspaceAsync(casterApiClient, implementationEntity, (Guid)definitionEntity.DirectoryId, varsFileContent, ct);

                                        if (workspaceId != null)
                                        {
                                            implementationEntity.WorkspaceId    = workspaceId;
                                            implementationEntity.InternalStatus = InternalImplementationStatus.PlanningLaunch;
                                            implementationEntity.Status         = ImplementationStatus.Planning;
                                            updateTheEntity = true;
                                        }
                                        else
                                        {
                                            retryCount++;
                                        }
                                    }
                                    break;
                                }

                                default:
                                {
                                    _logger.LogError($"Invalid status for Implementation {implementationEntity.Id}: {implementationEntity.Status} - {implementationEntity.InternalStatus}");
                                    implementationEntity.Status = ImplementationStatus.Failed;
                                    updateTheEntity             = true;
                                    break;
                                }
                                }
                                break;
                            }

                            // the "Planning" state means that caster is planning a run
                            case ImplementationStatus.Planning:
                            {
                                switch (implementationEntity.InternalStatus)
                                {
                                case InternalImplementationStatus.PlanningLaunch:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    var runId = await CasterApiExtensions.CreateRunAsync(implementationEntity, casterApiClient, false, ct);

                                    if (runId != null)
                                    {
                                        implementationEntity.RunId          = runId;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.PlannedLaunch;
                                        updateTheEntity = true;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.PlannedLaunch:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.WaitForRunToBePlannedAsync(implementationEntity, casterApiClient, _clientOptions.CurrentValue.CasterCheckIntervalSeconds, _clientOptions.CurrentValue.CasterPlanningMaxWaitMinutes, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.ApplyingLaunch;
                                        implementationEntity.Status         = ImplementationStatus.Applying;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                default:
                                {
                                    _logger.LogError($"Invalid status for Implementation {implementationEntity.Id}: {implementationEntity.Status} - {implementationEntity.InternalStatus}");
                                    implementationEntity.Status = ImplementationStatus.Failed;
                                    updateTheEntity             = true;
                                    break;
                                }
                                }
                                break;
                            }

                            // the "Applying" state means caster is applying a run (deploying VM's, etc.)
                            case ImplementationStatus.Applying:
                            {
                                switch (implementationEntity.InternalStatus)
                                {
                                case InternalImplementationStatus.ApplyingLaunch:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.ApplyRunAsync(implementationEntity, casterApiClient, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.AppliedLaunch;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.AppliedLaunch:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.WaitForRunToBeAppliedAsync(implementationEntity, casterApiClient, _clientOptions.CurrentValue.CasterCheckIntervalSeconds, _clientOptions.CurrentValue.CasterDeployMaxWaitMinutes, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.StartingSession;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.StartingSession:
                                {
                                    // start the steamfitter session, if there is one
                                    if (implementationEntity.SessionId != null)
                                    {
                                        steamfitterApiClient = RefreshClient(steamfitterApiClient, tokenResponse, ct);
                                        updateTheEntity      = await SteamfitterApiExtensions.StartSteamfitterSessionAsync(steamfitterApiClient, (Guid)implementationEntity.SessionId, ct);
                                    }
                                    else
                                    {
                                        updateTheEntity = true;
                                    }
                                    // moving on means that Launch is now complete
                                    if (updateTheEntity)
                                    {
                                        var launchDate = DateTime.UtcNow;
                                        implementationEntity.Name           = definitionEntity.Name;
                                        implementationEntity.Description    = definitionEntity.Description;
                                        implementationEntity.LaunchDate     = launchDate;
                                        implementationEntity.ExpirationDate = launchDate.AddHours(definitionEntity.DurationHours);
                                        implementationEntity.Status         = ImplementationStatus.Active;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.Launched;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                default:
                                {
                                    _logger.LogError($"Invalid status for Implementation {implementationEntity.Id}: {implementationEntity.Status} - {implementationEntity.InternalStatus}");
                                    implementationEntity.Status = ImplementationStatus.Failed;
                                    updateTheEntity             = true;
                                    break;
                                }
                                }
                                break;
                            }

                            // the "Ending" state means all entities are being torn down
                            case ImplementationStatus.Ending:
                            {
                                switch (implementationEntity.InternalStatus)
                                {
                                case InternalImplementationStatus.EndQueued:
                                case InternalImplementationStatus.DeletingExercise:
                                {
                                    if (implementationEntity.ExerciseId != null)
                                    {
                                        playerApiClient = RefreshClient(playerApiClient, tokenResponse, ct);
                                        updateTheEntity = await PlayerApiExtensions.DeletePlayerExerciseAsync(_clientOptions.CurrentValue.urls.playerApi, implementationEntity.ExerciseId, playerApiClient, ct);
                                    }
                                    else
                                    {
                                        updateTheEntity = true;
                                    }
                                    if (updateTheEntity)
                                    {
                                        implementationEntity.ExerciseId     = null;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.DeletingSession;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.DeletingSession:
                                {
                                    if (implementationEntity.SessionId != null)
                                    {
                                        steamfitterApiClient = RefreshClient(steamfitterApiClient, tokenResponse, ct);
                                        updateTheEntity      = await SteamfitterApiExtensions.EndSteamfitterSessionAsync(_clientOptions.CurrentValue.urls.steamfitterApi, implementationEntity.SessionId, steamfitterApiClient, ct);
                                    }
                                    else
                                    {
                                        updateTheEntity = true;
                                    }
                                    if (updateTheEntity)
                                    {
                                        implementationEntity.SessionId      = null;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.PlanningDestroy;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.PlanningDestroy:
                                {
                                    if (implementationEntity.WorkspaceId != null)
                                    {
                                        casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                        var runId = await CasterApiExtensions.CreateRunAsync(implementationEntity, casterApiClient, true, ct);

                                        if (runId != null)
                                        {
                                            implementationEntity.RunId          = runId;
                                            implementationEntity.InternalStatus = InternalImplementationStatus.PlannedDestroy;
                                            updateTheEntity = true;
                                        }
                                        else
                                        {
                                            retryCount++;
                                        }
                                    }
                                    else
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.Ended;
                                        implementationEntity.Status         = ImplementationStatus.Ended;
                                        updateTheEntity = true;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.PlannedDestroy:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.WaitForRunToBePlannedAsync(implementationEntity, casterApiClient, _clientOptions.CurrentValue.CasterCheckIntervalSeconds, _clientOptions.CurrentValue.CasterPlanningMaxWaitMinutes, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.ApplyingDestroy;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.ApplyingDestroy:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.ApplyRunAsync(implementationEntity, casterApiClient, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.InternalStatus = InternalImplementationStatus.AppliedDestroy;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.AppliedDestroy:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    await CasterApiExtensions.WaitForRunToBeAppliedAsync(implementationEntity, casterApiClient, _clientOptions.CurrentValue.CasterCheckIntervalSeconds, _clientOptions.CurrentValue.CasterDestroyMaxWaitMinutes, ct);

                                    // all conditions in this case require an implementation entity update
                                    updateTheEntity = true;
                                    // make sure that the run successfully deleted the resources
                                    var count = (await casterApiClient.GetResourcesByWorkspaceAsync((Guid)implementationEntity.WorkspaceId, ct)).Count();
                                    implementationEntity.RunId = null;
                                    if (count == 0)
                                    {
                                        // resources deleted, so continue to delete the workspace
                                        implementationEntity.InternalStatus = InternalImplementationStatus.DeletingWorkspace;
                                    }
                                    else
                                    {
                                        if (count < resourceCount)
                                        {
                                            // still some resources, but making progress, try the whole process again
                                            implementationEntity.InternalStatus = InternalImplementationStatus.PlanningDestroy;
                                            resourceRetryCount = 0;
                                        }
                                        else
                                        {
                                            // still some resources and not making progress. Check max retries.
                                            if (resourceRetryCount < _clientOptions.CurrentValue.ApiClientFailureMaxRetries)
                                            {
                                                // try the whole process again after a wait
                                                implementationEntity.InternalStatus = InternalImplementationStatus.PlanningDestroy;
                                                resourceRetryCount++;
                                                Thread.Sleep(TimeSpan.FromMinutes(_clientOptions.CurrentValue.CasterDestroyRetryDelayMinutes));
                                            }
                                            else
                                            {
                                                // the caster workspace resources could not be destroyed
                                                implementationEntity.InternalStatus = InternalImplementationStatus.FailedDestroy;
                                                implementationEntity.Status         = ImplementationStatus.Failed;
                                            }
                                        }
                                    }
                                    break;
                                }

                                case InternalImplementationStatus.DeletingWorkspace:
                                {
                                    casterApiClient = RefreshClient(casterApiClient, tokenResponse, ct);
                                    updateTheEntity = await CasterApiExtensions.DeleteCasterWorkspaceAsync(implementationEntity, casterApiClient, tokenResponse, ct);

                                    if (updateTheEntity)
                                    {
                                        implementationEntity.WorkspaceId    = null;
                                        implementationEntity.Status         = ImplementationStatus.Ended;
                                        implementationEntity.InternalStatus = InternalImplementationStatus.Ended;
                                    }
                                    else
                                    {
                                        retryCount++;
                                    }
                                    break;
                                }

                                default:
                                {
                                    _logger.LogError($"Invalid status for Implementation {implementationEntity.Id}: {implementationEntity.Status} - {implementationEntity.InternalStatus}");
                                    implementationEntity.Status = ImplementationStatus.Failed;
                                    updateTheEntity             = true;
                                    break;
                                }
                                }
                                break;
                            }
                            }
                            // check for exceeding the max number of retries
                            if (!updateTheEntity)
                            {
                                if (retryCount >= _clientOptions.CurrentValue.ApiClientFailureMaxRetries && _clientOptions.CurrentValue.ApiClientFailureMaxRetries > 0)
                                {
                                    _logger.LogError($"Retry count exceeded for Implementation {implementationEntity.Id}, with status of {implementationEntity.Status} - {implementationEntity.InternalStatus}");
                                    implementationEntity.Status = ImplementationStatus.Failed;
                                    updateTheEntity             = true;
                                }
                                else
                                {
                                    Thread.Sleep(TimeSpan.FromSeconds(_clientOptions.CurrentValue.ApiClientRetryIntervalSeconds));
                                }
                            }
                            // update the entity in the context, if we are moving on
                            if (updateTheEntity)
                            {
                                retryCount = 0;
                                implementationEntity.StatusDate = DateTime.UtcNow;
                                await alloyContext.SaveChangesAsync(ct);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error processing implementation {implementationEntity.Id}", ex);
            }
        }
Ejemplo n.º 7
0
        public static async Task <bool> DeletePlayerExerciseAsync(string playerApiUrl, Guid?exerciseId, S3PlayerApiClient playerApiClient, CancellationToken ct)
        {
            // no exercise to delete
            if (exerciseId == null)
            {
                return(true);
            }
            // try to delete the exercise
            try
            {
                await playerApiClient.DeleteExerciseAsync((Guid)exerciseId, ct);

                return(true);
            }
            catch (Exception ex)
            {
                return(false);
            }
        }
Ejemplo n.º 8
0
        public static async Task <string> GetCasterVarsFileContentAsync(ImplementationEntity implementationEntity, S3PlayerApiClient playerApiClient, CancellationToken ct)
        {
            try
            {
                var varsFileContent = "";
                var exercise        = (await playerApiClient.GetExerciseAsync((Guid)implementationEntity.ExerciseId, ct)) as S3.Player.Api.Models.Exercise;
                varsFileContent = $"exercise_id = \"{exercise.Id}\"\r\nuser_id = \"{implementationEntity.UserId}\"\r\nusername = \"{implementationEntity.Username}\"\r\n";
                var teams = (await playerApiClient.GetExerciseTeamsAsync((Guid)exercise.Id, ct)) as IEnumerable <Team>;

                foreach (var team in teams)
                {
                    var cleanTeamName = Regex.Replace(team.Name.ToLower().Replace(" ", "_"), "[@&'(\\s)<>#]", "", RegexOptions.None);
                    varsFileContent += $"{cleanTeamName} = \"{team.Id}\"\r\n";
                }

                return(varsFileContent);
            }
            catch (Exception ex)
            {
                return("");
            }
        }
Ejemplo n.º 9
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            var provider = Configuration["Database:Provider"];

            switch (provider)
            {
            case "InMemory":
                services.AddDbContextPool <Context>(opt => opt.UseInMemoryDatabase("vm"));
                break;

            case "Sqlite":
            case "SqlServer":
            case "PostgreSQL":
                services.AddDbProvider(Configuration);
                services.AddDbContextPool <Context>(builder => builder.UseConfiguredDatabase(Configuration));
                break;
            }

            services.AddOptions()
            .Configure <DatabaseOptions>(Configuration.GetSection("Database"))
            .AddScoped(config => config.GetService <IOptionsMonitor <DatabaseOptions> >().CurrentValue);

            IConfiguration   isoConfig  = Configuration.GetSection("IsoUpload");
            IsoUploadOptions isoOptions = new IsoUploadOptions();

            isoConfig.Bind(isoOptions);

            services.AddOptions()
            .Configure <IsoUploadOptions>(isoConfig)
            .AddScoped(config => config.GetService <IOptionsMonitor <IsoUploadOptions> >().CurrentValue);

            services
            .Configure <ClientOptions>(Configuration.GetSection("ClientSettings"))
            .AddScoped(config => config.GetService <IOptionsMonitor <ClientOptions> >().CurrentValue);

            services.AddCors(options => options.UseConfiguredCors(Configuration.GetSection("CorsPolicy")));
            services.AddMvc(options =>
            {
                options.Filters.Add(typeof(ValidateModelStateFilter));
                options.Filters.Add(typeof(JsonExceptionFilter));

                // Require all scopes in authOptions
                var policyBuilder = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
                Array.ForEach(_authOptions.AuthorizationScope.Split(' '), x => policyBuilder.RequireScope(x));

                var policy = policyBuilder.Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            })
            .AddJsonOptions(options =>
            {
                options.SerializerSettings.Converters.Add(new StringEnumConverter());
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            // allow upload of large files
            services.Configure <FormOptions>(x => {
                x.ValueLengthLimit         = int.MaxValue;
                x.MultipartBodyLengthLimit = isoOptions.MaxFileSize;
            });

            services.AddSwagger(_authOptions);

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority                 = _authOptions.Authority;
                options.RequireHttpsMetadata      = _authOptions.RequireHttpsMetadata;
                options.SaveToken                 = true;
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidAudiences = _authOptions.AuthorizationScope.Split(' ')
                };
            });

            services.AddRouting(options =>
            {
                options.LowercaseUrls = true;
            });

            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped <IPrincipal>(p => p.GetService <IHttpContextAccessor>().HttpContext.User);

            services.AddScoped <IVmService, VmService>();
            services.AddScoped <IPlayerService, PlayerService>();

            services.AddAutoMapper();

            services.AddHttpClient();

            services.AddScoped <IS3PlayerApiClient, S3PlayerApiClient>(p =>
            {
                var httpContextAccessor = p.GetRequiredService <IHttpContextAccessor>();
                var httpClientFactory   = p.GetRequiredService <IHttpClientFactory>();
                var clientOptions       = p.GetRequiredService <ClientOptions>();

                var playerUri = new Uri(clientOptions.urls.playerApi);

                string authHeader = httpContextAccessor.HttpContext.Request.Headers["Authorization"];

                var httpClient         = httpClientFactory.CreateClient();
                httpClient.BaseAddress = playerUri;
                httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

                var s3PlayerApiClient     = new S3PlayerApiClient(httpClient, true);
                s3PlayerApiClient.BaseUri = playerUri;

                return(s3PlayerApiClient);
            });
        }