blob: 7ca0f5be06e85308707834d59b8465a53ae33da7 [file]
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
$ErrorActionPreference = 'Stop'
$repositoryRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$testRoot = Join-Path ([System.IO.Path]::GetTempPath()) ('iotdb-edge-windows-' + [guid]::NewGuid())
New-Item -ItemType Directory -Path $testRoot | Out-Null
$javaStub = Join-Path $testRoot 'java.exe'
$script:caseCount = 0
$portNames = @(
'cn_internal_port', 'cn_consensus_port', 'dn_rpc_port', 'dn_internal_port',
'dn_mpp_data_exchange_port', 'dn_schema_region_consensus_port', 'dn_data_region_consensus_port'
)
function Get-FreePorts {
$result = @{}
$reservations = @()
try {
foreach ($name in $portNames) {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
$reservations += $listener
$result[$name] = $listener.LocalEndpoint.Port
}
return $result
} finally {
foreach ($listener in $reservations) {
$listener.Stop()
}
}
}
function Invoke-EdgeLauncher {
param(
[System.Collections.IDictionary]$Ports,
[string]$Locale = '',
[string[]]$ExtraLines = @(),
[switch]$MissingConfig
)
$script:caseCount++
$caseDir = Join-Path $testRoot "case-$script:caseCount"
$edgeHome = Join-Path $caseDir 'edge installation'
$configDir = Join-Path $caseDir 'custom configuration'
$launcherDir = Join-Path $edgeHome 'sbin/windows'
$envDir = Join-Path $configDir 'windows'
$javaHome = Join-Path $caseDir 'fake java'
$javaBin = Join-Path $javaHome 'bin'
New-Item -ItemType Directory -Path $launcherDir, $envDir, $javaBin -Force | Out-Null
foreach ($file in @('start-edge.bat', 'check-edge.ps1')) {
Copy-Item -LiteralPath (Join-Path $repositoryRoot "scripts/sbin/windows/$file") -Destination $launcherDir
}
Copy-Item -LiteralPath (Join-Path $repositoryRoot 'scripts/conf/windows/edge-env.bat') -Destination $envDir
$common = Get-Content -LiteralPath (Join-Path $repositoryRoot 'scripts/conf/windows/iotdb-common.bat') -Raw
Set-Content -LiteralPath (Join-Path $envDir 'iotdb-common.bat') -Value $common.Replace('@tsfile.locale.opt@', $Locale) -Encoding ASCII
if (-not $MissingConfig) {
$lines = @($ExtraLines)
foreach ($key in $Ports.Keys) {
$lines += " $key = $($Ports[$key]) "
}
Set-Content -LiteralPath (Join-Path $configDir 'iotdb-system.properties') -Value $lines -Encoding ASCII
}
Copy-Item -LiteralPath $javaStub -Destination $javaBin
$argsFile = Join-Path $caseDir 'java-arguments.txt'
$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
$startInfo.FileName = $env:ComSpec
$startInfo.Arguments = '/d /c call "' + (Join-Path $launcherDir 'start-edge.bat') + '"'
$startInfo.WorkingDirectory = $caseDir
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$startInfo.RedirectStandardInput = $true
$startInfo.EnvironmentVariables['IOTDB_HOME'] = $edgeHome
$startInfo.EnvironmentVariables['IOTDB_CONF'] = $configDir
$startInfo.EnvironmentVariables['JAVA_HOME'] = $javaHome
$startInfo.EnvironmentVariables['EDGE_TEST_JAVA_ARGS'] = $argsFile
$startInfo.EnvironmentVariables['IOTDB_JMX_OPTS'] = ''
$startInfo.EnvironmentVariables['TSFILE_LOCALE_JVM_OPT'] = '-Dtsfile.locale=stale'
$process = [System.Diagnostics.Process]::new()
$process.StartInfo = $startInfo
try {
[void]$process.Start()
$stdout = $process.StandardOutput.ReadToEndAsync()
$stderr = $process.StandardError.ReadToEndAsync()
$process.StandardInput.Close()
if (-not $process.WaitForExit(30000)) {
$process.Kill()
throw 'Timed out running the Edge Windows launcher'
}
return [pscustomobject]@{
ExitCode = $process.ExitCode
Output = $stdout.Result + $stderr.Result
JavaInvoked = Test-Path -LiteralPath $argsFile
Arguments = if (Test-Path -LiteralPath $argsFile) { Get-Content -LiteralPath $argsFile -Raw } else { '' }
}
} finally {
$process.Dispose()
}
}
function Assert-LaunchResult {
param($Result, [bool]$ShouldLaunch, [string]$Name)
if ($ShouldLaunch) {
if ($Result.ExitCode -ne 0 -or -not $Result.JavaInvoked) {
throw "${Name}: expected a successful Java launch. $($Result.Output)"
}
if ($Result.Arguments -notmatch 'org\.apache\.iotdb\.edge\.EdgeNode') {
throw "${Name}: the Edge main class was not invoked"
}
} elseif ($Result.ExitCode -eq 0 -or $Result.JavaInvoked) {
throw "${Name}: expected rejection before starting Java. $($Result.Output)"
}
Write-Host "PASS: $Name"
}
try {
# Use an executable stub so batch control flow and argument quoting match a real JVM.
Add-Type -OutputAssembly $javaStub -OutputType ConsoleApplication -TypeDefinition @'
using System;
using System.IO;
internal static class EdgeJavaStub
{
private static int Main(string[] args)
{
if (args.Length == 1 && args[0] == "-fullversion")
{
Console.Error.WriteLine("openjdk full version \"17.0.5+8\"");
return 0;
}
File.WriteAllLines(Environment.GetEnvironmentVariable("EDGE_TEST_JAVA_ARGS"), args);
return 0;
}
}
'@
$freePorts = Get-FreePorts
foreach ($locale in @('', '-Dtsfile.locale=zh')) {
$result = Invoke-EdgeLauncher -Ports $freePorts -Locale $locale
Assert-LaunchResult $result $true "locale '$locale', custom config and paths with spaces"
if ($locale -eq '') {
if ($result.Arguments -match '-Dtsfile\.locale=') {
throw 'The default package inherited a stale TsFile locale option'
}
} elseif ([regex]::Matches($result.Arguments, '-Dtsfile\.locale=zh').Count -ne 1 -or $result.Arguments -match 'tsfile\.locale=stale') {
throw 'The zh package did not apply exactly one filtered TsFile locale option'
}
}
foreach ($name in $portNames) {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
try {
$ports = $freePorts.Clone()
$ports[$name] = $listener.LocalEndpoint.Port
$result = Invoke-EdgeLauncher -Ports $ports
Assert-LaunchResult $result $false "occupied $name"
if ($result.Output -notmatch "The $name $($ports[$name]) is already occupied") {
throw "The occupied port was not identified correctly: $($result.Output)"
}
} finally {
$listener.Stop()
}
}
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
try {
$result = Invoke-EdgeLauncher -Ports $freePorts -ExtraLines @(
"# cn_internal_port=$($listener.LocalEndpoint.Port)",
"! dn_rpc_port=$($listener.LocalEndpoint.Port)",
"cn_internal_port=$($listener.LocalEndpoint.Port)"
)
Assert-LaunchResult $result $true 'comments, whitespace and the last value of a repeated property'
} finally {
$listener.Stop()
}
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 10720)
$listener.Start()
try {
$ports = $freePorts.Clone()
$ports.Remove('cn_consensus_port')
$result = Invoke-EdgeLauncher -Ports $ports
Assert-LaunchResult $result $false 'default port for an omitted property'
$result = Invoke-EdgeLauncher -Ports @{} -MissingConfig
Assert-LaunchResult $result $false 'default ports when the configuration file is absent'
if ($result.Output -notmatch 'cn_consensus_port 10720 is already occupied') {
throw 'The missing-file fallback did not check the default ConfigNode consensus port'
}
} finally {
$listener.Stop()
}
foreach ($value in @('0', '65536', 'not-a-port')) {
$ports = $freePorts.Clone()
$ports['dn_rpc_port'] = $value
$result = Invoke-EdgeLauncher -Ports $ports
Assert-LaunchResult $result $false "invalid port '$value'"
}
Write-Host "All $script:caseCount Windows Edge launcher cases passed."
} finally {
Remove-Item -LiteralPath $testRoot -Recurse -Force
}