-
Notifications
You must be signed in to change notification settings - Fork 816
Expand file tree
/
Copy pathKeycloakResourceBuilderExtensions.cs
More file actions
164 lines (146 loc) · 7.08 KB
/
KeycloakResourceBuilderExtensions.cs
File metadata and controls
164 lines (146 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Keycloak;
using Aspire.Hosting.Utils;
namespace Aspire.Hosting;
/// <summary>
/// Provides extension methods for adding Keycloak resources to an <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class KeycloakResourceBuilderExtensions
{
private const string AdminEnvVarName = "KEYCLOAK_ADMIN";
private const string AdminPasswordEnvVarName = "KEYCLOAK_ADMIN_PASSWORD";
private const int DefaultContainerPort = 8080;
private const string RealmImportDirectory = "/opt/keycloak/data/import";
/// <summary>
/// Adds a Keycloak container to the application model. This version of the package defaults to the <inheritdoc cref="KeycloakContainerImageTags.Tag"/> tag of the <inheritdoc cref="KeycloakContainerImageTags.Registry"/>/<inheritdoc cref="KeycloakContainerImageTags.Image"/> container image.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="name">The name of the resource. </param>
/// <param name="port">The host port that the underlying container is bound to when running locally.</param>
/// <param name="adminUsername">The parameter used as the admin for the Keycloak resource. If <see langword="null"/> a default value will be used.</param>
/// <param name="adminPassword">The parameter used as the admin password for the Keycloak resource. If <see langword="null"/> a default password will be used.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// The container exposes port 8080 by default.
/// </remarks>
/// <example>
/// Use in application host
/// <code lang="csharp">
/// var keycloak = builder.AddKeycloak("keycloak");
///
/// var myService = builder.AddProject<Projects.MyService<()
/// .WithReference(keycloak);
/// </code>
/// </example>
public static IResourceBuilder<KeycloakResource> AddKeycloak(
this IDistributedApplicationBuilder builder,
string name,
int? port = null,
IResourceBuilder<ParameterResource>? adminUsername = null,
IResourceBuilder<ParameterResource>? adminPassword = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(name);
var passwordParameter = adminPassword?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"{name}-password");
var resource = new KeycloakResource(name, adminUsername?.Resource, passwordParameter);
var keycloak = builder
.AddResource(resource)
.WithImage(KeycloakContainerImageTags.Image)
.WithImageRegistry(KeycloakContainerImageTags.Registry)
.WithImageTag(KeycloakContainerImageTags.Tag)
.WithHttpEndpoint(port: port, targetPort: DefaultContainerPort)
.WithEnvironment(context =>
{
context.EnvironmentVariables[AdminEnvVarName] = resource.AdminReference;
context.EnvironmentVariables[AdminPasswordEnvVarName] = resource.AdminPasswordParameter;
});
if (builder.ExecutionContext.IsRunMode)
{
keycloak.WithArgs("start-dev");
}
else
{
keycloak.WithArgs("start");
}
keycloak.WithArgs("--import-realm");
return keycloak;
}
/// <summary>
/// Adds a named volume for the data folder to a Keycloak container resource.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="name">The name of the volume. Defaults to an auto-generated name based on the application and resource names.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// The volume is mounted at /opt/keycloak/data in the container.
/// </remarks>
/// <example>
/// Use a data volume
/// <code lang="csharp">
/// var keycloak = builder.AddKeycloak("keycloak")
/// .WithDataVolume();
/// </code>
/// </example>
public static IResourceBuilder<KeycloakResource> WithDataVolume(this IResourceBuilder<KeycloakResource> builder, string? name = null)
{
ArgumentNullException.ThrowIfNull(builder);
return builder.WithVolume(name ?? VolumeNameGenerator.CreateVolumeName(builder, "data"), "/opt/keycloak/data",
false);
}
/// <summary>
/// Adds a bind mount for the data folder to a Keycloak container resource.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="source">The source directory on the host to mount into the container.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// The source directory is mounted at /opt/keycloak/data in the container.
/// </remarks>
/// <example>
/// Use a bind mount
/// <code lang="csharp">
/// var keycloak = builder.AddKeycloak("keycloak")
/// .WithDataBindMount("mydata");
/// </code>
/// </example>
public static IResourceBuilder<KeycloakResource> WithDataBindMount(this IResourceBuilder<KeycloakResource> builder, string source)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(source);
return builder.WithBindMount(source, "/opt/keycloak/data", false);
}
/// <summary>
/// Adds a realm import to a Keycloak container resource.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="importDirectory">The directory containing the realm import files.</param>
/// <param name="isReadOnly">A flag that indicates if the realm import directory is read-only.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// The realm import files are mounted at /opt/keycloak/data/import in the container.
/// </remarks>
/// <example>
/// Import the realms from a directory
/// <code lang="csharp">
/// var keycloak = builder.AddKeycloak("keycloak")
/// .WithRealmImport("../realms");
/// </code>
/// </example>
public static IResourceBuilder<KeycloakResource> WithRealmImport(
this IResourceBuilder<KeycloakResource> builder,
string importDirectory,
bool isReadOnly = false)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(importDirectory);
var importDirectoryFullPath = Path.GetFullPath(importDirectory, builder.ApplicationBuilder.AppHostDirectory);
if (!Directory.Exists(importDirectoryFullPath))
{
throw new DirectoryNotFoundException($"The realm import directory '{importDirectoryFullPath}' does not exist.");
}
builder.WithBindMount(importDirectoryFullPath, RealmImportDirectory, isReadOnly);
return builder;
}
}