@@ -315,6 +315,12 @@ impl CockroachStarter {
315
315
self . temp_dir . path ( )
316
316
}
317
317
318
+ /// Returns the path to the listen-url file for this execution
319
+ #[ cfg( test) ]
320
+ pub fn listen_url_file ( & self ) -> & Path {
321
+ & self . listen_url_file
322
+ }
323
+
318
324
/// Returns the path to the storage directory created for this execution.
319
325
pub fn store_dir ( & self ) -> & Path {
320
326
self . store_dir . as_path ( )
@@ -367,6 +373,8 @@ impl CockroachStarter {
367
373
// memory.
368
374
match tokio:: fs:: read_to_string ( & listen_url_file) . await {
369
375
Ok ( listen_url) if listen_url. contains ( '\n' ) => {
376
+ // The file is fully written.
377
+ // We're ready to move on.
370
378
let listen_url = listen_url. trim_end ( ) ;
371
379
make_pg_config ( listen_url) . map_err ( |source| {
372
380
poll:: CondCheckError :: Failed (
@@ -378,7 +386,31 @@ impl CockroachStarter {
378
386
} )
379
387
}
380
388
381
- _ => Err ( poll:: CondCheckError :: NotYet ) ,
389
+ Ok ( _) => {
390
+ // The file hasn't been fully written yet.
391
+ // Keep waiting.
392
+ Err ( poll:: CondCheckError :: NotYet )
393
+ }
394
+
395
+ Err ( error)
396
+ if error. kind ( ) == std:: io:: ErrorKind :: NotFound =>
397
+ {
398
+ // The file doesn't exist yet.
399
+ // Keep waiting.
400
+ Err ( poll:: CondCheckError :: NotYet )
401
+ }
402
+
403
+ Err ( error) => {
404
+ // Something else has gone wrong. Stop immediately
405
+ // and report the problem.
406
+ let source = anyhow ! ( error) . context ( format ! (
407
+ "checking listen file {:?}" ,
408
+ listen_url_file
409
+ ) ) ;
410
+ Err ( poll:: CondCheckError :: Failed (
411
+ CockroachStartError :: Unknown { source } ,
412
+ ) )
413
+ }
382
414
}
383
415
}
384
416
} ,
@@ -996,7 +1028,7 @@ mod test {
996
1028
#[ tokio:: test]
997
1029
async fn test_bad_cmd ( ) {
998
1030
let builder = CockroachStarterBuilder :: new_with_cmd ( "/nonexistent" ) ;
999
- let _ = test_database_start_failure ( builder) . await ;
1031
+ let _ = test_database_start_failure ( builder. build ( ) . unwrap ( ) ) . await ;
1000
1032
}
1001
1033
1002
1034
// Tests what happens if the "cockroach" command exits before writing the
@@ -1006,7 +1038,8 @@ mod test {
1006
1038
async fn test_cmd_fails ( ) {
1007
1039
let mut builder = new_builder ( ) ;
1008
1040
builder. arg ( "not-a-valid-argument" ) ;
1009
- let temp_dir = test_database_start_failure ( builder) . await ;
1041
+ let ( temp_dir, _) =
1042
+ test_database_start_failure ( builder. build ( ) . unwrap ( ) ) . await ;
1010
1043
fs:: metadata ( & temp_dir) . await . expect ( "temporary directory was deleted" ) ;
1011
1044
// The temporary directory is preserved in this case so that we can
1012
1045
// debug the failure. In this case, we injected the failure. Remove
@@ -1032,9 +1065,8 @@ mod test {
1032
1065
// caller can decide whether to check if it was cleaned up or not. The
1033
1066
// expected behavior depends on the failure mode.
1034
1067
async fn test_database_start_failure (
1035
- builder : CockroachStarterBuilder ,
1036
- ) -> PathBuf {
1037
- let starter = builder. build ( ) . unwrap ( ) ;
1068
+ starter : CockroachStarter ,
1069
+ ) -> ( PathBuf , CockroachStartError ) {
1038
1070
let temp_dir = starter. temp_dir ( ) . to_owned ( ) ;
1039
1071
eprintln ! ( "will run: {}" , starter. cmdline( ) ) ;
1040
1072
eprintln ! ( "environment:" ) ;
@@ -1044,7 +1076,7 @@ mod test {
1044
1076
let error =
1045
1077
starter. start ( ) . await . expect_err ( "unexpectedly started database" ) ;
1046
1078
eprintln ! ( "error: {:?}" , error) ;
1047
- temp_dir
1079
+ ( temp_dir, error )
1048
1080
}
1049
1081
1050
1082
// Tests when CockroachDB hangs on startup by setting the start timeout
@@ -1132,6 +1164,49 @@ mod test {
1132
1164
} ) ;
1133
1165
}
1134
1166
1167
+ // Test what happens if we can't read the listen-url file. This is a little
1168
+ // obscure, but it has been a problem.
1169
+ #[ tokio:: test]
1170
+ async fn test_setup_database_bad_listen_url ( ) {
1171
+ // We don't need to actually run Cockroach for this test, and it's
1172
+ // simpler (and faster) if we don't. But we do need something that
1173
+ // won't exit before we get a chance to trigger an error and that can
1174
+ // also accept the extra arguments that the builder will provide.
1175
+ let mut builder = CockroachStarterBuilder :: new_with_cmd ( "bash" ) ;
1176
+ builder. arg ( "-c" ) . arg ( "sleep 60" ) ;
1177
+ let starter = builder. build ( ) . unwrap ( ) ;
1178
+
1179
+ // We want to inject an error into the code path that reads the
1180
+ // listen-url file. We do this by precreating that path as a directory.
1181
+ // Then we'll get EISDIR when we try to read it.
1182
+ let listen_url_file = starter. listen_url_file ( ) . to_owned ( ) ;
1183
+ std:: fs:: create_dir ( & listen_url_file)
1184
+ . expect ( "pre-creating listen-URL path as directory" ) ;
1185
+ let ( temp_dir, error) = test_database_start_failure ( starter) . await ;
1186
+
1187
+ if let CockroachStartError :: Unknown { source } = error {
1188
+ let message = format ! ( "{:#}" , source) ;
1189
+ eprintln ! ( "error message was: {}" , message) ;
1190
+ // Verify the error message refers to the listening file (since
1191
+ // that's what we were operating on) and also reflects the EISDIR
1192
+ // error.
1193
+ assert ! ( message. starts_with( "checking listen file \" " ) ) ;
1194
+ assert ! ( message. contains( "Is a directory" ) ) ;
1195
+ } else {
1196
+ panic ! ( "unexpected error trying to start database: {:#}" , error) ;
1197
+ }
1198
+
1199
+ // Clean up the temporary directory -- carefully. Since we know exactly
1200
+ // what should be in it, we opt to remove these items individually
1201
+ // rather than risk blowing away something else inadvertently.
1202
+ fs:: remove_dir ( & listen_url_file)
1203
+ . await
1204
+ . expect ( "failed to remove listen-url directory" ) ;
1205
+ fs:: remove_dir ( temp_dir)
1206
+ . await
1207
+ . expect ( "failed to remove temporary directory" ) ;
1208
+ }
1209
+
1135
1210
// Test the happy path using the default store directory.
1136
1211
#[ tokio:: test]
1137
1212
async fn test_setup_database_default_dir ( ) {
0 commit comments