Files
inquiry_robot/deploy/_remote_redo_skipgrant_and_bind.ps1
T

239 lines
11 KiB
PowerShell

# Redo skip-grant on local YTD-MySQL carefully; create inquiry_robot; expose via 10.206.0.15.
# Then ensure PG role/db + listen; point .env to intranet 10.206.0.15.
$ErrorActionPreference = "Stop"
$mysqlBin = "C:\yutongda\tools\mysql-8.0.39-winx64\bin"
$mysql = Join-Path $mysqlBin "mysql.exe"
$mysqld = Join-Path $mysqlBin "mysqld.exe"
$ini = "C:\yutongda\tools\mysql-8.0.39-winx64\my.ini"
$datadir = "C:\yutongda\data\mysql"
$ops = "E:\wwwroot\ops"
$psql = "C:\yutongda\tools\pgsql\bin\psql.exe"
$sec = Get-Content (Join-Path $ops "inquiry_robot_db_secrets.json") -Raw | ConvertFrom-Json
$mysqlPass = [string]$sec.mysql_password
$pgPass = [string]$sec.pg_password
$intranet = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -like '10.*' } | Select-Object -First 1 -ExpandProperty IPAddress)
function New-Secret([int]$len = 48) {
$bytes = New-Object byte[] $len
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
return ([Convert]::ToBase64String($bytes) -replace '[+/=]', 'x').Substring(0, [Math]::Min(64, $len))
}
function Mysql-Ok([string]$h, [string]$u, [string]$p, [string]$db) {
$list = New-Object System.Collections.Generic.List[string]
if ($h) { [void]$list.Add("-h$h"); [void]$list.Add("-P3306") }
[void]$list.Add("-u$u")
if ($p -ne $null -and $p -ne "") { [void]$list.Add("-p$p") }
[void]$list.Add("--connect-timeout=8"); [void]$list.Add("--batch"); [void]$list.Add("-N")
if ($db) { [void]$list.Add($db) }
[void]$list.Add("-e"); [void]$list.Add("SELECT 1")
$out = & $mysql ($list.ToArray()) 2>&1
$text = ($out | ForEach-Object {"$_"}) -join "`n"
if ($text -match "ERROR") { return $false }
$lines = $out | ForEach-Object {"$_".Trim()} | Where-Object { $_ -ne "" -and $_ -notmatch "Warning" }
return ($lines -contains "1")
}
Write-Output ("intranet=" + $intranet)
Write-Output ("ops_exists=" + (Test-Path -LiteralPath $ops))
# Generate root password FIRST and persist before any service stop
$rootPass = New-Secret 48
$rootFile = Join-Path $ops "local_mysql_root_secret.json"
@{
root_password = $rootPass
createdAt = (Get-Date).ToString("o")
note = "local YTD-MySQL on app server; apps use inquiry_robot user"
} | ConvertTo-Json | Set-Content -LiteralPath $rootFile -Encoding UTF8
Set-Content -LiteralPath (Join-Path $ops ".mysql_root_stash.tmp") -Value $rootPass -Encoding ascii -NoNewline
Write-Output ("root_secret_saved len=" + $rootPass.Length)
Write-Output "=== preflight established ==="
$est = @(Get-NetTCPConnection -LocalPort 3306 -State Established -EA SilentlyContinue)
Write-Output ("established=" + $est.Count)
if ($est.Count -gt 0) { throw "active clients on 3306; refuse" }
Write-Output "=== stop YTD-MySQL ==="
Stop-Service YTD-MySQL -Force -ErrorAction Stop
Start-Sleep -Seconds 3
Get-Process mysqld -EA SilentlyContinue | Stop-Process -Force -EA SilentlyContinue
Start-Sleep -Seconds 2
if (Get-Process mysqld -EA SilentlyContinue) { throw "mysqld still running" }
Write-Output "=== maint mysqld skip-grant skip-networking ==="
$errLog = Join-Path $ops "mysql-skip-grant.err"
Remove-Item $errLog -Force -EA SilentlyContinue
$p = Start-Process -FilePath $mysqld -ArgumentList @(
"--defaults-file=$ini",
"--datadir=$datadir",
"--skip-grant-tables",
"--skip-networking",
"--shared-memory",
"--log-error=$errLog"
) -PassThru -WindowStyle Hidden
Write-Output ("maint_pid=" + $p.Id)
$ready = $false
for ($i = 0; $i -lt 40; $i++) {
Start-Sleep -Seconds 1
if ($p.HasExited) {
Get-Content $errLog -Tail 25 -EA SilentlyContinue
throw "maint exited"
}
# no password under skip-grant
if (Mysql-Ok $null "root" "" $null) { $ready = $true; break }
}
if (-not $ready) { Get-Content $errLog -Tail 25; throw "maint not ready" }
Write-Output "MAINT_READY"
$sqlPath = (Join-Path $ops "sql\_run_ir_mysql.sql") -replace '\\','/'
# Force empty-root recovery then set real passwords
$sql = @"
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY '$rootPass';
CREATE USER IF NOT EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '$rootPass';
ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '$rootPass';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION;
CREATE DATABASE IF NOT EXISTS ``inquiry_robot`` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'inquiry_robot'@'%' IDENTIFIED BY '$mysqlPass';
CREATE USER IF NOT EXISTS 'inquiry_robot'@'localhost' IDENTIFIED BY '$mysqlPass';
CREATE USER IF NOT EXISTS 'inquiry_robot'@'127.0.0.1' IDENTIFIED BY '$mysqlPass';
CREATE USER IF NOT EXISTS 'inquiry_robot'@'$intranet' IDENTIFIED BY '$mysqlPass';
ALTER USER 'inquiry_robot'@'%' IDENTIFIED BY '$mysqlPass';
ALTER USER 'inquiry_robot'@'localhost' IDENTIFIED BY '$mysqlPass';
ALTER USER 'inquiry_robot'@'127.0.0.1' IDENTIFIED BY '$mysqlPass';
ALTER USER 'inquiry_robot'@'$intranet' IDENTIFIED BY '$mysqlPass';
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'%';
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'localhost';
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'127.0.0.1';
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'$intranet';
FLUSH PRIVILEGES;
SELECT 'maint_ok' AS s;
"@
Set-Content -LiteralPath (Join-Path $ops "sql\_run_ir_mysql.sql") -Value $sql -Encoding ascii
$mout = & $mysql -u root -e "source $sqlPath" 2>&1 | Out-String
($mout -split "`r?`n" | Where-Object { $_ -notmatch "password|IDENTIFIED" }) | ForEach-Object { $_ }
if ($mout -notmatch "maint_ok") { throw "maint SQL failed" }
Write-Output "SQL_OK"
Write-Output "=== stop maint / start service ==="
Stop-Process -Id $p.Id -Force -EA SilentlyContinue
Start-Sleep -Seconds 3
Get-Process mysqld -EA SilentlyContinue | Stop-Process -Force -EA SilentlyContinue
Start-Sleep -Seconds 2
Start-Service YTD-MySQL
$ok = $false
for ($i = 0; $i -lt 40; $i++) {
Start-Sleep -Seconds 1
if ((Get-Service YTD-MySQL).Status -ne "Running") { continue }
if (Mysql-Ok "127.0.0.1" "inquiry_robot" $mysqlPass "inquiry_robot") { $ok = $true; break }
}
if (-not $ok) {
Write-Output ("root127=" + (Mysql-Ok "127.0.0.1" "root" $rootPass $null))
Write-Output ("app127=" + (Mysql-Ok "127.0.0.1" "inquiry_robot" $mysqlPass "inquiry_robot"))
throw "mysql smoke failed after restart"
}
Write-Output ("mysql127=OK intranet=" + (Mysql-Ok $intranet "inquiry_robot" $mysqlPass "inquiry_robot"))
Remove-Item -LiteralPath (Join-Path $ops "sql\_run_ir_mysql.sql") -Force -EA SilentlyContinue
Remove-Item -LiteralPath (Join-Path $ops "sql\_maint_create_inquiry_robot.sql") -Force -EA SilentlyContinue
Write-Output "=== PG ensure + intranet listen ==="
$env:PGPASSWORD = (Get-Content "C:\yutongda\secure\pgpass.txt" -Raw).Trim()
$dd = (& $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SHOW data_directory;").Trim()
Write-Output ("pg_data=" + $dd)
$pgConf = Join-Path $dd "postgresql.conf"
$pgHba = Join-Path $dd "pg_hba.conf"
$conf = Get-Content $pgConf -Raw
if ($conf -notmatch "(?m)^\s*listen_addresses\s*=\s*'\*'") {
if ($conf -match "(?m)^\s*#?\s*listen_addresses\s*=") {
$conf = [regex]::Replace($conf, "(?m)^\s*#?\s*listen_addresses\s*=\s*.*$", "listen_addresses = '*'", 1)
} else {
$conf += "`r`nlisten_addresses = '*'`r`n"
}
# postgresql.conf often expects system encoding; use UTF8 without BOM carefully
[System.IO.File]::WriteAllText($pgConf, $conf)
Write-Output "pg_listen_patched=true"
} else {
Write-Output "pg_listen_already_star"
}
$hbaLines = Get-Content $pgHba
if (-not ($hbaLines -match "inquiry_robot_runtime")) {
Add-Content $pgHba "host inquiry_robot_runtime inquiry_robot_runtime 10.206.0.0/16 scram-sha-256"
Add-Content $pgHba "host all all 10.206.0.0/16 scram-sha-256"
Write-Output "pg_hba_patched=true"
}
# upsert role/db via temp sql file to avoid PS quoting hell
$pgSql = @"
DO `$`$BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'inquiry_robot_runtime') THEN
CREATE ROLE inquiry_robot_runtime LOGIN PASSWORD '$pgPass';
ELSE
ALTER ROLE inquiry_robot_runtime WITH LOGIN PASSWORD '$pgPass';
END IF;
END`$`$;
SELECT 1 FROM pg_database WHERE datname='inquiry_robot_runtime';
"@
$pgSqlPath = Join-Path $ops "sql\_run_ir_pg.sql"
Set-Content $pgSqlPath -Value $pgSql -Encoding ascii
& $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -v ON_ERROR_STOP=1 -f $pgSqlPath 2>&1 | ForEach-Object { if ($_ -notmatch [regex]::Escape($pgPass)) { "$_" } }
$hasDb = & $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='inquiry_robot_runtime'"
if (($hasDb | Out-String) -notmatch "1") {
& $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -c "CREATE DATABASE inquiry_robot_runtime OWNER inquiry_robot_runtime;" 2>&1 | ForEach-Object { "$_" }
}
& $psql -h 127.0.0.1 -p 5432 -U postgres -d inquiry_robot_runtime -c "GRANT ALL ON SCHEMA public TO inquiry_robot_runtime;" 2>&1 | Out-Null
Remove-Item $pgSqlPath -Force -EA SilentlyContinue
$listen = (& $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -tAc "SHOW listen_addresses;").Trim()
Write-Output ("listen_addresses=" + $listen)
if ($listen -ne "*" -and $listen -notmatch $intranet) {
$pgSvc = Get-Service | Where-Object { $_.Name -match 'postgres|pgsql|YTD-PG' }
Write-Output ("restart pg svc=" + (($pgSvc | ForEach-Object Name) -join ","))
foreach ($s in $pgSvc) { Restart-Service $s.Name -Force -EA SilentlyContinue }
Start-Sleep -Seconds 6
}
& $psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -c "SELECT pg_reload_conf();" 2>&1 | Out-Null
$env:PGPASSWORD = $pgPass
$pg127 = (& $psql -h 127.0.0.1 -p 5432 -U inquiry_robot_runtime -d inquiry_robot_runtime -tAc "SELECT 'pg_ok'" 2>&1 | Out-String).Trim()
$pgNet = (& $psql -h $intranet -p 5432 -U inquiry_robot_runtime -d inquiry_robot_runtime -tAc "SELECT 'pg_ok'" 2>&1 | Out-String).Trim()
Write-Output ("pg127=" + $pg127)
Write-Output ("pgNet=" + $pgNet)
Remove-Item Env:PGPASSWORD -EA SilentlyContinue
if ($pg127 -notmatch "pg_ok") { throw "pg local smoke failed" }
Write-Output "=== patch .env hosts to intranet ==="
$envPath = "E:\wwwroot\.env"
$jdbc = "jdbc:mysql://${intranet}:3306/inquiry_robot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&serverTimezone=Asia/Shanghai"
$lines = Get-Content $envPath
$out = foreach ($line in $lines) {
if ($line -match '^MYSQL_HOST=') { "MYSQL_HOST=$intranet" }
elseif ($line -match '^PG_HOST=') { "PG_HOST=$intranet" }
elseif ($line -match '^YTD_MYSQL_URL=') { "YTD_MYSQL_URL=$jdbc" }
elseif ($line -match '^MYSQL_PASSWORD=') { "MYSQL_PASSWORD=$mysqlPass" }
elseif ($line -match '^PG_PASSWORD=') { "PG_PASSWORD=$pgPass" }
else { $line }
}
Set-Content $envPath -Value $out -Encoding UTF8
if (Test-Path "E:\wwwroot\agent\current") { Copy-Item $envPath "E:\wwwroot\agent\current\.env" -Force }
$status = @{
mysqlHost = $intranet
pgHost = $intranet
mysqlDb = "inquiry_robot"
pgDb = "inquiry_robot_runtime"
mysqlCreated = $true
pgCreated = $true
mysqlIntranetSmoke = [bool](Mysql-Ok $intranet "inquiry_robot" $mysqlPass "inquiry_robot")
pgIntranetSmoke = ($pgNet -match "pg_ok")
placement = "app-server via intranet IP (interim)"
dataHost14 = "still need DBA to create same empty DBs if/when migrating to 10.206.0.14"
updatedAt = (Get-Date).ToString("o")
} | ConvertTo-Json
Set-Content (Join-Path $ops "data-plane-status.json") -Value $status -Encoding UTF8
Write-Output $status
Write-Output "ALL_DONE"