-
-
Notifications
You must be signed in to change notification settings - Fork 356
feat: Add Testcontainers.FlociAz module #1729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
thomhurst
wants to merge
5
commits into
testcontainers:develop
Choose a base branch
from
thomhurst:feat/flociaz-module
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
39d2989
feat: Add Testcontainers.FlociAz module
thomhurst 1d67cf0
Merge branch 'develop' into feat/flociaz-module
HofmeisterAn 67dc760
test(FlociAz): cover supported services
thomhurst 1b32914
test(FlociAz): stabilize ARM request bodies
thomhurst 9c64545
feat(FlociAz): add sidecar compatibility
thomhurst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # FlociAz | ||
|
|
||
| [FlociAz](https://github.com/floci-io/floci-az) emulates Azure management and data-plane APIs in one container. The module starts in a Docker-safe mode: services that would otherwise create child containers use their mocked or embedded implementations. | ||
|
|
||
| Add the module to a test project: | ||
|
|
||
| ```shell | ||
| dotnet add package Testcontainers.FlociAz | ||
| ``` | ||
|
|
||
| Start FlociAz and use its storage connection string or service-specific endpoints: | ||
|
|
||
| ```csharp | ||
| await using var flociAz = new FlociAzBuilder("floci/floci-az:0.12.0") | ||
| .Build(); | ||
|
|
||
| await flociAz.StartAsync(); | ||
|
|
||
| var blobs = new BlobServiceClient(flociAz.GetConnectionString()); | ||
| var keyVaultEndpoint = flociAz.GetServiceEndpoint("keyvault"); | ||
| var armEndpoint = flociAz.GetEndpoint(); | ||
| ``` | ||
|
|
||
| ## Service compatibility | ||
|
|
||
| The following matrix is covered against FlociAz 0.12.0. “Real” means the test reaches the service's actual protocol or runtime, not only its ARM representation. | ||
|
|
||
| | Service | Verified compatibility | | ||
| |---------|------------------------| | ||
| | Blob Storage | Azure Storage SDK create, upload, and download | | ||
| | Queue Storage | Azure Storage SDK create, send, and receive | | ||
| | Table Storage | Azure Data Tables SDK create, insert, and read | | ||
| | Functions | Management lifecycle, mocked invocation, and real Node.js runtime execution | | ||
| | App Configuration | Key-value write and read | | ||
| | Cosmos DB for NoSQL | Database, container, and partitioned document lifecycle | | ||
| | Key Vault | Authenticated secret write and read | | ||
| | Event Hubs | Mocked namespace management only | | ||
| | Azure SQL Database | ARM server lifecycle in the default management-only provider | | ||
| | Azure Database for PostgreSQL | ARM lifecycle and real Npgsql query | | ||
| | Service Bus | Mocked queue/topic/subscription/rule topology and real Azure SDK AMQP send/receive | | ||
| | Azure Monitor | Workspace, collection endpoint/rule, log ingestion, and KQL query | | ||
| | AKS | Mocked ARM cluster lifecycle | | ||
| | Azure Container Instances | Mocked ARM container-group lifecycle | | ||
| | Virtual Machines | Mocked ARM VM lifecycle | | ||
| | API Management | ARM service lifecycle | | ||
| | Azure Cache for Redis | ARM lifecycle and real RESP write/read | | ||
| | Azure Container Registry | ARM lifecycle and real Registry V2 API | | ||
| | Microsoft Entra ID | OAuth client-credentials token issuance | | ||
| | Microsoft Graph | Service-principal discovery and seeded group membership | | ||
| | Communication Services Email | Send operation and inspection mailbox | | ||
| | Azure Resource Manager | Resource-group and service resource lifecycle | | ||
| | Virtual Network | ARM virtual-network lifecycle | | ||
| | Event Grid | Topic keys, event publication, and lifecycle | | ||
| | Managed Identity | User-assigned ARM lifecycle and IMDS token issuance | | ||
|
|
||
| ### Upstream 0.12.0 boundaries | ||
|
|
||
| - Event Hubs AMQP is deliberately hard-coded to mocked mode upstream because Azure SDK connections reset. | ||
| - Azure Container Instances accepts `mocked=false`, but 0.12.0 still behaves as mocked mode; container-backed mode is planned upstream. | ||
| - AKS real mode starts k3s, but does not reliably transition the ARM resource from `Creating` to `Succeeded` in the containerized Testcontainers topology. The module therefore defaults it to mocked mode. | ||
| - Azure SQL's managed data plane requires explicit acceptance of the Microsoft SQL Server EULA. The module never accepts it on the user's behalf; enable and test that mode only after reviewing the license. | ||
|
|
||
| ## Docker-backed services | ||
|
|
||
| Functions, PostgreSQL, Service Bus, Redis, and ACR have verified real modes. Grant FlociAz Docker access and opt individual services into real mode: | ||
|
|
||
| ```csharp | ||
| await using var flociAz = new FlociAzBuilder("floci/floci-az:0.12.0") | ||
| .WithDockerSocket() | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_FUNCTIONS_MOCKED", "false") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_POSTGRES_MOCKED", "false") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_SERVICE_BUS_MOCKED", "false") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_REDIS_MOCKED", "false") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_ACR_MOCKED", "false") | ||
| .Build(); | ||
| ``` | ||
|
|
||
| !!! warning | ||
|
|
||
| The Docker socket provides root-equivalent access to the Docker host. Use `WithDockerSocket()` only with trusted images. Child containers and volumes are namespaced and registered with the Testcontainers Resource Reaper. | ||
|
|
||
| FlociAz `/connect` responses contain the child container's internal hostname and port. Resolve that pair to a host port before connecting from the test process: | ||
|
|
||
| ```csharp | ||
| var mappedPort = await flociAz.GetSidecarMappedPublicPortAsync( | ||
| sidecarHostname, | ||
| sidecarPrivatePort); | ||
| ``` | ||
|
|
||
| Use `flociAz.Hostname` with the returned port. This works with local and remote Docker endpoints supported by Testcontainers. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| root = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| namespace Testcontainers.FlociAz; | ||
|
|
||
| /// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" /> | ||
| [PublicAPI] | ||
| public sealed class FlociAzBuilder : ContainerBuilder<FlociAzBuilder, FlociAzContainer, FlociAzConfiguration> | ||
| { | ||
| private const string DockerSocket = "/var/run/docker.sock"; | ||
|
|
||
| public const ushort FlociAzPort = 4577; | ||
|
|
||
| public const string AccountName = "devstoreaccount1"; | ||
|
|
||
| public const string AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="image"> | ||
| /// The full Docker image name, including the image repository and tag | ||
| /// (e.g., <c>floci/floci-az:0.12.0</c>). | ||
| /// </param> | ||
| /// <remarks> | ||
| /// Docker image tags available at <see href="https://hub.docker.com/r/floci/floci-az/tags" />. | ||
| /// </remarks> | ||
| public FlociAzBuilder(string image) | ||
| : this(new DockerImage(image)) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="image"> | ||
| /// An <see cref="IImage" /> instance that specifies the Docker image to be used | ||
| /// for the container builder configuration. | ||
| /// </param> | ||
| /// <remarks> | ||
| /// Docker image tags available at <see href="https://hub.docker.com/r/floci/floci-az/tags" />. | ||
| /// </remarks> | ||
| public FlociAzBuilder(IImage image) | ||
| : this(new FlociAzConfiguration()) | ||
| { | ||
| DockerResourceConfiguration = Init().WithImage(image).DockerResourceConfiguration; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzBuilder" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| private FlociAzBuilder(FlociAzConfiguration resourceConfiguration) | ||
| : base(resourceConfiguration) | ||
| { | ||
| DockerResourceConfiguration = resourceConfiguration; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override FlociAzConfiguration DockerResourceConfiguration { get; } | ||
|
|
||
| /// <summary> | ||
| /// Grants FlociAz access to the Docker daemon for services that use sidecar containers. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The Docker socket provides root-equivalent access to the Docker host. Only enable it for | ||
| /// trusted images. FlociAz child containers and volumes receive a unique namespace that is | ||
| /// registered with the Testcontainers Resource Reaper. | ||
| /// </remarks> | ||
| /// <param name="dockerSocket">The host Docker socket path, or <c>null</c> to detect it.</param> | ||
| /// <returns>A configured instance of <see cref="FlociAzBuilder" />.</returns> | ||
| public FlociAzBuilder WithDockerSocket(string dockerSocket = null) | ||
| { | ||
| var endpoint = DockerResourceConfiguration.DockerEndpointAuthConfig.Endpoint; | ||
| var detectedSocket = endpoint.Scheme.Equals("unix", StringComparison.OrdinalIgnoreCase) ? endpoint.AbsolutePath : DockerSocket; | ||
| var source = dockerSocket ?? TestcontainersSettings.DockerSocketOverride ?? detectedSocket; | ||
| var resourceNamespace = "tc-" + Guid.NewGuid().ToString("N"); | ||
|
|
||
| return WithBindMount(source, DockerSocket, AccessMode.ReadWrite) | ||
| .WithEnvironment("FLOCI_AZ_DOCKER_RESOURCE_NAMESPACE", resourceNamespace); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public override FlociAzContainer Build() | ||
| { | ||
| Validate(); | ||
| return new FlociAzContainer(DockerResourceConfiguration); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override FlociAzBuilder Init() | ||
| { | ||
| return base.Init() | ||
| .WithPortBinding(FlociAzPort, true) | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_EVENT_HUB_ENABLED", "false") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_FUNCTIONS_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_POSTGRES_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_AKS_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_ACR_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_REDIS_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_SERVICE_BUS_MOCKED", "true") | ||
| .WithEnvironment("FLOCI_AZ_SERVICES_COSMOS_MOCKED", "true") | ||
| .WithConnectionStringProvider(new FlociAzConnectionStringProvider()) | ||
| .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request => | ||
| request.ForPath("/_floci/health").ForPort(FlociAzPort))); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override FlociAzBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new FlociAzConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override FlociAzBuilder Clone(IContainerConfiguration resourceConfiguration) | ||
| { | ||
| return Merge(DockerResourceConfiguration, new FlociAzConfiguration(resourceConfiguration)); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override FlociAzBuilder Merge(FlociAzConfiguration oldValue, FlociAzConfiguration newValue) | ||
| { | ||
| return new FlociAzBuilder(new FlociAzConfiguration(oldValue, newValue)); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| namespace Testcontainers.FlociAz; | ||
|
|
||
| /// <inheritdoc cref="ContainerConfiguration" /> | ||
| [PublicAPI] | ||
| public sealed class FlociAzConfiguration : ContainerConfiguration | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzConfiguration" /> class. | ||
| /// </summary> | ||
| public FlociAzConfiguration() | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The Docker resource configuration.</param> | ||
| public FlociAzConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) | ||
| : base(resourceConfiguration) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="resourceConfiguration">The container configuration.</param> | ||
| public FlociAzConfiguration(IContainerConfiguration resourceConfiguration) | ||
| : base(resourceConfiguration) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FlociAzConfiguration" /> class. | ||
| /// </summary> | ||
| /// <param name="oldValue">The old FlociAz configuration.</param> | ||
| /// <param name="newValue">The new FlociAz configuration.</param> | ||
| public FlociAzConfiguration(FlociAzConfiguration oldValue, FlociAzConfiguration newValue) | ||
| : base(oldValue, newValue) | ||
| { | ||
| } | ||
| } |
13 changes: 13 additions & 0 deletions
13
src/Testcontainers.FlociAz/FlociAzConnectionStringProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| namespace Testcontainers.FlociAz; | ||
|
|
||
| /// <summary> | ||
| /// Provides the FlociAz connection string. | ||
| /// </summary> | ||
| internal sealed class FlociAzConnectionStringProvider : ContainerConnectionStringProvider<FlociAzContainer, FlociAzConfiguration> | ||
| { | ||
| /// <inheritdoc /> | ||
| protected override string GetHostConnectionString() | ||
| { | ||
| return Container.GetConnectionString(); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.