@@ -14,6 +14,7 @@ namespace Aspire.Hosting;
14
14
public static class MySqlBuilderExtensions
15
15
{
16
16
private const string PasswordEnvVarName = "MYSQL_ROOT_PASSWORD" ;
17
+ private const UnixFileMode FileMode644 = UnixFileMode . UserRead | UnixFileMode . UserWrite | UnixFileMode . GroupRead | UnixFileMode . OtherRead ;
17
18
18
19
/// <summary>
19
20
/// Adds a MySQL server resource to the application model. For local development a container is used.
@@ -102,14 +103,11 @@ public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> bui
102
103
103
104
containerName ??= $ "{ builder . Resource . Name } -phpmyadmin";
104
105
105
- var configurationTempFileName = Path . GetTempFileName ( ) ;
106
-
107
106
var phpMyAdminContainer = new PhpMyAdminContainerResource ( containerName ) ;
108
107
var phpMyAdminContainerBuilder = builder . ApplicationBuilder . AddResource ( phpMyAdminContainer )
109
108
. WithImage ( MySqlContainerImageTags . PhpMyAdminImage , MySqlContainerImageTags . PhpMyAdminTag )
110
109
. WithImageRegistry ( MySqlContainerImageTags . Registry )
111
110
. WithHttpEndpoint ( targetPort : 80 , name : "http" )
112
- . WithBindMount ( configurationTempFileName , "/etc/phpmyadmin/config.user.inc.php" )
113
111
. ExcludeFromManifest ( ) ;
114
112
115
113
builder . ApplicationBuilder . Eventing . Subscribe < AfterEndpointsAllocatedEvent > ( ( e , ct ) =>
@@ -140,32 +138,33 @@ public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> bui
140
138
}
141
139
else
142
140
{
143
- using var stream = new FileStream ( configurationTempFileName , FileMode . Create ) ;
144
- using var writer = new StreamWriter ( stream ) ;
141
+ var tempConfigFile = WritePhpMyAdminConfiguration ( mySqlInstances ) ;
145
142
146
- writer . WriteLine ( "<?php" ) ;
147
- writer . WriteLine ( ) ;
148
- writer . WriteLine ( "$i = 0;" ) ;
149
- writer . WriteLine ( ) ;
150
- foreach ( var mySqlInstance in mySqlInstances )
143
+ try
151
144
{
152
- if ( mySqlInstance . PrimaryEndpoint . IsAllocated )
145
+ var aspireStore = e . Services . GetRequiredService < IAspireStore > ( ) ;
146
+
147
+ // Deterministic file path for the configuration file based on its content
148
+ var configStoreFilename = aspireStore . GetFileNameWithContent ( $ "{ builder . Resource . Name } -config.user.inc.php", tempConfigFile ) ;
149
+
150
+ // Need to grant read access to the config file on unix like systems.
151
+ if ( ! OperatingSystem . IsWindows ( ) )
152
+ {
153
+ File . SetUnixFileMode ( configStoreFilename , FileMode644 ) ;
154
+ }
155
+
156
+ phpMyAdminContainerBuilder . WithBindMount ( configStoreFilename , "/etc/phpmyadmin/config.user.inc.php" ) ;
157
+ }
158
+ finally
159
+ {
160
+ try
161
+ {
162
+ File . Delete ( tempConfigFile ) ;
163
+ }
164
+ catch
153
165
{
154
- var endpoint = mySqlInstance . PrimaryEndpoint ;
155
- writer . WriteLine ( "$i++;" ) ;
156
- // PhpMyAdmin assumes MySql is being accessed over a default Aspire container network and hardcodes the resource address
157
- // This will need to be refactored once updated service discovery APIs are available
158
- writer . WriteLine ( $ "$cfg['Servers'][$i]['host'] = '{ endpoint . Resource . Name } :{ endpoint . TargetPort } ';") ;
159
- writer . WriteLine ( $ "$cfg['Servers'][$i]['verbose'] = '{ mySqlInstance . Name } ';") ;
160
- writer . WriteLine ( $ "$cfg['Servers'][$i]['auth_type'] = 'cookie';") ;
161
- writer . WriteLine ( $ "$cfg['Servers'][$i]['user'] = 'root';") ;
162
- writer . WriteLine ( $ "$cfg['Servers'][$i]['password'] = '{ mySqlInstance . PasswordParameter . Value } ';") ;
163
- writer . WriteLine ( $ "$cfg['Servers'][$i]['AllowNoPassword'] = true;") ;
164
- writer . WriteLine ( ) ;
165
166
}
166
167
}
167
- writer . WriteLine ( "$cfg['DefaultServer'] = 1;" ) ;
168
- writer . WriteLine ( "?>" ) ;
169
168
}
170
169
171
170
return Task . CompletedTask ;
@@ -235,4 +234,38 @@ public static IResourceBuilder<MySqlServerResource> WithInitBindMount(this IReso
235
234
236
235
return builder . WithBindMount ( source , "/docker-entrypoint-initdb.d" , isReadOnly ) ;
237
236
}
237
+
238
+ private static string WritePhpMyAdminConfiguration ( IEnumerable < MySqlServerResource > mySqlInstances )
239
+ {
240
+ // This temporary file is not used by the container, it will be copied and then deleted
241
+ var filePath = Path . GetTempFileName ( ) ;
242
+
243
+ using var writer = new StreamWriter ( filePath ) ;
244
+
245
+ writer . WriteLine ( "<?php" ) ;
246
+ writer . WriteLine ( ) ;
247
+ writer . WriteLine ( "$i = 0;" ) ;
248
+ writer . WriteLine ( ) ;
249
+ foreach ( var mySqlInstance in mySqlInstances )
250
+ {
251
+ if ( mySqlInstance . PrimaryEndpoint . IsAllocated )
252
+ {
253
+ var endpoint = mySqlInstance . PrimaryEndpoint ;
254
+ writer . WriteLine ( "$i++;" ) ;
255
+ // PhpMyAdmin assumes MySql is being accessed over a default Aspire container network and hardcodes the resource address
256
+ // This will need to be refactored once updated service discovery APIs are available
257
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['host'] = '{ endpoint . Resource . Name } :{ endpoint . TargetPort } ';") ;
258
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['verbose'] = '{ mySqlInstance . Name } ';") ;
259
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['auth_type'] = 'cookie';") ;
260
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['user'] = 'root';") ;
261
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['password'] = '{ mySqlInstance . PasswordParameter . Value } ';") ;
262
+ writer . WriteLine ( $ "$cfg['Servers'][$i]['AllowNoPassword'] = true;") ;
263
+ writer . WriteLine ( ) ;
264
+ }
265
+ }
266
+ writer . WriteLine ( "$cfg['DefaultServer'] = 1;" ) ;
267
+ writer . WriteLine ( "?>" ) ;
268
+
269
+ return filePath ;
270
+ }
238
271
}
0 commit comments