Skip to content

Commit 79d5bcb

Browse files
committed
Demonstrate index management with a custom CA cert
1 parent 0475113 commit 79d5bcb

File tree

6 files changed

+84
-10
lines changed

6 files changed

+84
-10
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ indent_size = 2
1313
[nuget.config]
1414
indent_size = 2
1515

16-
[*.{yaml,yml}]
16+
[*.{yaml,yml,json}]
1717
indent_size = 2
1818

1919
[*.cs]

src/Couchbase.Aspire.Hosting.Indices/CouchbaseIndicesBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public static IResourceBuilder<CouchbaseIndexManagerResource> WithIndices(this I
8282
}
8383
catch (IOException ex)
8484
{
85-
throw new DistributedApplicationException($"Index definition path '{path}' is invalid.", ex);
85+
throw new InvalidOperationException($"Index definition path '{path}' is invalid.", ex);
8686
}
8787
}
8888

src/Couchbase.Aspire.Hosting.Indices/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,28 @@ var myService = builder.AddProject<Projects.MyService>()
4646
Index definition files are JSON or YAML files that define the indexes to be created on a Couchbase bucket. They may be referenced by directory
4747
or by individual file. See [couchbase-index-manager Documentation](https://github.com/brantburnett/couchbase-index-manager/tree/main/packages/couchbase-index-manager-cli#definition-files) for details on the file format.
4848

49+
## Using with a secure Couchbase cluster
50+
51+
If your Couchbase cluster uses TLS/SSL, you may need to configure the index manager to trust the cluster's certificate.
52+
53+
```csharp
54+
var certAuthorityCollection = builder.AddCertificateAuthorityCollection("couchbase-cert-authority")
55+
.WithCertificate(certificateWithPrivateKey);
56+
57+
var couchbase = builder.AddCouchbase("couchbase");
58+
var bucket = couchbase.AddBucket("mybucket")
59+
.WithRootCertificateAuthority(certAuthorityCollection); // Use the certificate for the Couchbase cluster
60+
61+
var bucketIndices = bucket.AddIndexManager("mybucket-indices")
62+
.WithIndices("../indices")
63+
.WithCertificateAuthorityCollection(certAuthorityCollection); // Trust the certificate for index management
64+
65+
var myService = builder.AddProject<Projects.MyService>()
66+
.WithReference(bucket)
67+
.WaitFor(bucket)
68+
.WaitForCompletion(bucketIndices);
69+
```
70+
4971
## Feedback & contributing
5072

5173
https://github.com/couchbaselabs/couchbase-aspire

src/Couchbase.Aspire.Hosting/CouchbaseClusterBuilderExtensions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,32 @@ public static IResourceBuilder<CouchbaseClusterResource> WithRootCertificationAu
530530
return builder.WithAnnotation(annotation, ResourceAnnotationMutationBehavior.Replace);
531531
}
532532

533+
/// <summary>
534+
/// Enables TLS for cluster communications using a root certification authority.
535+
/// </summary>
536+
/// <param name="builder">Builder for the couchbase cluster.</param>
537+
/// <param name="certAuthorityCollection">Collection that includes the certificate. Must include a private key.</param>
538+
/// <param name="trustCertificate">If the certificate must be explicitly trusted for initialization and health check operations.</param>
539+
/// <returns>The <paramref name="builder"/>.</returns>
540+
/// <remarks>
541+
/// The root certificate must be trusted by any application connecting to the cluster. It must also be trusted
542+
/// by the host running Aspire if <paramref name="trustCertificate"/> is <c>false</c>.
543+
/// </remarks>
544+
public static IResourceBuilder<CouchbaseClusterResource> WithRootCertificationAuthority(this IResourceBuilder<CouchbaseClusterResource> builder,
545+
IResourceBuilder<CertificateAuthorityCollection> certAuthorityCollection, bool trustCertificate = false)
546+
{
547+
ArgumentNullException.ThrowIfNull(builder);
548+
ArgumentNullException.ThrowIfNull(certAuthorityCollection);
549+
550+
var certificate = certAuthorityCollection.Resource.Certificates.FirstOrDefault(p => p.HasPrivateKey);
551+
if (certificate is null)
552+
{
553+
throw new InvalidOperationException($"{nameof(certAuthorityCollection)} must include a certificate with a private key.");
554+
}
555+
556+
return builder.WithRootCertificationAuthority(certificate, trustCertificate: trustCertificate);
557+
}
558+
533559
/// <summary>
534560
/// Sets the lifetime behavior of the Couchbase cluster.
535561
/// </summary>

tests/Aspire.Test.AppHost/AppHost.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
using Couchbase.Aspire.Hosting;
22
using Couchbase.KeyValue;
33
using Couchbase.Management.Buckets;
4+
using Microsoft.Extensions.Configuration;
45

56
var builder = DistributedApplication.CreateBuilder(args);
67

78
var couchbasePassword = builder.AddParameter("couchbase-password", "password", secret: true);
89

910
var couchbase = builder.AddCouchbase("couchbase", password: couchbasePassword)
1011
.WithManagementPort(8091) // Optional fixed port number for the primary node
11-
.WithSecureManagementPort(18091) // Optional fixed port number for the primary node
1212
.WithCouchbaseEdition(CouchbaseEdition.Enterprise); // Optional edition, default is Enterprise
1313

14-
// Uncomment this section to test building a secure cluster. Note that the test web app will not be
15-
// able to connect unless the certificate is trusted on the host machine.
16-
// var certificate = Aspire.Test.AppHost.Helpers.LoadCACertificate("CouchbaseCA.pfx");
17-
// couchbase.WithRootCertificationAuthority(certificate, trustCertificate: true);
18-
1914
var couchbaseGroup1 = couchbase.AddServerGroup("couchbase-group1")
2015
.WithServices(CouchbaseServices.Data | CouchbaseServices.Query | CouchbaseServices.Index | CouchbaseServices.Search)
2116
.WithReplicas(2);
@@ -30,7 +25,9 @@
3025
.WithMinimumDurabilityLevel(DurabilityLevel.MajorityAndPersistToActive)
3126
.WithEvictionPolicy(EvictionPolicyType.FullEviction)
3227
.WithCompressionMode(CompressionMode.Active)
33-
.WithReplicas(0)
28+
.WithReplicas(0);
29+
30+
var testIndices = testBucket.AddIndexManager("test-indices")
3431
.WithIndices("test-indices");
3532

3633
var cacheBucket = couchbase.AddBucket("cache-bucket", bucketName: "cache")
@@ -45,6 +42,21 @@
4542
builder.AddProject<Projects.Aspire_Test_WebApp>("aspire-test-webapp")
4643
.WithExternalHttpEndpoints()
4744
.WithHttpHealthCheck("/health")
48-
.WithReference(testBucket).WaitFor(testBucket);
45+
.WithReference(testBucket).WaitFor(testBucket)
46+
.WaitForCompletion(testIndices);
47+
48+
if (builder.Configuration.GetValue("COUCHBASE_SECURE", false))
49+
{
50+
// Note that the test web app will not be able to connect unless the certificate is trusted on the host machine.
51+
var certAuthorityCollection = builder.AddCertificateAuthorityCollection("couchbase-cert-authority")
52+
.WithCertificate(Aspire.Test.AppHost.Helpers.LoadCACertificate("CouchbaseCA.pfx"));
53+
54+
couchbase
55+
.WithSecureManagementPort(18091) // Optional fixed port number for the primary node
56+
.WithRootCertificationAuthority(certAuthorityCollection, trustCertificate: true);
57+
58+
// Trust the Couchbase CA for the index manager
59+
testIndices.WithCertificateAuthorityCollection(certAuthorityCollection);
60+
}
4961

5062
builder.Build().Run();

tests/Aspire.Test.AppHost/Properties/launchSettings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23063",
1414
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22041"
1515
}
16+
},
17+
"https+couchbases": {
18+
"commandName": "Project",
19+
"dotnetRunMessages": true,
20+
"launchBrowser": true,
21+
"applicationUrl": "https://localhost:17182;http://localhost:15107",
22+
"environmentVariables": {
23+
"COUCHBASE_SECURE": "true",
24+
"ASPNETCORE_ENVIRONMENT": "Development",
25+
"DOTNET_ENVIRONMENT": "Development",
26+
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21071",
27+
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23063",
28+
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22041"
29+
}
1630
}
1731
}
1832
}

0 commit comments

Comments
 (0)