diff --git a/src/frontend/config/sidebar/integrations.topics.ts b/src/frontend/config/sidebar/integrations.topics.ts index 73b3ae413..c6691a8ed 100644 --- a/src/frontend/config/sidebar/integrations.topics.ts +++ b/src/frontend/config/sidebar/integrations.topics.ts @@ -868,6 +868,7 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = { }, items: [ { label: 'Docker', slug: 'integrations/compute/docker' }, + { label: 'Floci', slug: 'integrations/compute/floci' }, { label: 'K3s', slug: 'integrations/compute/k3s' }, { label: 'Kubernetes', slug: 'integrations/compute/kubernetes' }, ], diff --git a/src/frontend/src/assets/icons/floci-icon-light.svg b/src/frontend/src/assets/icons/floci-icon-light.svg new file mode 100644 index 000000000..70762bd95 --- /dev/null +++ b/src/frontend/src/assets/icons/floci-icon-light.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/src/assets/icons/floci-icon.svg b/src/frontend/src/assets/icons/floci-icon.svg new file mode 100644 index 000000000..6d26ba35b --- /dev/null +++ b/src/frontend/src/assets/icons/floci-icon.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/frontend/src/components/IntegrationGrid.astro b/src/frontend/src/components/IntegrationGrid.astro index fb7b3c7f8..3871ebf4a 100644 --- a/src/frontend/src/components/IntegrationGrid.astro +++ b/src/frontend/src/components/IntegrationGrid.astro @@ -42,6 +42,8 @@ import denoLightIcon from '@assets/icons/deno-light-icon.png'; import devTunnelsIcon from '@assets/icons/dev-tunnels-icon.svg'; import dockerIcon from '@assets/icons/docker.svg'; import elasticIcon from '@assets/icons/elastic-icon.png'; +import flociIcon from '@assets/icons/floci-icon.svg'; +import flociLightIcon from '@assets/icons/floci-icon-light.svg'; import flagDIcon from '@assets/icons/flagd-icon.svg'; import flagDLightIcon from '@assets/icons/flagd-light-icon.svg'; import garnetIcon from '@assets/icons/garnet-icon.png'; @@ -182,6 +184,7 @@ const icons = [ { meta: devTunnelsIcon, alt: 'Dev Tunnels', search: 'devtunnels' }, { meta: dockerIcon, alt: 'Docker', search: 'docker' }, { meta: elasticIcon, alt: 'Elasticsearch', search: 'elasticsearch' }, + { meta: flociIcon, alt: 'Floci', search: 'floci aws', light: flociLightIcon }, { meta: flagDIcon, alt: 'flagd', search: 'flagd', light: flagDLightIcon }, { meta: garnetIcon, alt: 'Garnet', search: 'garnet' }, { meta: goIcon, alt: 'Go', search: 'golang gofeature', light: goLightIcon }, diff --git a/src/frontend/src/content/docs/integrations/compute/floci.mdx b/src/frontend/src/content/docs/integrations/compute/floci.mdx new file mode 100644 index 000000000..0cdc64fcb --- /dev/null +++ b/src/frontend/src/content/docs/integrations/compute/floci.mdx @@ -0,0 +1,587 @@ +--- +title: Floci integration +seoTitle: Aspire Floci integration for AWS, Azure & GCP +description: Learn how to use the Aspire Floci hosting integration to emulate AWS, Azure, and GCP services locally in your development environment. +--- + +import { Image } from 'astro:assets'; +import { Aside, Badge, Tabs, TabItem } from '@astrojs/starlight/components'; +import flociIcon from '@assets/icons/floci-icon-light.svg'; + + + +
+ Floci logo +
+ + +The Aspire Floci hosting integration enables you to model [Floci](https://floci.io) — a family of high-performance local cloud emulators — as container resources in your Aspire application. Floci ships one image per cloud, each API-compatible with its respective provider: + +- **`floci/floci`** — AWS, 65+ services including Lambda, S3, DynamoDB, SQS, and SNS +- **`floci/floci-az`** — Azure, including Blob/Queue/Table Storage, Cosmos DB, Functions, Event Hubs, and Service Bus +- **`floci/floci-gcp`** — GCP, including Pub/Sub, Firestore, Datastore, Storage, Secret Manager, and Cloud Functions + +Every API is available in both C# and TypeScript (polyglot AppHost). It supports: + +- Running any combination of the AWS, Azure, and GCP emulators as container resources in the same Aspire application +- Automatic environment variable injection for dependent services to connect to the emulated cloud +- Customizable port and cloud-specific defaults (region, account ID, project ID) +- Data persistence with named volumes or bind mounts +- Docker-socket access for container-backed services (Lambda, Azure Functions, Cloud Run/Cloud SQL) +- An optional [Floci UI](https://github.com/floci-io/floci-ui) web console, attachable to one or all three clouds at once +- Custom Quarkus configuration files for the AWS emulator + +## Installation + +To start building an Aspire app that uses Floci, install the [📦 CommunityToolkit.Aspire.Hosting.Floci](https://www.nuget.org/packages/CommunityToolkit.Aspire.Hosting.Floci) NuGet package: + + + + +```bash title="Terminal" +aspire add communitytoolkit-floci +``` + +Or, choose a manual installation approach: + +```csharp title="AppHost.cs" +#:package CommunityToolkit.Aspire.Hosting.Floci@* +``` + +```xml title="AppHost.csproj" + +``` + + + + +```bash title="Terminal" +aspire add communitytoolkit-floci +``` + +This updates your `aspire.config.json` with the Floci hosting integration package: + +```json title="aspire.config.json" ins={3} +{ + "packages": { + "CommunityToolkit.Aspire.Hosting.Floci": "*" + } +} +``` + + + + +### Add a Floci resource + +Each cloud has its own dedicated method — `AddFlociAws`/`addFlociAws`, `AddFlociAzure`/`addFlociAzure`, and `AddFlociGcp`/`addFlociGcp` — and each returns its own resource type. Add whichever clouds your app depends on; they can coexist in the same AppHost. + +**AWS** + + + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var aws = builder.AddFlociAws("floci-aws"); + +var api = builder.AddProject("api") + .WithReference(aws) + .WaitFor(aws); + +builder.Build().Run(); +``` + + +```typescript title="apphost.mts" +import { createBuilder } from './.aspire/modules/aspire.mjs'; + +const builder = await createBuilder(); + +const aws = await builder.addFlociAws('floci-aws'); + +const api = await builder.addProject('api', '../Api/Api.csproj') + .withReference(aws) + .waitFor(aws); + +await builder.build().run(); +``` + + + +`WithReference(aws)`/`withReference(aws)` uses the standard Aspire connection string injection and automatically injects the AWS environment variables listed in [Environment variables](#environment-variables) into the dependent resource. + +**Azure** + + + +```csharp title="AppHost.cs" +var azure = builder.AddFlociAzure("floci-az"); + +builder.AddProject("api") + .WithReference(azure) + .WaitFor(azure); +``` + + +```typescript title="apphost.mts" +const azure = await builder.addFlociAzure('floci-az'); + +await builder.addProject('api', '../Api/Api.csproj') + .withReference(azure) + .waitFor(azure); +``` + + + +**GCP** + + + +```csharp title="AppHost.cs" +var gcp = builder.AddFlociGcp("floci-gcp", defaultProjectId: "my-project"); + +builder.AddProject("api") + .WithReference(gcp) + .WaitFor(gcp); +``` + + +```typescript title="apphost.mts" +const gcp = await builder.addFlociGcp('floci-gcp', { + defaultProjectId: 'my-project', +}); + +await builder.addProject('api', '../Api/Api.csproj') + .withReference(gcp) + .waitFor(gcp); +``` + + + +### Configure resource properties + +**AWS** accepts a custom region and account ID: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws", + defaultRegion: "eu-west-1", + defaultAccountId: "123456789012"); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws', { + defaultRegion: 'eu-west-1', + defaultAccountId: '123456789012', +}); +``` + + + +- **Port**: The host port Floci listens on (default: 4566) +- **Default Region**: AWS region for the emulator (default: `us-east-1`) +- **Default Account ID**: AWS account ID for the emulator (default: `000000000000`) + +**GCP** accepts a custom project ID: + + + +```csharp title="AppHost.cs" +var gcp = builder.AddFlociGcp("floci-gcp", + defaultProjectId: "my-project"); +``` + + +```typescript title="apphost.mts" +const gcp = await builder.addFlociGcp('floci-gcp', { + defaultProjectId: 'my-project', +}); +``` + + + +- **Port**: The host port Floci listens on (default: 4566) +- **Default Project ID**: GCP project ID for the emulator (default: `floci-local`) + +**Azure** only accepts a custom port; there's no region/account/project equivalent to configure. + +### Enable Lambda, Azure Functions, and container-backed services + +Each emulator needs access to the Docker socket to launch sibling containers for its container-backed services (AWS Lambda, Azure Functions, GCP Cloud Run/Cloud SQL). `WithDockerSocket`/`withDockerSocket` works the same way on all three clouds: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws") + .WithDockerSocket(); + +var azure = builder.AddFlociAzure("floci-az") + .WithDockerSocket(); + +var gcp = builder.AddFlociGcp("floci-gcp") + .WithDockerSocket(); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws'); +await aws.withDockerSocket(); + +const azure = await builder.addFlociAzure('floci-az'); +await azure.withDockerSocket(); + +const gcp = await builder.addFlociGcp('floci-gcp'); +await gcp.withDockerSocket(); +``` + + + +On non-standard Docker installations (e.g., Podman, Rancher Desktop), pass the socket path explicitly: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws") + .WithDockerSocket("/run/user/1000/podman/podman.sock"); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws'); +await aws.withDockerSocket({ socketPath: '/run/user/1000/podman/podman.sock' }); +``` + + + +### Add data persistence to Floci + +By default each emulator stores all state in memory. Both persistence approaches are available on all three clouds. + +#### Named volume (recommended) + +Named volumes automatically switch Floci from in-memory to persistent mode: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws") + .WithDataVolume("floci-data"); + +var azure = builder.AddFlociAzure("floci-az") + .WithDataVolume("floci-az-data"); + +var gcp = builder.AddFlociGcp("floci-gcp") + .WithDataVolume("floci-gcp-data"); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws'); +await aws.withDataVolume('floci-data'); + +const azure = await builder.addFlociAzure('floci-az'); +await azure.withDataVolume('floci-az-data'); + +const gcp = await builder.addFlociGcp('floci-gcp'); +await gcp.withDataVolume('floci-gcp-data'); +``` + + + +#### Bind mount + +Alternatively, use a host bind mount for directory-based storage: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws") + .WithDataBindMount("/path/to/data"); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws'); +await aws.withDataBindMount('/path/to/data'); +``` + + + +Either approach persists resource state across container restarts and allows local inspection of the stored state. + +### Use the cloud SDKs to interact with Floci + +Once a Floci resource is running and referenced by your service, use the matching cloud SDK as you normally would — `WithReference`/`withReference` injects the environment variables each SDK reads by convention. For example, against the AWS emulator: + + + +```csharp title="Service.cs" +using Amazon.S3; +using Amazon.S3.Model; + +public class StorageService +{ + private readonly IAmazonS3 _s3Client; + + public StorageService(IAmazonS3 s3Client) + { + _s3Client = s3Client; + } + + public async Task CreateBucketAsync(string bucketName) + { + await _s3Client.PutBucketAsync(new PutBucketRequest + { + BucketName = bucketName + }); + } + + public async Task UploadObjectAsync(string bucketName, string key, Stream stream) + { + await _s3Client.PutObjectAsync(new PutObjectRequest + { + BucketName = bucketName, + Key = key, + InputStream = stream + }); + } +} +``` + + + +### Floci UI web console + +Run the [Floci UI](https://github.com/floci-io/floci-ui) web console alongside an emulator to browse its hosted resources. `WithFlociUI`/`withFlociUI` is available on all three cloud resource types: + + + +```csharp title="AppHost.cs" +var floci = builder.AddFlociAws("floci") + .WithFlociUI(); +``` + + +```typescript title="apphost.mts" +const floci = await builder.addFlociAws('floci'); +await floci.withFlociUI(); +``` + + + +Customize the container name or pin the host port: + + + +```csharp title="AppHost.cs" +var floci = builder.AddFlociAws("floci") + .WithFlociUI(ui => ui.WithHostPort(14500), containerName: "my-floci-ui"); +``` + + +```typescript title="apphost.mts" +const floci = await builder.addFlociAws('floci'); +await floci.withFlociUI({ + containerName: 'my-floci-ui', + configureContainer: async (ui) => { + await ui.withHostPort({ port: 14500 }); + }, +}); +``` + + + + + +#### Attach all three clouds to one console + +A single UI console can attach to any combination of clouds — call `WithFlociUI`/`withFlociUI` on whichever cloud creates the console, then attach the others with `WithPluggedCloud`/`withPluggedCloud`: + + + +```csharp title="AppHost.cs" +var aws = builder.AddFlociAws("floci-aws"); +var azure = builder.AddFlociAzure("floci-az"); +var gcp = builder.AddFlociGcp("floci-gcp"); + +aws.WithFlociUI(configureContainer: ui => +{ + ui.WithPluggedCloud(azure); + ui.WithPluggedCloud(gcp); +}); +``` + + +```typescript title="apphost.mts" +const aws = await builder.addFlociAws('floci-aws'); +const azure = await builder.addFlociAzure('floci-az'); +const gcp = await builder.addFlociGcp('floci-gcp'); + +await aws.withFlociUI({ + configureContainer: async (ui) => { + await ui.withPluggedCloudAzure(azure); + await ui.withPluggedCloudGcp(gcp); + }, +}); +``` + + + +The UI container (`floci/floci-ui`) is added as a child resource of whichever cloud resource created it, wired to each attached cloud's endpoint over the container network (`FLOCI_ENDPOINT`/`FLOCI_AZURE_ENDPOINT`/`FLOCI_GCP_ENDPOINT`), and is excluded from the deployment manifest — it's a local development tool only. + + + +### Quarkus configuration file (AWS only) + +Mount a custom `application.yml` to tune any Floci setting that does not have a dedicated extension method. The file is injected read-only at `/deployments/config/application.yml` — the standard Quarkus Docker config override location. This is currently only available on the AWS emulator: + + + +```csharp title="AppHost.cs" +var floci = builder.AddFlociAws("floci") + .WithConfigFile("./floci.yml"); +``` + + +```typescript title="apphost.mts" +const floci = await builder.addFlociAws('floci'); +await floci.withConfigFile('./floci.yml'); +``` + + + +Example `floci.yml` that enables debug logging and disables signature validation: + +```yaml title="floci.yml" +floci: + auth: + validate-signatures: false +quarkus: + log: + level: DEBUG +``` + +All Floci settings can also be set via `FLOCI_`-prefixed environment variables — `WithConfigFile`/`withConfigFile` is only needed for settings that don't have a dedicated extension method. + +### Connection string / endpoint properties + +Available on all three cloud resource types: + + + +```csharp +var endpoint = floci.PrimaryEndpoint; +var host = floci.Host; +var port = floci.Port; +var connectionString = floci.ConnectionStringExpression; +``` + + +```typescript +const endpoint = await floci.primaryEndpoint(); +const host = await floci.host(); +const port = await floci.port(); +const connectionString = await floci.connectionStringExpression(); +``` + + + +`connectionStringExpression` resolves to `http://localhost:{port}` for host processes and `http://host.docker.internal:{port}` for container dependents that call `WithReference`/`withReference`. + +## Environment variables + +When a service references a Floci resource using `WithReference`/`withReference`, the following environment variables are automatically injected. + +**AWS** (`ConnectionStrings__` shown as `ConnectionStrings__floci-aws`): + +| Variable | Value | +|----------|-------| +| `ConnectionStrings__floci-aws` | `http://localhost:{port}` (standard Aspire connection string) | +| `AWS_ENDPOINT_URL` | `http://localhost:{port}` (host processes) / `http://host.docker.internal:{port}` (containers) | +| `AWS_DEFAULT_REGION` | Region passed to `AddFlociAws`/`addFlociAws` (default: `us-east-1`) | +| `AWS_ACCESS_KEY_ID` | `test` | +| `AWS_SECRET_ACCESS_KEY` | `test` | + +**Azure** (`ConnectionStrings__` shown as `ConnectionStrings__floci-az`): + +| Variable | Value | +|----------|-------| +| `ConnectionStrings__floci-az` | `http://localhost:{port}` (standard Aspire connection string) | +| `AZURE_STORAGE_CONNECTION_STRING` | Connection string pointed at the Floci Azure endpoint, using the well-known `devstoreaccount1` dev storage account credentials | + +**GCP** (`ConnectionStrings__` shown as `ConnectionStrings__floci-gcp`): + +| Variable | Value | +|----------|-------| +| `ConnectionStrings__floci-gcp` | `http://localhost:{port}` (standard Aspire connection string) | +| `PUBSUB_EMULATOR_HOST` | `localhost:{port}` (host processes) / `host.docker.internal:{port}` (containers) | +| `FIRESTORE_EMULATOR_HOST` | `localhost:{port}` (host processes) / `host.docker.internal:{port}` (containers) | +| `DATASTORE_EMULATOR_HOST` | `localhost:{port}` (host processes) / `host.docker.internal:{port}` (containers) | +| `STORAGE_EMULATOR_HOST` | `http://localhost:{port}` (host processes) / `http://host.docker.internal:{port}` (containers) | +| `SECRET_MANAGER_EMULATOR_HOST` | `localhost:{port}` (host processes) / `host.docker.internal:{port}` (containers) | +| `GOOGLE_CLOUD_PROJECT` | Project ID passed to `AddFlociGcp`/`addFlociGcp` (default: `floci-local`) | +| `CLOUDSDK_CORE_PROJECT` | Same project ID, for tools that read the `gcloud` CLI's config var instead | + +You can override any of these settings via standard Aspire environment variable configuration. All Floci-specific settings can also be set via `FLOCI_`-prefixed environment variables. + +## Integration testing + +Floci is ideal for integration testing cloud-dependent code without requiring real cloud credentials or incurring costs. Since each Floci resource is managed as a standard Aspire resource, you can use it in integration tests the same way you use other Aspire resources. + +## Supported services + +- **AWS** (`floci/floci`) — 65+ services including S3, DynamoDB, Lambda, EC2, SQS, SNS, Kinesis, RDS, CloudFormation, CloudWatch, IAM, and many more +- **Azure** (`floci/floci-az`) — Blob/Queue/Table Storage, Cosmos DB, Functions, Event Hubs, Service Bus +- **GCP** (`floci/floci-gcp`) — Pub/Sub, Firestore, Datastore, Storage, Secret Manager, Cloud Functions + +For a complete list of supported services per cloud, visit the [Floci documentation](https://floci.io). + +## Troubleshooting + +### Container fails to start + +- Ensure Docker is running and has sufficient resources +- Check that the specified port isn't already in use +- Verify the Floci container image is available locally or can be pulled from Docker Hub + +### Connection refused errors + +- Verify the Floci container is running: `docker ps | grep floci` +- Check that the endpoint URL matches the configured port +- Ensure the service has the correct `WithReference`/`withReference` to the Floci resource + +### Data persistence issues + +- Verify the bind mount directory has the correct permissions +- Check that the host path exists before starting the container +- Use absolute paths for bind mounts to avoid path resolution issues + +## See also + +- [Floci documentation](https://floci.io) +- [Floci GitHub repository](https://github.com/floci-io/floci) +- [Floci UI GitHub repository](https://github.com/floci-io/floci-ui) +- [AWS SDK for .NET](https://docs.aws.amazon.com/sdk-for-net/) +- [Aspire Hosting documentation](/architecture/overview/) +- [CommunityToolkit.Aspire.Hosting.Floci GitHub](https://github.com/CommunityToolkit/Aspire) +- [CommunityToolkit.Aspire.Hosting.Floci NuGet package](https://www.nuget.org/packages/CommunityToolkit.Aspire.Hosting.Floci) diff --git a/src/frontend/src/data/aspire-integrations.json b/src/frontend/src/data/aspire-integrations.json index f78fbe88b..d88dfbfe7 100644 --- a/src/frontend/src/data/aspire-integrations.json +++ b/src/frontend/src/data/aspire-integrations.json @@ -2220,6 +2220,26 @@ "downloads": 19927, "version": "13.4.0" }, + { + "title": "CommunityToolkit.Aspire.Hosting.Floci", + "description": "An Aspire integration for Floci, a family of high-performance local cloud emulators for AWS, Azure, and GCP.", + "icon": "https://api.nuget.org/v3-flatcontainer/communitytoolkit.aspire.hosting.floci/13.4.0-preview.1.260717/icon", + "href": "https://www.nuget.org/packages/CommunityToolkit.Aspire.Hosting.Floci", + "tags": [ + "aspire", + "integration", + "communitytoolkit", + "dotnetcommunitytoolkit", + "hosting", + "floci", + "aws", + "azure", + "gcp", + "emulator" + ], + "downloads": 0, + "version": "13.4.0-preview.1.260717" + }, { "title": "CommunityToolkit.Aspire.Hosting.Flyway", "description": "An Aspire integration for Flyway database migration tool.", @@ -3105,4 +3125,4 @@ "downloads": 19722, "version": "13.4.0" } -] \ No newline at end of file +] diff --git a/src/frontend/src/data/integration-docs.json b/src/frontend/src/data/integration-docs.json index a12098aaa..8e34b78a3 100644 --- a/src/frontend/src/data/integration-docs.json +++ b/src/frontend/src/data/integration-docs.json @@ -403,6 +403,10 @@ "match": "CommunityToolkit.Aspire.Hosting.Flagd", "href": "/integrations/devtools/flagd/flagd-get-started/" }, + { + "match": "CommunityToolkit.Aspire.Hosting.Floci", + "href": "/integrations/compute/floci/" + }, { "match": "CommunityToolkit.Aspire.Hosting.GoFeatureFlag", "href": "/integrations/devtools/goff/goff-get-started/"