diff --git a/README.md b/README.md index 56327d5..4383266 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ GitLabKit Runner Admin can help improve experience of managing runners. ### How? #### Main page Put your group ID in text box and hit enter to go to group page. Simple enough. + ![main-page](/doc/images/main-page.png) #### Group page @@ -43,6 +44,7 @@ Displays all runners in the group - Use the toggle in front of runner name to individually enable/disable it - Click on job name on the right to navigate back to job page on GitLab - Click on runner name for runner history page + ![group-page](/doc/images/group-page.png) ##### Filters! @@ -118,8 +120,10 @@ Settings could be passed to Runner Admin's container using these environment var | variable | example | required | description | |---|---|---|---| | `CONNECTIONS__GITLABSERVER` | `https://gitlab.yourcompany.com` | yes | GitLab server URL | -| `CONNECTIONS__REDISSERVER` | `redis-server:16379` | yes | Redis host | | `SECRETS__GITLABTOKEN` | `rG2f93ddaz` | yes | GitLab token. Could be a personal token or group token that has sufficient permission to view CI/CD settings in the group | +| `CONNECTIONS__REDISSERVER` | `redis-server:16379` | no | Redis host. If set, it will be used as cache. | +| `SECRETS__REDISUSER` | `username` | no | Redis ACL user. Leave empty if Redis has no ACL user set. | +| `SECRETS__REDISPASSWORD` | `password` | no | Redis password. Could be used alone if Redis has set `requirepass`. Or could be used together with `SECRETS__REDISUSER` for ACL authentication. | | `LOGTARGETS__SEQ` | `http://seq-server:5341` | no | Runner Admin supports [Seq](https://datalust.co/seq) logger. Use this to set log ingestion URL. | | `APPLICATIONINSIGHTS__CONNECTIONSTRING` | - | no | Runner Admin supports [Application Insights](https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) to see application diagnostics. Use this to set connection string from Azure dashboard. | diff --git a/src/GitLabKit.Runner.Core/Configs/Secrets.cs b/src/GitLabKit.Runner.Core/Configs/Secrets.cs index c2e5067..cb015de 100644 --- a/src/GitLabKit.Runner.Core/Configs/Secrets.cs +++ b/src/GitLabKit.Runner.Core/Configs/Secrets.cs @@ -3,4 +3,6 @@ public class Secrets { public string GitLabToken { get; set; } + public string RedisUser { get; set; } + public string RedisPassword { get; set; } } \ No newline at end of file diff --git a/src/GitLabKit.Runner.Core/Repositories/GitLabRepository.cs b/src/GitLabKit.Runner.Core/Repositories/GitLabRepository.cs index 4f7a975..619c341 100644 --- a/src/GitLabKit.Runner.Core/Repositories/GitLabRepository.cs +++ b/src/GitLabKit.Runner.Core/Repositories/GitLabRepository.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -13,7 +12,6 @@ using Microsoft.Extensions.Options; using Nelibur.ObjectMapper; using Newtonsoft.Json; -using Serilog; namespace GitLabKit.Runner.Core.Repositories; @@ -31,7 +29,7 @@ public interface IGitLabRepository public class GitLabRepository : IGitLabRepository { - private readonly IRedisCache _redis; + private readonly ICache _cache; private readonly IGitLabClient _gitLabClient; private readonly HttpClient _gitLabHttpClient = new(); @@ -39,9 +37,9 @@ public class GitLabRepository : IGitLabRepository private static string GroupInfoCacheKey(int id) => $"group_{id}_info"; private static string RunnerCacheKey(int id) => $"runner_{id}"; - public GitLabRepository(IOptions secretsOptions, IOptions connectionOptions, IRedisCache redis) + public GitLabRepository(IOptions secretsOptions, IOptions connectionOptions, ICache cache) { - _redis = redis; + _cache = cache; _gitLabHttpClient.BaseAddress = new Uri($"{connectionOptions.Value.GitLabServer}/api/v4/"); _gitLabHttpClient.DefaultRequestHeaders.Add("PRIVATE-TOKEN", secretsOptions.Value.GitLabToken); _gitLabClient = new GitLabClient(connectionOptions.Value.GitLabServer, secretsOptions.Value.GitLabToken); @@ -51,7 +49,7 @@ public async Task GetGroup(int groupId) { var dataFunc = () => _gitLabClient.Groups.GetAsync(groupId); - var groupInfo = await _redis.GetCached(GroupInfoCacheKey(groupId), dataFunc, TimeSpan.FromDays(1)); + var groupInfo = await _cache.GetCached(GroupInfoCacheKey(groupId), dataFunc, TimeSpan.FromDays(1)); return TinyMapper.Map(groupInfo); } @@ -62,7 +60,7 @@ public async Task GetGroup(int groupId) $"groups/{groupId}/runners?type=group_type", new Dictionary()); - var runners = await _redis.GetCached(GroupRunnerListCacheKey(groupId), dataFunc, TimeSpan.FromMinutes(1)); + var runners = await _cache.GetCached(GroupRunnerListCacheKey(groupId), dataFunc, TimeSpan.FromMinutes(1)); return runners; } @@ -70,7 +68,7 @@ public async Task GetGroup(int groupId) public async Task GetSingleRunner(int runnerId) { var dataFunc = () => _gitLabClient.Runners.GetAsync(runnerId); - var runner = await _redis.GetCached(RunnerCacheKey(runnerId), dataFunc, TimeSpan.FromMinutes(1)); + var runner = await _cache.GetCached(RunnerCacheKey(runnerId), dataFunc, TimeSpan.FromMinutes(1)); return TinyMapper.Map(runner); } @@ -80,7 +78,7 @@ public async Task> GetRunnerCurrentJobs(int runnerId) $"runners/{runnerId}/jobs", new Dictionary {{"status", "running"}}); - var jobs = await _redis.GetCached($"runner_{runnerId}_currentjob", dataFunc, TimeSpan.FromMinutes(1)); + var jobs = await _cache.GetCached($"runner_{runnerId}_currentjob", dataFunc, TimeSpan.FromMinutes(1)); return jobs; } @@ -91,14 +89,14 @@ public async Task> GetRunnerJobs(int runnerId) $"runners/{runnerId}/jobs", new Dictionary()); - var jobs = await _redis.GetCached($"runner_{runnerId}_alljob", dataFunc, TimeSpan.FromMinutes(1)); + var jobs = await _cache.GetCached($"runner_{runnerId}_alljob", dataFunc, TimeSpan.FromMinutes(1)); return jobs; } public async Task InvalidateRunnerCache(int runnerId) { - await _redis.Delete(RunnerCacheKey(runnerId)); + await _cache.Delete(RunnerCacheKey(runnerId)); } public async Task SetRunnerActiveStatus(int runnerId, bool isActive) diff --git a/src/GitLabKit.Runner.Core/Repositories/ICache.cs b/src/GitLabKit.Runner.Core/Repositories/ICache.cs new file mode 100644 index 0000000..6787fcd --- /dev/null +++ b/src/GitLabKit.Runner.Core/Repositories/ICache.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace GitLabKit.Runner.Core.Repositories; + +public interface ICache +{ + Task GetCached(string key, Func> dataFunc, TimeSpan ttl); + Task Delete(string key); +} \ No newline at end of file diff --git a/src/GitLabKit.Runner.Core/Repositories/NoCache.cs b/src/GitLabKit.Runner.Core/Repositories/NoCache.cs new file mode 100644 index 0000000..71c2581 --- /dev/null +++ b/src/GitLabKit.Runner.Core/Repositories/NoCache.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; + +namespace GitLabKit.Runner.Core.Repositories; + +public class NoCache : ICache +{ + public async Task GetCached(string key, Func> dataFunc, TimeSpan ttl) + { + return await dataFunc.Invoke(); + } + + public Task Delete(string key) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/GitLabKit.Runner.Core/Repositories/RedisCache.cs b/src/GitLabKit.Runner.Core/Repositories/RedisCache.cs index 2fa4f45..bcb724e 100644 --- a/src/GitLabKit.Runner.Core/Repositories/RedisCache.cs +++ b/src/GitLabKit.Runner.Core/Repositories/RedisCache.cs @@ -6,13 +6,7 @@ namespace GitLabKit.Runner.Core.Repositories; -public interface IRedisCache -{ - Task GetCached(string key, Func> dataFunc, TimeSpan ttl); - Task Delete(string key); -} - -public class RedisCache : IRedisCache +public class RedisCache : ICache { private readonly IConnectionMultiplexer _redis; diff --git a/src/GitLabKit.Runner.Web/Program.cs b/src/GitLabKit.Runner.Web/Program.cs index 4753d02..41b61c0 100644 --- a/src/GitLabKit.Runner.Web/Program.cs +++ b/src/GitLabKit.Runner.Web/Program.cs @@ -8,8 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; -using Serilog; -using StackExchange.Redis; +using Serilog; using Swashbuckle.Extensions; var builder = WebApplication.CreateBuilder(args); @@ -45,20 +44,15 @@ var connectionsConfig = configuration.GetSection(nameof(Connections)); builder.Services.Configure(connectionsConfig); builder.Services.Configure(configuration.GetSection(nameof(ApplicationInsights))); -builder.Services.Configure(configuration.GetSection(nameof(Secrets))); + +var secretsConfig = configuration.GetSection(nameof(Secrets)); +builder.Services.Configure(secretsConfig); builder.Services.AddHttpContextAccessor(); builder.Services.AddApplicationInsightsTelemetry().EnrichAppInsightsData(); builder.Services.AddSingleton(); -builder.Services.AddSingleton(ConnectionMultiplexer.Connect(connectionsConfig.GetSection("RedisServer").Value, - options => - { - options.AbortOnConnectFail = false; - options.ConnectTimeout = 1000; - })); - -builder.Services.AddSingleton(); +builder.Services.AddCache(connectionsConfig, secretsConfig); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/GitLabKit.Runner.Web/Startup/CacheConfigurator.cs b/src/GitLabKit.Runner.Web/Startup/CacheConfigurator.cs new file mode 100644 index 0000000..891dea3 --- /dev/null +++ b/src/GitLabKit.Runner.Web/Startup/CacheConfigurator.cs @@ -0,0 +1,37 @@ +using GitLabKit.Runner.Core.Repositories; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; + +namespace GitLabKit.Runner.Web.Startup; + +public static class CacheConfigurator +{ + public static void AddCache(this IServiceCollection service, IConfiguration connectionsConfig, IConfiguration secretsConfig) + { + var redisServerUrl = connectionsConfig.GetValue("RedisServer"); + + if (!string.IsNullOrEmpty(redisServerUrl)) + { + service.AddSingleton( + ConnectionMultiplexer.Connect(redisServerUrl, + options => + { + options.AbortOnConnectFail = false; + options.ConnectTimeout = 1000; + + var user = secretsConfig.GetValue("RedisUser"); + var password = secretsConfig.GetValue("RedisPassword"); + + if (!string.IsNullOrEmpty(user)) options.User = user; + if (!string.IsNullOrEmpty(password)) options.Password = password; + })); + + service.AddSingleton(); + } + else + { + service.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/src/GitLabKit.Runner.Web/appsettings.Development.json b/src/GitLabKit.Runner.Web/appsettings.Development.json index 9ae8e2a..451e72a 100644 --- a/src/GitLabKit.Runner.Web/appsettings.Development.json +++ b/src/GitLabKit.Runner.Web/appsettings.Development.json @@ -13,7 +13,7 @@ }, "Connections": { "GitLabServer": "", - "RedisServer": "localhost:16379" + "RedisServer": "" }, "ApplicationInsights": { "ConnectionString": "InstrumentationKey=163976b1-0110-4102-bb20-28088f8c8fa3;IngestionEndpoint=http://localhost:9999/;" diff --git a/src/GitLabKit.Runner.Web/appsettings.json b/src/GitLabKit.Runner.Web/appsettings.json index 737c45b..0e57354 100644 --- a/src/GitLabKit.Runner.Web/appsettings.json +++ b/src/GitLabKit.Runner.Web/appsettings.json @@ -17,7 +17,9 @@ "Seq": "" }, "Secrets": { - "GitLabToken": "" + "GitLabToken": "", + "RedisUser": "", + "RedisPassword": "" }, "Connections": { "GitLabServer": "",