|
| 1 | +package org.testcontainers.azure; |
| 2 | + |
| 3 | +import org.testcontainers.containers.BindMode; |
| 4 | +import org.testcontainers.containers.GenericContainer; |
| 5 | +import org.testcontainers.utility.DockerImageName; |
| 6 | + |
| 7 | +import java.io.File; |
| 8 | + |
| 9 | +/** |
| 10 | + * Testcontainers implementation for Azurite Emulator. |
| 11 | + * <p> |
| 12 | + * Supported image: {@code mcr.microsoft.com/azure-storage/azurite} |
| 13 | + * <p> |
| 14 | + * Exposed ports: |
| 15 | + * <ul> |
| 16 | + * <li>10000 (blob port)</li> |
| 17 | + * <li>10001 (queue port)</li> |
| 18 | + * <li>10002 (table port)</li> |
| 19 | + * </ul> |
| 20 | + * <p> |
| 21 | + * See command line options <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio%2Cblob-storage#command-line-options">here</a>. |
| 22 | + */ |
| 23 | +public class AzuriteContainer extends GenericContainer<AzuriteContainer> { |
| 24 | + |
| 25 | + private static final String DEFAULT_HOST = "127.0.0.1"; |
| 26 | + |
| 27 | + private static final int DEFAULT_BLOB_PORT = 10000; |
| 28 | + |
| 29 | + private static final int DEFAULT_QUEUE_PORT = 10001; |
| 30 | + |
| 31 | + private static final int DEFAULT_TABLE_PORT = 10002; |
| 32 | + |
| 33 | + private static final String CONNECTION_STRING_FORMAT = |
| 34 | + "DefaultEndpointsProtocol=%s;AccountName=%s;AccountKey=%s;BlobEndpoint=%s://%s:%d/%s;QueueEndpoint=%s://%s:%d/%s;TableEndpoint=%s://%s:%d/%s;"; |
| 35 | + |
| 36 | + /** |
| 37 | + * The account name of the default credentials. |
| 38 | + */ |
| 39 | + public static final String WELL_KNOWN_ACCOUNT_NAME = "devstoreaccount1"; |
| 40 | + |
| 41 | + /** |
| 42 | + * The account key of the default credentials. |
| 43 | + */ |
| 44 | + public static final String WELL_KNOWN_ACCOUNT_KEY = |
| 45 | + "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; |
| 46 | + |
| 47 | + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( |
| 48 | + "mcr.microsoft.com/azure-storage/azurite" |
| 49 | + ); |
| 50 | + |
| 51 | + private String host = DEFAULT_HOST; |
| 52 | + |
| 53 | + private File cert = null; |
| 54 | + |
| 55 | + private String certExtension = null; |
| 56 | + |
| 57 | + private File key = null; |
| 58 | + |
| 59 | + private String pwd = null; |
| 60 | + |
| 61 | + /** |
| 62 | + * @param dockerImageName specified docker image name to run |
| 63 | + */ |
| 64 | + public AzuriteContainer(final DockerImageName dockerImageName) { |
| 65 | + super(dockerImageName); |
| 66 | + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * Configure SSL with a custom certificate and password. |
| 71 | + * |
| 72 | + * @param pfxCert The PFX certificate file |
| 73 | + * @param password The password securing the certificate |
| 74 | + * @return this |
| 75 | + */ |
| 76 | + public AzuriteContainer withSsl(final File pfxCert, final String password) { |
| 77 | + cert = pfxCert; |
| 78 | + pwd = password; |
| 79 | + certExtension = ".pfx"; |
| 80 | + return this; |
| 81 | + } |
| 82 | + |
| 83 | + /** |
| 84 | + * Configure SSL with a custom certificate and private key. |
| 85 | + * |
| 86 | + * @param pemCert The PEM certificate file |
| 87 | + * @param pemKey The PEM key file |
| 88 | + * @return this |
| 89 | + */ |
| 90 | + public AzuriteContainer withSsl(final File pemCert, final File pemKey) { |
| 91 | + cert = pemCert; |
| 92 | + key = pemKey; |
| 93 | + certExtension = ".pem"; |
| 94 | + return this; |
| 95 | + } |
| 96 | + |
| 97 | + /** |
| 98 | + * Sets the hostname we want to use to connect to our emulator. (default: {@link #DEFAULT_HOST}) |
| 99 | + * |
| 100 | + * @param host The host name |
| 101 | + * @return this |
| 102 | + */ |
| 103 | + public AzuriteContainer withHost(final String host) { |
| 104 | + this.host = host; |
| 105 | + return this; |
| 106 | + } |
| 107 | + |
| 108 | + @Override |
| 109 | + protected void configure() { |
| 110 | + withEnv("AZURITE_ACCOUNTS", WELL_KNOWN_ACCOUNT_NAME + ":" + WELL_KNOWN_ACCOUNT_KEY); |
| 111 | + withCommand(getCommandLine()); |
| 112 | + if (cert != null) { |
| 113 | + final String certAbsolutePath = cert.getAbsolutePath(); |
| 114 | + logger().info("Using path for cert file: '{}'", certAbsolutePath); |
| 115 | + withFileSystemBind(certAbsolutePath, "/cert" + certExtension, BindMode.READ_ONLY); |
| 116 | + if (key != null) { |
| 117 | + final String keyAbsolutePath = key.getAbsolutePath(); |
| 118 | + logger().info("Using path for key file: '{}'", keyAbsolutePath); |
| 119 | + withFileSystemBind(keyAbsolutePath, "/key.pem", BindMode.READ_ONLY); |
| 120 | + } |
| 121 | + } |
| 122 | + withExposedPorts(DEFAULT_BLOB_PORT, DEFAULT_QUEUE_PORT, DEFAULT_TABLE_PORT); |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Returns the connection string for the default credentials. |
| 127 | + * |
| 128 | + * @return connection string |
| 129 | + */ |
| 130 | + public String getDefaultConnectionString() { |
| 131 | + return getConnectionString(WELL_KNOWN_ACCOUNT_NAME, WELL_KNOWN_ACCOUNT_KEY); |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * Returns the connection string for the account name and key specified. |
| 136 | + * |
| 137 | + * @param accountName The name of the account |
| 138 | + * @param accountKey The account key |
| 139 | + * @return connection string |
| 140 | + */ |
| 141 | + public String getConnectionString(final String accountName, final String accountKey) { |
| 142 | + final String protocol = cert != null ? "https" : "http"; |
| 143 | + return String.format( |
| 144 | + CONNECTION_STRING_FORMAT, |
| 145 | + protocol, |
| 146 | + accountName, |
| 147 | + accountKey, |
| 148 | + protocol, |
| 149 | + host, |
| 150 | + getMappedPort(DEFAULT_BLOB_PORT), |
| 151 | + accountName, |
| 152 | + protocol, |
| 153 | + host, |
| 154 | + getMappedPort(DEFAULT_QUEUE_PORT), |
| 155 | + accountName, |
| 156 | + protocol, |
| 157 | + host, |
| 158 | + getMappedPort(DEFAULT_TABLE_PORT), |
| 159 | + accountName |
| 160 | + ); |
| 161 | + } |
| 162 | + |
| 163 | + String getCommandLine() { |
| 164 | + final StringBuilder args = new StringBuilder("azurite"); |
| 165 | + args.append(" --blobHost ").append(host).append(" --blobPort ").append(DEFAULT_BLOB_PORT); |
| 166 | + args.append(" --queueHost ").append(host).append(" --queuePort ").append(DEFAULT_QUEUE_PORT); |
| 167 | + args.append(" --tableHost ").append(host).append(" --tablePort ").append(DEFAULT_TABLE_PORT); |
| 168 | + args.append(" --location ").append("/data"); |
| 169 | + if (cert != null) { |
| 170 | + args.append(" --cert ").append("/cert").append(certExtension); |
| 171 | + if (pwd != null) { |
| 172 | + args.append(" --pwd ").append(pwd); |
| 173 | + } else { |
| 174 | + args.append(" --key ").append("/key.pem"); |
| 175 | + } |
| 176 | + } |
| 177 | + final String cmd = args.toString(); |
| 178 | + logger().debug("Using command line: '{}'", cmd); |
| 179 | + return cmd; |
| 180 | + } |
| 181 | +} |
0 commit comments