Skip to content

Commit 42a20c8

Browse files
committed
Create a single node cluster by default with no add'l config required
1 parent 66e3645 commit 42a20c8

File tree

8 files changed

+148
-12
lines changed

8 files changed

+148
-12
lines changed

src/Couchbase.Aspire.Client/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,13 @@ Then, in the _AppHost.cs_ file of your project, register a Couchbase cluster and
128128

129129
```csharp
130130
var couchbase = builder.AddCouchbase("couchbase");
131-
var servers = couchbase.AddServerGroup("couchbase_servers");
132-
var bucket = servers.AddBucket("mybucket");
131+
var bucket = couchbase.AddBucket("mybucket");
133132

134133
var myService = builder.AddProject<Projects.MyService>()
135134
.WithReference(bucket)
136135
.WaitFor(bucket);
137136

138-
// Altertively, reference the cluster rather than a specific bucket, INamedBucketProvider will not be registered in DI
137+
// Alternatively, reference the cluster rather than a specific bucket, INamedBucketProvider will not be registered in DI
139138
var myService2 = builder.AddProject<Projects.MyService>()
140139
.WithReference(couchbase)
141140
.WaitFor(couchbase);

src/Couchbase.Aspire.Hosting/CouchbaseClusterBuilderExtensions.cs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public static IResourceBuilder<CouchbaseClusterResource> AddCouchbase(this IDist
113113
serviceRequirementsFactory: _ => cluster.GetHealthCheckServiceRequirements(),
114114
name: healthCheckKey);
115115

116-
return builder.AddResource(cluster)
116+
var clusterBuilder = builder.AddResource(cluster)
117117
.WithInitialState(new()
118118
{
119119
ResourceType = "CouchbaseCluster",
@@ -148,6 +148,49 @@ public static IResourceBuilder<CouchbaseClusterResource> AddCouchbase(this IDist
148148
});
149149
}
150150
})
151+
.OnInitializeResource(async (resource, @event, ct) =>
152+
{
153+
// Ensure we display the Docker image as the Source for the cluster
154+
155+
var server = resource.GetPrimaryServer();
156+
if (server is not null)
157+
{
158+
_ = Task.Run(async () =>
159+
{
160+
var rns = @event.Services.GetRequiredService<ResourceNotificationService>();
161+
162+
await foreach (var @event in rns.WatchAsync(ct).ConfigureAwait(false))
163+
{
164+
if (@event.Resource == server)
165+
{
166+
var serverSource = @event.Snapshot.Properties
167+
.FirstOrDefault(p => p.Name == "container.image")?
168+
.Value;
169+
170+
if (serverSource is not null)
171+
{
172+
await rns.PublishUpdateAsync(resource, s =>
173+
{
174+
var currentSource = s.Properties.FirstOrDefault(p => p.Name == CustomResourceKnownProperties.Source);
175+
176+
if (serverSource != currentSource?.Value)
177+
{
178+
return s with
179+
{
180+
Properties =
181+
(currentSource is not null ? s.Properties.Remove(currentSource) : s.Properties)
182+
.Add(new(CustomResourceKnownProperties.Source, serverSource))
183+
};
184+
}
185+
186+
return s;
187+
});
188+
}
189+
}
190+
}
191+
}, ct);
192+
}
193+
})
151194
.WithCommand(KnownResourceCommands.StartCommand, "Start", async (context) =>
152195
{
153196
var orchestrator = context.ServiceProvider.GetRequiredService<CouchbaseClusterOrchestrator>();
@@ -207,6 +250,33 @@ public static IResourceBuilder<CouchbaseClusterResource> AddCouchbase(this IDist
207250
IconVariant = IconVariant.Filled,
208251
IsHighlighted = true,
209252
});
253+
254+
// Add the default single node server group
255+
clusterBuilder.AddServerGroup($"{name}-server", isDefaultServerGroup: true);
256+
257+
return clusterBuilder;
258+
}
259+
260+
/// <summary>
261+
/// Specify the Couchbase services to be enabled on this cluster. Only applies if no server groups are added.
262+
/// </summary>
263+
/// <param name="builder">Builder for the Couchbase cluster.</param>
264+
/// <param name="services">The services to be enabled.</param>
265+
/// <returns>The <paramref name="builder"/>.</returns>
266+
public static IResourceBuilder<CouchbaseClusterResource> WithServices(this IResourceBuilder<CouchbaseClusterResource> builder,
267+
CouchbaseServices services)
268+
{
269+
ArgumentNullException.ThrowIfNull(builder);
270+
271+
var serverGroup = builder.Resource.ServerGroups.Values.FirstOrDefault(p => p.IsDefaultServerGroup);
272+
if (serverGroup is null)
273+
{
274+
throw new InvalidOperationException("Services may only be set on the Couchbase cluster when no service groups are added. Use WithServices on the server group instead.");
275+
}
276+
277+
builder.ApplicationBuilder.CreateResourceBuilder(serverGroup).WithServices(services);
278+
279+
return builder;
210280
}
211281

212282
public static IResourceBuilder<CouchbaseClusterResource> WithSettings(this IResourceBuilder<CouchbaseClusterResource> builder, Action<CouchbaseClusterSettingsCallbackContext> configureSettings)

src/Couchbase.Aspire.Hosting/CouchbaseClusterResource.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ internal void AddServerGroup(string name, CouchbaseServerGroupResource serverGro
128128
_serverGroups.TryAdd(name, serverGroup);
129129
}
130130

131+
internal bool RemoveServerGroup(string name)
132+
{
133+
return _serverGroups.Remove(name);
134+
}
135+
131136
public IEnumerable<CouchbaseServerResource> Servers => _serverGroups.Values.SelectMany(g => g.Servers);
132137

133138
private readonly Dictionary<string, CouchbaseBucketBaseResource> _buckets = new(StringComparer.OrdinalIgnoreCase);

src/Couchbase.Aspire.Hosting/CouchbaseServerBuilderExtensions.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static IResourceBuilder<CouchbaseServerResource> AddServer(this IResource
1919
var server = new CouchbaseServerResource(name, builder.Resource);
2020
builder.Resource.AddServer(server);
2121

22-
return builder.ApplicationBuilder.AddResource(server)
22+
var serverBuilder = builder.ApplicationBuilder.AddResource(server)
2323
.WithParentRelationship(builder)
2424
.WithImage(CouchbaseContainerImageTags.Image, CouchbaseContainerImageTags.Tag)
2525
.WithImageRegistry(CouchbaseContainerImageTags.Registry)
@@ -62,9 +62,25 @@ CouchbaseEndpointNames.FtsSecure or CouchbaseEndpointNames.AnalyticsSecure or Co
6262
}
6363
}
6464
})
65+
6566
.WithNodeCertificate()
6667
.ExcludeFromManifest()
6768
.ApplyDynamicConfiguration();
69+
70+
// Hide the server if the server group is the default server group, which is also hidden
71+
if (builder.Resource.IsDefaultServerGroup)
72+
{
73+
serverBuilder.OnInitializeResource(async (resource, @event, ct) =>
74+
{
75+
var rns = @event.Services.GetRequiredService<ResourceNotificationService>();
76+
await rns.PublishUpdateAsync(resource, s => s with
77+
{
78+
IsHidden = true
79+
}).ConfigureAwait(false);
80+
});
81+
}
82+
83+
return serverBuilder;
6884
}
6985

7086
private static IResourceBuilder<CouchbaseServerResource> ApplyInsecureEndpoints(this IResourceBuilder<CouchbaseServerResource> builder)

src/Couchbase.Aspire.Hosting/CouchbaseServerGroupBuilderExtensions.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,30 @@ namespace Couchbase.Aspire.Hosting;
66
public static class CouchbaseServerGroupBuilderExtensions
77
{
88
public static IResourceBuilder<CouchbaseServerGroupResource> AddServerGroup(this IResourceBuilder<CouchbaseClusterResource> builder,
9-
[ResourceName] string name)
9+
[ResourceName] string name) =>
10+
builder.AddServerGroup(name, isDefaultServerGroup: false);
11+
12+
internal static IResourceBuilder<CouchbaseServerGroupResource> AddServerGroup(this IResourceBuilder<CouchbaseClusterResource> builder,
13+
[ResourceName] string name,
14+
bool isDefaultServerGroup)
1015
{
1116
ArgumentNullException.ThrowIfNull(builder);
1217
ArgumentException.ThrowIfNullOrEmpty(name);
1318

14-
var serverGroup = new CouchbaseServerGroupResource(name, builder.Resource);
19+
var defaultServerGroup = builder.Resource.ServerGroups.FirstOrDefault(p => p.Value.IsDefaultServerGroup);
20+
if (defaultServerGroup.Key is not null)
21+
{
22+
// We're adding manual server groups, remove the default one
23+
builder.Resource.RemoveServerGroup(defaultServerGroup.Key);
24+
25+
builder.ApplicationBuilder.Resources.Remove(defaultServerGroup.Value);
26+
foreach (var server in defaultServerGroup.Value.Servers)
27+
{
28+
builder.ApplicationBuilder.Resources.Remove(server);
29+
}
30+
}
31+
32+
var serverGroup = new CouchbaseServerGroupResource(name, builder.Resource, isDefaultServerGroup);
1533
builder.Resource.AddServerGroup(name, serverGroup);
1634

1735
var serverGroupBuilder = builder.ApplicationBuilder.AddResource(serverGroup)
@@ -20,6 +38,7 @@ public static IResourceBuilder<CouchbaseServerGroupResource> AddServerGroup(this
2038
ResourceType = "CouchbaseServerGroup",
2139
CreationTimeStamp = DateTime.UtcNow,
2240
State = KnownResourceStates.NotStarted,
41+
IsHidden = isDefaultServerGroup,
2342
Properties =
2443
[
2544
new(CustomResourceKnownProperties.Source, "Couchbase")

src/Couchbase.Aspire.Hosting/CouchbaseServerGroupResource.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ public CouchbaseServerGroupResource(string name, CouchbaseClusterResource parent
1111
Parent = parent;
1212
}
1313

14+
internal CouchbaseServerGroupResource(string name, CouchbaseClusterResource parent, bool isDefaultServerGroup) : this(name, parent)
15+
{
16+
IsDefaultServerGroup = isDefaultServerGroup;
17+
}
18+
1419
/// <summary>
1520
/// Gets the parent Couchbase Server container resource.
1621
/// </summary>
1722
public CouchbaseClusterResource Parent { get; }
1823

24+
internal bool IsDefaultServerGroup { get; }
25+
1926
private readonly List<CouchbaseServerResource> _servers = [];
2027

2128
/// <summary>

src/Couchbase.Aspire.Hosting/README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,33 @@ dotnet add package Couchbase.Aspire.Hosting
1212

1313
## Usage example
1414

15-
In the _AppHost.cs_ file of your `AppHost`, add a Couchbase cluster resource and consume the connection using the following methods:
15+
In the _AppHost.cs_ file of your `AppHost`, add a Couchbase cluster resource with the data, query, and index services
16+
and consume the connection using the following methods:
1617

1718
```csharp
1819
var couchbase = builder.AddCouchbase("couchbase");
19-
var servers = couchbase.AddServerGroup("couchbase_servers");
20-
var bucket = servers.AddBucket("mybucket");
20+
var bucket = couchbase.AddBucket("mybucket");
21+
22+
var myService = builder.AddProject<Projects.MyService>()
23+
.WithReference(bucket)
24+
.WaitFor(bucket);
25+
```
26+
27+
### Multi-dimensional scaling example
28+
29+
It is also possible to run a multiple server cluster with various services assigned to different server groups:
30+
31+
```csharp
32+
var couchbase = builder.AddCouchbase("couchbase");
33+
var bucket1 = couchbase.AddBucket("bucket1");
34+
35+
var group1 = couchbase.AddServerGroup("couchbase-group1")
36+
.WithServices(CouchbaseServices.Data)
37+
.WithReplicas(2);
38+
39+
var group2 = couchbase.AddServerGroup("couchbase-group2")
40+
.WithServices(CouchbaseServices.Index | CouchbaseServices.Query)
41+
.WithReplicas(2);
2142

2243
var myService = builder.AddProject<Projects.MyService>()
2344
.WithReference(bucket)
@@ -40,7 +61,6 @@ The Couchbase cluster resource exposes the following connection properties:
4061

4162
The `ConnectionString` property is also available, which exposes all properties as a single string in the format `couchbase://{Username}:{Password}@{Host}:{Port},{Host2}:{Port}`.
4263

43-
4464
### Couchbase bucket
4565

4666
The Couchbase bucket resource exposes the following connection properties:

tests/Aspire.Test.AppHost/AppHost.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
var builder = DistributedApplication.CreateBuilder(args);
66

7-
var couchbasePassword = builder.AddParameter("couchbase-password", "password");
7+
var couchbasePassword = builder.AddParameter("couchbase-password", "password", secret: true);
88

99
var couchbase = builder.AddCouchbase("couchbase", password: couchbasePassword)
1010
.WithManagementPort(8091) // Optional fixed port number for the primary node

0 commit comments

Comments
 (0)