31
31
import org .junit .jupiter .api .Assertions ;
32
32
import org .junit .jupiter .api .BeforeAll ;
33
33
import org .junit .jupiter .api .TestTemplate ;
34
+ import org .testcontainers .containers .Container ;
34
35
import org .testcontainers .containers .GenericContainer ;
35
36
import org .testcontainers .containers .output .Slf4jLogConsumer ;
37
+ import org .testcontainers .containers .wait .strategy .Wait ;
36
38
import org .testcontainers .lifecycle .Startables ;
37
39
import org .testcontainers .shaded .com .github .dockerjava .core .command .ExecStartResultCallback ;
38
40
42
44
43
45
import java .io .ByteArrayOutputStream ;
44
46
import java .io .IOException ;
47
+ import java .io .StringReader ;
45
48
import java .nio .charset .StandardCharsets ;
46
49
import java .util .ArrayList ;
47
50
import java .util .Collections ;
48
51
import java .util .List ;
52
+ import java .util .Properties ;
53
+ import java .util .function .BiFunction ;
54
+ import java .util .stream .Collectors ;
55
+ import java .util .stream .IntStream ;
49
56
import java .util .stream .Stream ;
50
57
51
58
@ DisabledOnContainer (
@@ -68,14 +75,30 @@ public class FtpFileIT extends TestSuiteBase implements TestResource {
68
75
69
76
private GenericContainer <?> ftpContainer ;
70
77
78
+ private String ftpPassiveAddress ;
79
+
80
+ private BiFunction <Integer , Integer , Integer []> generateExposedPorts =
81
+ (startPort , endPort ) ->
82
+ IntStream .rangeClosed (startPort , endPort ).boxed ().toArray (Integer []::new );
83
+
84
+ private BiFunction <Integer , Integer , List <String >> generatePortBindings =
85
+ (startPort , endPort ) ->
86
+ IntStream .rangeClosed (startPort , endPort )
87
+ .mapToObj (i -> i + ":" + i )
88
+ .collect (Collectors .toList ());
89
+
71
90
@ BeforeAll
72
91
@ Override
73
92
public void startUp () throws Exception {
93
+ int passiveStartPort = 30000 ;
94
+ int passiveEndPort = 30004 ;
74
95
ftpContainer =
75
96
new GenericContainer <>(FTP_IMAGE )
76
97
.withExposedPorts (FTP_PORT )
77
98
.withNetwork (NETWORK )
78
99
.withExposedPorts (FTP_PORT )
100
+ .withExposedPorts (
101
+ generateExposedPorts .apply (passiveStartPort , passiveEndPort ))
79
102
.withNetworkAliases (ftp_CONTAINER_HOST )
80
103
.withEnv ("FILE_OPEN_MODE" , "0666" )
81
104
.withEnv ("WRITE_ENABLE" , "YES" )
@@ -85,13 +108,31 @@ public void startUp() throws Exception {
85
108
.withEnv ("LOCAL_UMASK" , "000" )
86
109
.withEnv ("FTP_USER" , USERNAME )
87
110
.withEnv ("FTP_PASS" , PASSWORD )
88
- .withEnv ("PASV_ADDRESS" , "0.0.0.0" )
111
+ .withEnv ("PASV_MIN_PORT" , String .valueOf (passiveStartPort ))
112
+ .withEnv ("PASV_MAX_PORT" , String .valueOf (passiveEndPort ))
89
113
.withLogConsumer (new Slf4jLogConsumer (log ))
114
+ // Modify the strategy mode because the passive mode port does not need to
115
+ // be checked here, it does not start with the FTP startup.
116
+ .waitingFor (Wait .forLogMessage (".*" , 1 ))
90
117
.withPrivilegedMode (true );
91
118
92
- ftpContainer .setPortBindings (Collections .singletonList ("21:21" ));
119
+ List <String > portBind = new ArrayList <>();
120
+ portBind .add ("21:21" );
121
+ portBind .addAll (generatePortBindings .apply (passiveStartPort , passiveEndPort ));
122
+
123
+ ftpContainer .setPortBindings (portBind );
93
124
ftpContainer .start ();
94
125
Startables .deepStart (Stream .of (ftpContainer )).join ();
126
+
127
+ // Get the passive mode address of the FTP container
128
+ Properties properties = new Properties ();
129
+ properties .load (
130
+ new StringReader (
131
+ ftpContainer
132
+ .execInContainer ("sh" , "-c" , "cat /etc/vsftpd/vsftpd.conf" )
133
+ .getStdout ()));
134
+ ftpPassiveAddress = properties .getProperty ("pasv_address" );
135
+
95
136
log .info ("ftp container started" );
96
137
97
138
ContainerUtil .copyFileIntoContainers (
@@ -126,6 +167,33 @@ public void startUp() throws Exception {
126
167
ftpContainer .execInContainer ("sh" , "-c" , "chown -R ftp:ftp /home/vsftpd/seatunnel/" );
127
168
}
128
169
170
+ @ TestTemplate
171
+ public void testFtpFileReadAndWriteForPassive (TestContainer container )
172
+ throws IOException , InterruptedException {
173
+ List <String > configParams = Collections .singletonList ("ftpHost=" + ftpPassiveAddress );
174
+ // Test passive mode
175
+ assertJobExecution (
176
+ container , "/text/ftp_file_text_to_assert_for_passive.conf" , configParams );
177
+ assertJobExecution (container , "/text/fake_to_ftp_file_text_for_passive.conf" , configParams );
178
+
179
+ String homePath = "/home/vsftpd/seatunnel/tmp/seatunnel/passive_text" ;
180
+ // test write ftp text file
181
+ Assertions .assertEquals (1 , getFileListFromContainer (homePath ).size ());
182
+
183
+ // Confirm data is written correctly
184
+ Container .ExecResult execResult =
185
+ ftpContainer .execInContainer ("sh" , "-c" , "awk 'END {print NR}' " + homePath + "/*" );
186
+ Assertions .assertEquals ("15" , execResult .getStdout ().trim ());
187
+
188
+ deleteFileFromContainer (homePath );
189
+ }
190
+
191
+ private void assertJobExecution (TestContainer container , String configPath , List <String > params )
192
+ throws IOException , InterruptedException {
193
+ Container .ExecResult execResult = container .executeJob (configPath , params );
194
+ Assertions .assertEquals (0 , execResult .getExitCode (), execResult .getStderr ());
195
+ }
196
+
129
197
@ TestTemplate
130
198
public void testFtpFileReadAndWrite (TestContainer container )
131
199
throws IOException , InterruptedException {
0 commit comments