Skip to content

Commit 613de3b

Browse files
committed
Address Edge distribution review feedback
1 parent bebe888 commit 613de3b

10 files changed

Lines changed: 372 additions & 34 deletions

File tree

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#
19+
20+
$ErrorActionPreference = 'Stop'
21+
$repositoryRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
22+
$testRoot = Join-Path ([System.IO.Path]::GetTempPath()) ('iotdb-edge-windows-' + [guid]::NewGuid())
23+
New-Item -ItemType Directory -Path $testRoot | Out-Null
24+
$javaStub = Join-Path $testRoot 'java.exe'
25+
$script:caseCount = 0
26+
$portNames = @(
27+
'cn_internal_port', 'cn_consensus_port', 'dn_rpc_port', 'dn_internal_port',
28+
'dn_mpp_data_exchange_port', 'dn_schema_region_consensus_port', 'dn_data_region_consensus_port'
29+
)
30+
31+
function Get-FreePorts {
32+
$result = @{}
33+
$reservations = @()
34+
try {
35+
foreach ($name in $portNames) {
36+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
37+
$listener.Start()
38+
$reservations += $listener
39+
$result[$name] = $listener.LocalEndpoint.Port
40+
}
41+
return $result
42+
} finally {
43+
foreach ($listener in $reservations) {
44+
$listener.Stop()
45+
}
46+
}
47+
}
48+
49+
function Invoke-EdgeLauncher {
50+
param(
51+
[System.Collections.IDictionary]$Ports,
52+
[string]$Locale = '',
53+
[string[]]$ExtraLines = @(),
54+
[switch]$MissingConfig
55+
)
56+
57+
$script:caseCount++
58+
$caseDir = Join-Path $testRoot "case-$script:caseCount"
59+
$edgeHome = Join-Path $caseDir 'edge installation'
60+
$configDir = Join-Path $caseDir 'custom configuration'
61+
$launcherDir = Join-Path $edgeHome 'sbin/windows'
62+
$envDir = Join-Path $configDir 'windows'
63+
$javaHome = Join-Path $caseDir 'fake java'
64+
$javaBin = Join-Path $javaHome 'bin'
65+
New-Item -ItemType Directory -Path $launcherDir, $envDir, $javaBin -Force | Out-Null
66+
foreach ($file in @('start-edge.bat', 'check-edge.ps1')) {
67+
Copy-Item -LiteralPath (Join-Path $repositoryRoot "scripts/sbin/windows/$file") -Destination $launcherDir
68+
}
69+
Copy-Item -LiteralPath (Join-Path $repositoryRoot 'scripts/conf/windows/edge-env.bat') -Destination $envDir
70+
$common = Get-Content -LiteralPath (Join-Path $repositoryRoot 'scripts/conf/windows/iotdb-common.bat') -Raw
71+
Set-Content -LiteralPath (Join-Path $envDir 'iotdb-common.bat') -Value $common.Replace('@tsfile.locale.opt@', $Locale) -Encoding ASCII
72+
if (-not $MissingConfig) {
73+
$lines = @($ExtraLines)
74+
foreach ($key in $Ports.Keys) {
75+
$lines += " $key = $($Ports[$key]) "
76+
}
77+
Set-Content -LiteralPath (Join-Path $configDir 'iotdb-system.properties') -Value $lines -Encoding ASCII
78+
}
79+
80+
Copy-Item -LiteralPath $javaStub -Destination $javaBin
81+
$argsFile = Join-Path $caseDir 'java-arguments.txt'
82+
$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
83+
$startInfo.FileName = $env:ComSpec
84+
$startInfo.Arguments = '/d /c call "' + (Join-Path $launcherDir 'start-edge.bat') + '"'
85+
$startInfo.WorkingDirectory = $caseDir
86+
$startInfo.UseShellExecute = $false
87+
$startInfo.CreateNoWindow = $true
88+
$startInfo.RedirectStandardOutput = $true
89+
$startInfo.RedirectStandardError = $true
90+
$startInfo.RedirectStandardInput = $true
91+
$startInfo.EnvironmentVariables['IOTDB_HOME'] = $edgeHome
92+
$startInfo.EnvironmentVariables['IOTDB_CONF'] = $configDir
93+
$startInfo.EnvironmentVariables['JAVA_HOME'] = $javaHome
94+
$startInfo.EnvironmentVariables['EDGE_TEST_JAVA_ARGS'] = $argsFile
95+
$startInfo.EnvironmentVariables['IOTDB_JMX_OPTS'] = ''
96+
$startInfo.EnvironmentVariables['TSFILE_LOCALE_JVM_OPT'] = '-Dtsfile.locale=stale'
97+
$process = [System.Diagnostics.Process]::new()
98+
$process.StartInfo = $startInfo
99+
try {
100+
[void]$process.Start()
101+
$stdout = $process.StandardOutput.ReadToEndAsync()
102+
$stderr = $process.StandardError.ReadToEndAsync()
103+
$process.StandardInput.Close()
104+
if (-not $process.WaitForExit(30000)) {
105+
$process.Kill()
106+
throw 'Timed out running the Edge Windows launcher'
107+
}
108+
return [pscustomobject]@{
109+
ExitCode = $process.ExitCode
110+
Output = $stdout.Result + $stderr.Result
111+
JavaInvoked = Test-Path -LiteralPath $argsFile
112+
Arguments = if (Test-Path -LiteralPath $argsFile) { Get-Content -LiteralPath $argsFile -Raw } else { '' }
113+
}
114+
} finally {
115+
$process.Dispose()
116+
}
117+
}
118+
119+
function Assert-LaunchResult {
120+
param($Result, [bool]$ShouldLaunch, [string]$Name)
121+
if ($ShouldLaunch) {
122+
if ($Result.ExitCode -ne 0 -or -not $Result.JavaInvoked) {
123+
throw "${Name}: expected a successful Java launch. $($Result.Output)"
124+
}
125+
if ($Result.Arguments -notmatch 'org\.apache\.iotdb\.edge\.EdgeNode') {
126+
throw "${Name}: the Edge main class was not invoked"
127+
}
128+
} elseif ($Result.ExitCode -eq 0 -or $Result.JavaInvoked) {
129+
throw "${Name}: expected rejection before starting Java. $($Result.Output)"
130+
}
131+
Write-Host "PASS: $Name"
132+
}
133+
134+
try {
135+
# Use an executable stub so batch control flow and argument quoting match a real JVM.
136+
Add-Type -OutputAssembly $javaStub -OutputType ConsoleApplication -TypeDefinition @'
137+
using System;
138+
using System.IO;
139+
140+
internal static class EdgeJavaStub
141+
{
142+
private static int Main(string[] args)
143+
{
144+
if (args.Length == 1 && args[0] == "-fullversion")
145+
{
146+
Console.Error.WriteLine("openjdk full version \"17.0.5+8\"");
147+
return 0;
148+
}
149+
File.WriteAllLines(Environment.GetEnvironmentVariable("EDGE_TEST_JAVA_ARGS"), args);
150+
return 0;
151+
}
152+
}
153+
'@
154+
155+
$freePorts = Get-FreePorts
156+
foreach ($locale in @('', '-Dtsfile.locale=zh')) {
157+
$result = Invoke-EdgeLauncher -Ports $freePorts -Locale $locale
158+
Assert-LaunchResult $result $true "locale '$locale', custom config and paths with spaces"
159+
if ($locale -eq '') {
160+
if ($result.Arguments -match '-Dtsfile\.locale=') {
161+
throw 'The default package inherited a stale TsFile locale option'
162+
}
163+
} elseif ([regex]::Matches($result.Arguments, '-Dtsfile\.locale=zh').Count -ne 1 -or $result.Arguments -match 'tsfile\.locale=stale') {
164+
throw 'The zh package did not apply exactly one filtered TsFile locale option'
165+
}
166+
}
167+
168+
foreach ($name in $portNames) {
169+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
170+
$listener.Start()
171+
try {
172+
$ports = $freePorts.Clone()
173+
$ports[$name] = $listener.LocalEndpoint.Port
174+
$result = Invoke-EdgeLauncher -Ports $ports
175+
Assert-LaunchResult $result $false "occupied $name"
176+
if ($result.Output -notmatch "The $name $($ports[$name]) is already occupied") {
177+
throw "The occupied port was not identified correctly: $($result.Output)"
178+
}
179+
} finally {
180+
$listener.Stop()
181+
}
182+
}
183+
184+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
185+
$listener.Start()
186+
try {
187+
$result = Invoke-EdgeLauncher -Ports $freePorts -ExtraLines @(
188+
"# cn_internal_port=$($listener.LocalEndpoint.Port)",
189+
"! dn_rpc_port=$($listener.LocalEndpoint.Port)",
190+
"cn_internal_port=$($listener.LocalEndpoint.Port)"
191+
)
192+
Assert-LaunchResult $result $true 'comments, whitespace and the last value of a repeated property'
193+
} finally {
194+
$listener.Stop()
195+
}
196+
197+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 10720)
198+
$listener.Start()
199+
try {
200+
$ports = $freePorts.Clone()
201+
$ports.Remove('cn_consensus_port')
202+
$result = Invoke-EdgeLauncher -Ports $ports
203+
Assert-LaunchResult $result $false 'default port for an omitted property'
204+
$result = Invoke-EdgeLauncher -Ports @{} -MissingConfig
205+
Assert-LaunchResult $result $false 'default ports when the configuration file is absent'
206+
if ($result.Output -notmatch 'cn_consensus_port 10720 is already occupied') {
207+
throw 'The missing-file fallback did not check the default ConfigNode consensus port'
208+
}
209+
} finally {
210+
$listener.Stop()
211+
}
212+
213+
foreach ($value in @('0', '65536', 'not-a-port')) {
214+
$ports = $freePorts.Clone()
215+
$ports['dn_rpc_port'] = $value
216+
$result = Invoke-EdgeLauncher -Ports $ports
217+
Assert-LaunchResult $result $false "invalid port '$value'"
218+
}
219+
Write-Host "All $script:caseCount Windows Edge launcher cases passed."
220+
} finally {
221+
Remove-Item -LiteralPath $testRoot -Recurse -Force
222+
}

‎.github/workflows/edge-it.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ jobs:
6969
-P with-integration-tests,EdgeIT \
7070
-DskipUTs \
7171
-Dit.test=IoTDBEdgeBasicIT \
72+
-DEdgeConfigNodeAddress=127.0.0.2 \
7273
-DfailIfNoTests=false \
7374
-Dfailsafe.failIfNoSpecifiedTests=false \
7475
-pl integration-test \
@@ -83,3 +84,11 @@ jobs:
8384
integration-test/target/failsafe-reports
8485
if-no-files-found: ignore
8586
retention-days: 1
87+
88+
WindowsScripts:
89+
runs-on: windows-latest
90+
steps:
91+
- uses: actions/checkout@v5
92+
- name: Test Edge Windows launchers
93+
shell: powershell
94+
run: .github/scripts/test-edge-windows.ps1

‎distribution/src/assembly/edge.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
<include>stop-edge.sh</include>
103103
<include>start-cli.sh</include>
104104
<include>windows/start-edge.bat</include>
105+
<include>windows/check-edge.ps1</include>
105106
<include>windows/stop-edge.bat</include>
106107
<include>windows/start-cli.bat</include>
107108
<include>windows/start-cli-table.bat</include>

‎distribution/src/assembly/resources/conf-edge/logback-edge.xml‎

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -230,22 +230,14 @@
230230
<logger level="info" name="SAMPLED_QUERIES" additivity="false">
231231
<appender-ref ref="SAMPLED_QUERIES"/>
232232
</logger>
233-
<logger level="info" name="QUERY_FREQUENCY">
234-
<appender-ref ref="QUERY_FREQUENCY"/>
235-
</logger>
233+
<logger level="info" name="QUERY_FREQUENCY"/>
236234
<logger level="info" name="DETAILED_FAILURE_QUERY_TRACE"/>
237235
<logger level="info" name="COMPACTION">
238236
<appender-ref ref="COMPACTION"/>
239237
</logger>
240-
<logger level="info" name="org.apache.iotdb.pipe.api">
241-
<appender-ref ref="PIPE"/>
242-
</logger>
243-
<logger level="info" name="org.apache.iotdb.db.pipe">
244-
<appender-ref ref="PIPE"/>
245-
</logger>
246-
<logger level="info" name="org.apache.iotdb.commons.pipe">
247-
<appender-ref ref="PIPE"/>
248-
</logger>
238+
<logger level="info" name="org.apache.iotdb.pipe.api"/>
239+
<logger level="info" name="org.apache.iotdb.db.pipe"/>
240+
<logger level="info" name="org.apache.iotdb.commons.pipe"/>
249241
<logger level="info" name="EXPLAIN_ANALYZE" additivity="false">
250242
<appender-ref ref="EXPLAIN_ANALYZE"/>
251243
</logger>

‎integration-test/src/test/java/org/apache/iotdb/edge/it/IoTDBEdgeBasicIT.java‎

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,15 @@
3030
import org.junit.Test;
3131
import org.junit.experimental.categories.Category;
3232
import org.junit.runner.RunWith;
33+
import org.w3c.dom.Document;
34+
import org.w3c.dom.Element;
35+
import org.w3c.dom.NodeList;
36+
37+
import javax.xml.XMLConstants;
38+
import javax.xml.parsers.DocumentBuilderFactory;
3339

3440
import java.io.IOException;
41+
import java.io.InputStream;
3542
import java.io.UncheckedIOException;
3643
import java.nio.charset.StandardCharsets;
3744
import java.nio.file.Files;
@@ -49,6 +56,7 @@
4956
import java.util.LinkedHashMap;
5057
import java.util.List;
5158
import java.util.Map;
59+
import java.util.Properties;
5260
import java.util.Set;
5361
import java.util.concurrent.TimeUnit;
5462
import java.util.stream.Collectors;
@@ -71,6 +79,7 @@ public class IoTDBEdgeBasicIT {
7179

7280
private static final long SCRIPT_TIMEOUT_SECONDS = 45;
7381
private static final long STARTUP_TIMEOUT_SECONDS = 120;
82+
private static final Properties PACKAGED_SYSTEM_PROPERTIES = new Properties();
7483

7584
private static Path edgeHome;
7685
private static int[] ports;
@@ -91,6 +100,10 @@ public static void setUp() throws Exception {
91100
final Path extractionDir = WORK_DIR.resolve("package");
92101
unzip(edgePackage, extractionDir);
93102
edgeHome = findEdgeHome(extractionDir);
103+
try (InputStream input =
104+
Files.newInputStream(edgeHome.resolve("conf/iotdb-system.properties"))) {
105+
PACKAGED_SYSTEM_PROPERTIES.load(input);
106+
}
94107

95108
ports = EnvUtils.searchAvailablePorts();
96109
rpcPort = ports[2];
@@ -179,15 +192,41 @@ private static Connection openTableConnection() throws SQLException {
179192
SessionConfig.DEFAULT_PASSWORD);
180193
}
181194

195+
@Test
196+
public void testPackagedConfiguration() throws Exception {
197+
assertFalse(PACKAGED_SYSTEM_PROPERTIES.containsKey("model_inference_execution_thread_count"));
198+
assertTrue(Files.isRegularFile(edgeHome.resolve("sbin/windows/check-edge.ps1")));
199+
200+
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
201+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
202+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
203+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
204+
final Document document;
205+
try (InputStream input = Files.newInputStream(edgeHome.resolve("conf/logback-edge.xml"))) {
206+
document = factory.newDocumentBuilder().parse(input);
207+
}
208+
final Set<String> appenderNames = new HashSet<>();
209+
final NodeList appenders = document.getElementsByTagName("appender");
210+
for (int i = 0; i < appenders.getLength(); i++) {
211+
appenderNames.add(((Element) appenders.item(i)).getAttribute("name"));
212+
}
213+
final NodeList references = document.getElementsByTagName("appender-ref");
214+
for (int i = 0; i < references.getLength(); i++) {
215+
final String name = ((Element) references.item(i)).getAttribute("ref");
216+
assertTrue("Undefined Edge log appender: " + name, appenderNames.contains(name));
217+
}
218+
}
219+
182220
private static String jdbcUrl() {
183221
return Config.IOTDB_URL_PREFIX + "127.0.0.1:" + rpcPort;
184222
}
185223

186224
private static void configurePorts(final Path configFile) throws IOException {
225+
final String configNodeAddress = System.getProperty("EdgeConfigNodeAddress", "127.0.0.1");
187226
final Map<String, String> replacements = new LinkedHashMap<>();
188-
replacements.put("cn_seed_config_node", "127.0.0.1:" + ports[0]);
189-
replacements.put("dn_seed_config_node", "127.0.0.1:" + ports[0]);
190-
replacements.put("cn_internal_address", "127.0.0.1");
227+
replacements.put("cn_seed_config_node", configNodeAddress + ":" + ports[0]);
228+
replacements.put("dn_seed_config_node", configNodeAddress + ":" + ports[0]);
229+
replacements.put("cn_internal_address", configNodeAddress);
191230
replacements.put("cn_internal_port", Integer.toString(ports[0]));
192231
replacements.put("cn_consensus_port", Integer.toString(ports[1]));
193232
replacements.put("dn_rpc_address", "127.0.0.1");

‎iotdb-core/confignode/src/main/java/org/apache/iotdb/edge/EdgeNode.java‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ public static void main(String[] args) throws Exception {
7676
"EdgeNode-ConfigNode-Bootstrap");
7777
configNodeThread.start();
7878

79+
String internalAddress = ConfigNodeDescriptor.getInstance().getConf().getInternalAddress();
7980
int internalPort = ConfigNodeDescriptor.getInstance().getConf().getInternalPort();
80-
waitPortOpen(internalPort, configNodeError);
81+
waitPortOpen(internalAddress, internalPort, configNodeError);
8182
throwIfConfigNodeBootstrapFailed(configNodeError);
8283
Thread.sleep(LEADER_ELECTION_GRACE_MS);
8384
throwIfConfigNodeBootstrapFailed(configNodeError);
@@ -96,15 +97,16 @@ private static void throwIfConfigNodeBootstrapFailed(AtomicReference<Throwable>
9697
}
9798
}
9899

99-
private static void waitPortOpen(int port, AtomicReference<Throwable> configNodeError)
100+
private static void waitPortOpen(
101+
String address, int port, AtomicReference<Throwable> configNodeError)
100102
throws InterruptedException {
101103
long deadline = System.currentTimeMillis() + CONFIG_NODE_READY_TIMEOUT_MS;
102104
while (System.currentTimeMillis() < deadline) {
103105
if (configNodeError.get() != null) {
104106
return;
105107
}
106108
try (Socket socket = new Socket()) {
107-
socket.connect(new InetSocketAddress("127.0.0.1", port), 1000);
109+
socket.connect(new InetSocketAddress(address, port), 1000);
108110
return;
109111
} catch (Exception e) {
110112
Thread.sleep(PORT_PROBE_INTERVAL_MS);

0 commit comments

Comments
 (0)