Skip to content
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

chore: Add WithAcceptLicenseAgreement(bool) to container builder #1370

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions src/Testcontainers.Db2/Db2Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ public sealed class Db2Builder : ContainerBuilder<Db2Builder, Db2Container, Db2C

public const string DefaultPassword = "db2inst1";

private const string AcceptLicenseAgreementEnvVar = "LICENSE";

private const string AcceptLicenseAgreement = "accept";

private const string DeclineLicenseAgreement = "decline";

/// <summary>
/// Initializes a new instance of the <see cref="Db2Builder" /> class.
/// </summary>
Expand All @@ -42,6 +36,15 @@ private Db2Builder(Db2Configuration resourceConfiguration)
/// <inheritdoc />
protected override Db2Configuration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "LICENSE";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "accept";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "decline";

/// <summary>
/// Accepts the license agreement.
/// </summary>
Expand All @@ -50,7 +53,7 @@ private Db2Builder(Db2Configuration resourceConfiguration)
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the Db2 license agreement is accepted.</param>
/// <returns>A configured instance of <see cref="Db2Builder" />.</returns>
public Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
public override Db2Builder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement;
return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement);
Expand Down Expand Up @@ -94,6 +97,7 @@ public Db2Builder WithPassword(string password)
public override Db2Container Build()
{
Validate();
ValidateLicenseAgreement();
return new Db2Container(DockerResourceConfiguration);
}

Expand All @@ -110,16 +114,8 @@ protected override Db2Builder Init() => base.Init()
/// <inheritdoc />
protected override void Validate()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

base.Validate();

Predicate<Db2Configuration> licenseAgreementNotAccepted = value =>
!value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal);

_ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image))
.ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name));

_ = Guard.Argument(DockerResourceConfiguration.Username, nameof(DockerResourceConfiguration.Username))
.NotNull()
.NotEmpty();
Expand Down
15 changes: 9 additions & 6 deletions src/Testcontainers.Neo4j/Neo4jBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ public sealed class Neo4jBuilder : ContainerBuilder<Neo4jBuilder, Neo4jContainer

public const ushort Neo4jBoltPort = 7687;

private const string AcceptLicenseAgreementEnvVar = "NEO4J_ACCEPT_LICENSE_AGREEMENT";

private const string AcceptLicenseAgreement = "yes";

private const string DeclineLicenseAgreement = "no";

/// <summary>
/// Initializes a new instance of the <see cref="Neo4jBuilder" /> class.
/// </summary>
Expand All @@ -38,6 +32,15 @@ private Neo4jBuilder(Neo4jConfiguration resourceConfiguration)
/// <inheritdoc />
protected override Neo4jConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "NEO4J_ACCEPT_LICENSE_AGREEMENT";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "yes";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "no";

/// <summary>
/// Sets the image to the Neo4j Enterprise Edition.
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers.Pulsar/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
global using System.Threading;
global using System.Threading.Tasks;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
Expand Down
32 changes: 11 additions & 21 deletions src/Testcontainers.ServiceBus/ServiceBusBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ public sealed class ServiceBusBuilder : ContainerBuilder<ServiceBusBuilder, Serv

public const ushort ServiceBusPort = 5672;

private const string AcceptLicenseAgreementEnvVar = "ACCEPT_EULA";

private const string AcceptLicenseAgreement = "Y";

private const string DeclineLicenseAgreement = "N";

/// <summary>
/// Initializes a new instance of the <see cref="ServiceBusBuilder" /> class.
/// </summary>
Expand All @@ -40,6 +34,15 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration)
/// <inheritdoc />
protected override ServiceBusConfiguration DockerResourceConfiguration { get; }

/// <inheritdoc />
protected override string AcceptLicenseAgreementEnvVar { get; } = "ACCEPT_EULA";

/// <inheritdoc />
protected override string AcceptLicenseAgreement { get; } = "Y";

/// <inheritdoc />
protected override string DeclineLicenseAgreement { get; } = "N";

/// <summary>
/// Accepts the license agreement.
/// </summary>
Expand All @@ -48,7 +51,7 @@ private ServiceBusBuilder(ServiceBusConfiguration resourceConfiguration)
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the Azure Service Bus Emulator license agreement is accepted.</param>
/// <returns>A configured instance of <see cref="ServiceBusBuilder" />.</returns>
public ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
public override ServiceBusBuilder WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
var licenseAgreement = acceptLicenseAgreement ? AcceptLicenseAgreement : DeclineLicenseAgreement;
return WithEnvironment(AcceptLicenseAgreementEnvVar, licenseAgreement);
Expand Down Expand Up @@ -85,6 +88,7 @@ public ServiceBusBuilder WithMsSqlContainer(
public override ServiceBusContainer Build()
{
Validate();
ValidateLicenseAgreement();

if (DockerResourceConfiguration.DatabaseContainer != null)
{
Expand All @@ -105,20 +109,6 @@ public override ServiceBusContainer Build()
return new ServiceBusContainer(serviceBusContainer.DockerResourceConfiguration);
}

/// <inheritdoc />
protected override void Validate()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

base.Validate();

Predicate<ServiceBusConfiguration> licenseAgreementNotAccepted = value =>
!value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal);

_ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image))
.ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name));
}

/// <inheritdoc />
protected override ServiceBusBuilder Init()
{
Expand Down
1 change: 0 additions & 1 deletion src/Testcontainers.ServiceBus/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
global using System.Threading;
global using System.Threading.Tasks;
global using Docker.DotNet.Models;
global using DotNet.Testcontainers;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
Expand Down
4 changes: 2 additions & 2 deletions src/Testcontainers/Builders/Base64Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ public Base64Provider(JsonElement jsonElement, ILogger logger)
}

/// <summary>
/// Gets a predicate that determines whether or not a <see cref="JsonProperty" /> contains a Docker registry key.
/// Gets a predicate that determines whether a <see cref="JsonProperty" /> contains a Docker registry key.
/// </summary>
public static Func<JsonProperty, string, bool> HasDockerRegistryKey { get; }
= (property, hostname) => property.Name.Equals(hostname, StringComparison.OrdinalIgnoreCase) || property.Name.EndsWith("://" + hostname, StringComparison.OrdinalIgnoreCase);

/// <inheritdoc />
public bool IsApplicable(string hostname)
{
return !default(JsonElement).Equals(_rootElement) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryKey(property, hostname));
return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => HasDockerRegistryKey(property, hostname));
}

/// <inheritdoc />
Expand Down
39 changes: 38 additions & 1 deletion src/Testcontainers/Builders/ContainerBuilder`3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,28 @@ protected ContainerBuilder(TConfigurationEntity dockerResourceConfiguration)
{
}

/// <summary>
/// Gets the name of the environment variable that must be set to accept the image license agreement.
/// </summary>
protected virtual string AcceptLicenseAgreementEnvVar { get; }

/// <summary>
/// Gets the expected value of <see cref="AcceptLicenseAgreementEnvVar" /> that indicates acceptance of the license agreement.
/// </summary>
protected virtual string AcceptLicenseAgreement { get; }

/// <summary>
/// Gets the expected value of <see cref="AcceptLicenseAgreementEnvVar" /> that indicates rejection of the license agreement.
/// </summary>
protected virtual string DeclineLicenseAgreement { get; }

/// <inheritdoc />
public virtual TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement)
{
const string licenseAgreementNotRequired = "The module does not require you to accept a license agreement.";
throw new InvalidOperationException(licenseAgreementNotRequired);
}

/// <inheritdoc />
public TBuilderEntity DependsOn(IContainer container)
{
Expand Down Expand Up @@ -374,7 +396,7 @@ public TBuilderEntity WithStartupCallback(Func<TContainerEntity, CancellationTok
/// <inheritdoc />
protected override TBuilderEntity Init()
{
return base.Init().WithImagePullPolicy(PullPolicy.Missing).WithPortForwarding().WithOutputConsumer(Consume.DoNotConsumeStdoutAndStderr()).WithWaitStrategy(Wait.ForUnixContainer()).WithStartupCallback((_, ct) => Task.CompletedTask);
return base.Init().WithImagePullPolicy(PullPolicy.Missing).WithPortForwarding().WithOutputConsumer(Consume.DoNotConsumeStdoutAndStderr()).WithWaitStrategy(Wait.ForUnixContainer()).WithStartupCallback((_, _) => Task.CompletedTask);
}

/// <inheritdoc />
Expand All @@ -390,6 +412,21 @@ protected override void Validate()
.NotNull();
}

/// <summary>
/// Validates the license agreement.
/// </summary>
/// <exception cref="ArgumentException">Thrown when the license agreement is not accepted.</exception>
protected virtual void ValidateLicenseAgreement()
{
const string message = "The image '{0}' requires you to accept a license agreement.";

Predicate<TConfigurationEntity> licenseAgreementNotAccepted = value =>
!value.Environments.TryGetValue(AcceptLicenseAgreementEnvVar, out var licenseAgreementValue) || !AcceptLicenseAgreement.Equals(licenseAgreementValue, StringComparison.Ordinal);

_ = Guard.Argument(DockerResourceConfiguration, nameof(DockerResourceConfiguration.Image))
.ThrowIf(argument => licenseAgreementNotAccepted(argument.Value), argument => throw new ArgumentException(string.Format(message, DockerResourceConfiguration.Image.FullName), argument.Name));
}

/// <summary>
/// Clones the Docker resource builder configuration.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers/Builders/CredsHelperProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public CredsHelperProvider(JsonElement jsonElement, ILogger logger)
/// <inheritdoc />
public bool IsApplicable(string hostname)
{
return !default(JsonElement).Equals(_rootElement) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryKey(property, hostname));
return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !JsonValueKind.Null.Equals(_rootElement.ValueKind) && _rootElement.EnumerateObject().Any(property => Base64Provider.HasDockerRegistryKey(property, hostname));
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers/Builders/CredsStoreProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CredsStoreProvider(JsonElement jsonElement, ILogger logger)
/// <inheritdoc />
public bool IsApplicable(string hostname)
{
return !default(JsonElement).Equals(_rootElement) && !string.IsNullOrEmpty(_rootElement.GetString());
return !JsonValueKind.Undefined.Equals(_rootElement.ValueKind) && !string.IsNullOrEmpty(_rootElement.GetString());
}

/// <inheritdoc />
Expand Down
12 changes: 12 additions & 0 deletions src/Testcontainers/Builders/IContainerBuilder`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ namespace DotNet.Testcontainers.Builders
[PublicAPI]
public interface IContainerBuilder<out TBuilderEntity, out TContainerEntity> : IAbstractBuilder<TBuilderEntity, TContainerEntity, CreateContainerParameters>
{
/// <summary>
/// Accepts the license agreement.
/// </summary>
/// <remarks>
/// Modules that require a license agreement must override and implement this
/// method to enforce proper license acceptance behavior.
/// </remarks>
/// <param name="acceptLicenseAgreement">A boolean value indicating whether the license agreement is accepted.</param>
/// <returns>A configured instance of <typeparamref name="TBuilderEntity" />.</returns>
/// <exception cref="InvalidOperationException">Thrown when the module does not require a license agreement.</exception>
TBuilderEntity WithAcceptLicenseAgreement(bool acceptLicenseAgreement);

/// <summary>
/// Sets the dependent container to resolve and start before starting this container configuration.
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions tests/Testcontainers.Db2.Tests/DeclineLicenseAgreementTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Testcontainers.Db2;

public sealed partial class DeclineLicenseAgreementTest
{
[GeneratedRegex("The image '.+' requires you to accept a license agreement\\.")]
private static partial Regex LicenseAgreementNotAccepted();

[Fact]
public void WithoutAcceptingLicenseAgreementThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new Db2Builder().Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}

[Fact]
public void WithLicenseAgreementDeclinedThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new Db2Builder().WithAcceptLicenseAgreement(false).Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}
}
2 changes: 2 additions & 0 deletions tests/Testcontainers.Db2.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
global using System;
global using System.Data;
global using System.Text.RegularExpressions;
global using System.Data.Common;
global using System.Threading.Tasks;
global using DotNet.Testcontainers.Commons;
Expand Down
3 changes: 0 additions & 3 deletions tests/Testcontainers.Kafka.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
global using System;
global using System.Collections.Generic;
global using System.Diagnostics;
global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
global using Confluent.Kafka;
global using Confluent.Kafka.SyncOverAsync;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Testcontainers.Tests;

public sealed class AcceptLicenseAgreementTest
{
[Fact]
public void WithLicenseAgreementAcceptedThrowsArgumentException()
{
var exception = Assert.Throws<InvalidOperationException>(() => new ContainerBuilder().WithAcceptLicenseAgreement(true).Build());
Assert.Equal("The module does not require you to accept a license agreement.", exception.Message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Testcontainers.ServiceBus;

public sealed partial class DeclineLicenseAgreementTest
{
[GeneratedRegex("The image '.+' requires you to accept a license agreement\\.")]
private static partial Regex LicenseAgreementNotAccepted();

[Fact]
public void WithoutAcceptingLicenseAgreementThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new ServiceBusBuilder().Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}

[Fact]
public void WithLicenseAgreementDeclinedThrowsArgumentException()
{
var exception = Assert.Throws<ArgumentException>(() => new ServiceBusBuilder().WithAcceptLicenseAgreement(false).Build());
Assert.Matches(LicenseAgreementNotAccepted(), exception.Message);
}
}
2 changes: 2 additions & 0 deletions tests/Testcontainers.ServiceBus.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
global using System;
global using System.Text.RegularExpressions;
global using System.Threading.Tasks;
global using Azure.Messaging.ServiceBus;
global using DotNet.Testcontainers.Builders;
Expand Down