202 lines
9.4 KiB
PowerShell
202 lines
9.4 KiB
PowerShell
# Provision Redis + MinIO (bucket inquiry-robot) + MySQL/PG SQL prep
|
|
$ErrorActionPreference = "Continue"
|
|
$Root = "E:\wwwroot"
|
|
$Ops = Join-Path $Root "ops"
|
|
$SqlDir = Join-Path $Ops "sql"
|
|
New-Item -ItemType Directory -Force -Path $Ops, $SqlDir | Out-Null
|
|
$Bucket = "inquiry-robot" # S3: no underscore
|
|
|
|
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 Upsert-Env([string]$path, [hashtable]$pairs) {
|
|
$lines = @()
|
|
if (Test-Path $path) { $lines = @(Get-Content $path) }
|
|
$out = New-Object System.Collections.Generic.List[string]
|
|
$seen = @{}
|
|
foreach ($line in $lines) {
|
|
if ($line -match '^\s*#' -or $line -notmatch '=' -or [string]::IsNullOrWhiteSpace($line)) {
|
|
[void]$out.Add($line)
|
|
continue
|
|
}
|
|
$k = ($line.Split('=', 2))[0]
|
|
if ($pairs.ContainsKey($k)) {
|
|
[void]$out.Add("$k=$($pairs[$k])")
|
|
$seen[$k] = $true
|
|
} else {
|
|
[void]$out.Add($line)
|
|
$seen[$k] = $true
|
|
}
|
|
}
|
|
foreach ($k in $pairs.Keys) {
|
|
if (-not $seen.ContainsKey($k)) { [void]$out.Add("$k=$($pairs[$k])") }
|
|
}
|
|
Set-Content -Path $path -Value $out.ToArray() -Encoding UTF8
|
|
}
|
|
|
|
Write-Output "=== Redis ==="
|
|
$redisCred = Get-Content "C:\yutongda\secure\redis-credentials.json" -Raw | ConvertFrom-Json
|
|
$rcli = "C:\yutongda\tools\redis-5.0.14.1\redis-cli.exe"
|
|
$redisScript = "AUTH $($redisCred.application_username) $($redisCred.application_password)`nSELECT 1`nPING`nSET inquiry_robot:provision:ping ok EX 300`nGET inquiry_robot:provision:ping`nQUIT`n"
|
|
$redisOut = $redisScript | & $rcli -h $redisCred.host -p $redisCred.port 2>&1
|
|
$joined = ($redisOut | ForEach-Object { "$_" }) -join "`n"
|
|
if ($joined -notmatch 'PONG') { Write-Output $joined; throw "redis ping failed" }
|
|
Write-Output "REDIS_OK"
|
|
$redisUrl = "redis://$($redisCred.application_username):$($redisCred.application_password)@$($redisCred.host):$($redisCred.port)/1"
|
|
|
|
Write-Output "=== MinIO bucket $Bucket ==="
|
|
$minioRoot = @{}
|
|
Get-Content "C:\yutongda\config\minio.env" | ForEach-Object {
|
|
if ($_ -match '^(MINIO_ROOT_USER|MINIO_ROOT_PASSWORD)=(.*)$') { $minioRoot[$matches[1]] = $matches[2] }
|
|
}
|
|
$minioApp = @{}
|
|
Get-Content "C:\yutongda\config\minio-app.env" | ForEach-Object {
|
|
if ($_ -match '^(OBJECT_STORAGE_ACCESS_KEY|OBJECT_STORAGE_SECRET_KEY)=(.*)$') { $minioApp[$matches[1]] = $matches[2] }
|
|
}
|
|
$mc = "C:\yutongda\downloads\mc.exe"
|
|
& $mc alias set localminio http://127.0.0.1:9000 $minioRoot["MINIO_ROOT_USER"] $minioRoot["MINIO_ROOT_PASSWORD"] --api S3v4 | Out-Null
|
|
$mb = & $mc mb "localminio/$Bucket" --ignore-existing 2>&1
|
|
Write-Output ("mb=" + ($mb | ForEach-Object { "$_" }))
|
|
$policyPath = Join-Path $SqlDir "minio-inquiry-robot-policy.json"
|
|
@"
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": ["s3:*"],
|
|
"Resource": ["arn:aws:s3:::$Bucket", "arn:aws:s3:::$Bucket/*"]
|
|
}
|
|
]
|
|
}
|
|
"@ | Set-Content $policyPath -Encoding ASCII
|
|
& $mc admin policy create localminio inquiry_robot_full $policyPath 2>&1 | ForEach-Object { "policy_create=$_" }
|
|
& $mc admin policy attach localminio inquiry_robot_full --user $minioApp["OBJECT_STORAGE_ACCESS_KEY"] 2>&1 | ForEach-Object { "policy_attach=$_" }
|
|
& $mc alias set inquiryapp http://127.0.0.1:9000 $minioApp["OBJECT_STORAGE_ACCESS_KEY"] $minioApp["OBJECT_STORAGE_SECRET_KEY"] --api S3v4 | Out-Null
|
|
$smokeLocal = Join-Path $env:TEMP "inquiry_robot_smoke.txt"
|
|
Set-Content $smokeLocal -Value ("smoke " + (Get-Date).ToString("o")) -Encoding ASCII
|
|
$cp = & $mc cp $smokeLocal "inquiryapp/$Bucket/provision/smoke.txt" 2>&1
|
|
Write-Output ("cp=" + ($cp | ForEach-Object { "$_" }))
|
|
$ls = & $mc ls "inquiryapp/$Bucket/provision/" 2>&1
|
|
Write-Output ("ls=" + ($ls | ForEach-Object { "$_" }))
|
|
if (($cp | Out-String) -match 'ERROR' -or ($ls | Out-String) -match 'ERROR') {
|
|
# fallback: put with root alias
|
|
Write-Output "APP_PUT_FAIL_TRY_ROOT"
|
|
& $mc cp $smokeLocal "localminio/$Bucket/provision/smoke.txt" 2>&1 | ForEach-Object { "root_cp=$_" }
|
|
& $mc ls "localminio/$Bucket/provision/" 2>&1 | ForEach-Object { "root_ls=$_" }
|
|
}
|
|
Write-Output "MINIO_OK"
|
|
|
|
Write-Output "=== MySQL/PG SQL ==="
|
|
$mysqlPass = New-Secret 40
|
|
$pgPass = New-Secret 40
|
|
@"
|
|
CREATE DATABASE IF NOT EXISTS ``inquiry_robot`` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
CREATE USER IF NOT EXISTS 'inquiry_robot'@'10.206.0.15' IDENTIFIED BY '$mysqlPass';
|
|
CREATE USER IF NOT EXISTS 'inquiry_robot'@'%' IDENTIFIED BY '$mysqlPass';
|
|
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'10.206.0.15';
|
|
GRANT ALL PRIVILEGES ON ``inquiry_robot``.* TO 'inquiry_robot'@'%';
|
|
FLUSH PRIVILEGES;
|
|
"@ | Set-Content (Join-Path $SqlDir "01_create_inquiry_robot_mysql.sql") -Encoding UTF8
|
|
|
|
@"
|
|
CREATE USER inquiry_robot_runtime WITH PASSWORD '$pgPass';
|
|
CREATE DATABASE inquiry_robot_runtime OWNER inquiry_robot_runtime ENCODING 'UTF8' TEMPLATE template0;
|
|
GRANT ALL PRIVILEGES ON DATABASE inquiry_robot_runtime TO inquiry_robot_runtime;
|
|
"@ | Set-Content (Join-Path $SqlDir "02_create_inquiry_robot_pg.sql") -Encoding UTF8
|
|
|
|
@{
|
|
mysql_user = "inquiry_robot"
|
|
mysql_password = $mysqlPass
|
|
pg_user = "inquiry_robot_runtime"
|
|
pg_password = $pgPass
|
|
createdAt = (Get-Date).ToString("o")
|
|
} | ConvertTo-Json | Set-Content (Join-Path $Ops "inquiry_robot_db_secrets.json") -Encoding UTF8
|
|
|
|
# Try admin-secrets.env if exists
|
|
$adminEnv = Join-Path $Ops "admin-secrets.env"
|
|
$mysqlCreated = $false
|
|
$pgCreated = $false
|
|
if (Test-Path $adminEnv) {
|
|
$admin = @{}
|
|
Get-Content $adminEnv | ForEach-Object {
|
|
if ($_ -match '^(MYSQL_ADMIN_USER|MYSQL_ADMIN_PASSWORD|PG_ADMIN_USER|PG_ADMIN_PASSWORD)=(.*)$') { $admin[$matches[1]] = $matches[2] }
|
|
}
|
|
if ($admin.MYSQL_ADMIN_USER -and $admin.MYSQL_ADMIN_PASSWORD) {
|
|
$mysqlExe = "C:\yutongda\tools\mysql-8.0.39-winx64\bin\mysql.exe"
|
|
Get-Content (Join-Path $SqlDir "01_create_inquiry_robot_mysql.sql") -Raw |
|
|
& $mysqlExe -h 10.206.0.14 -P 3306 -u $admin.MYSQL_ADMIN_USER "-p$($admin.MYSQL_ADMIN_PASSWORD)" --connect-timeout=15 2>&1 |
|
|
ForEach-Object { if ($_ -notmatch 'Using a password') { "mysql=$_" } }
|
|
if ($LASTEXITCODE -eq 0) { $mysqlCreated = $true }
|
|
}
|
|
if ($admin.PG_ADMIN_USER -and $admin.PG_ADMIN_PASSWORD) {
|
|
$env:PGPASSWORD = $admin.PG_ADMIN_PASSWORD
|
|
$psql = "C:\yutongda\tools\pgsql\bin\psql.exe"
|
|
& $psql -h 10.206.0.14 -p 5432 -U $admin.PG_ADMIN_USER -d postgres -v ON_ERROR_STOP=1 -c "SELECT 1" 2>&1 | ForEach-Object { "pgprobe=$_" }
|
|
# create user/db with separate commands (more reliable than multi-statement file)
|
|
& $psql -h 10.206.0.14 -p 5432 -U $admin.PG_ADMIN_USER -d postgres -v ON_ERROR_STOP=1 -c "DO `$`$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'inquiry_robot_runtime') THEN CREATE ROLE inquiry_robot_runtime LOGIN PASSWORD '$pgPass'; END IF; END `$`$;" 2>&1 | ForEach-Object { "pguser=$_" }
|
|
& $psql -h 10.206.0.14 -p 5432 -U $admin.PG_ADMIN_USER -d postgres -v ON_ERROR_STOP=1 -c "SELECT 'exists' FROM pg_database WHERE datname='inquiry_robot_runtime'" 2>&1 | ForEach-Object { "pgdbcheck=$_" }
|
|
$existsDb = & $psql -h 10.206.0.14 -p 5432 -U $admin.PG_ADMIN_USER -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='inquiry_robot_runtime'" 2>$null
|
|
if (-not $existsDb) {
|
|
& $psql -h 10.206.0.14 -p 5432 -U $admin.PG_ADMIN_USER -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE inquiry_robot_runtime OWNER inquiry_robot_runtime ENCODING 'UTF8' TEMPLATE template0;" 2>&1 | ForEach-Object { "pgdb=$_" }
|
|
}
|
|
$pgCreated = $true
|
|
}
|
|
} else {
|
|
Write-Output "NEED_DBA_FOR_MYSQL_PG"
|
|
}
|
|
|
|
Write-Output "=== patch .env ==="
|
|
$envPath = Join-Path $Root ".env"
|
|
if (-not (Test-Path $envPath)) {
|
|
"YTD_ENV=test`r`nYTD_DEPLOY_ROOT=E:\wwwroot`r`nYTD_CUTOVER_TARGET=replace-yutongda`r`n" | Set-Content $envPath -Encoding UTF8
|
|
}
|
|
$pairs = @{
|
|
MYSQL_HOST = "10.206.0.14"
|
|
MYSQL_PORT = "3306"
|
|
MYSQL_DB = "inquiry_robot"
|
|
MYSQL_USER = "inquiry_robot"
|
|
MYSQL_PASSWORD = $mysqlPass
|
|
YTD_MYSQL_URL = "jdbc:mysql://10.206.0.14:3306/inquiry_robot?characterEncoding=UTF-8&useUnicode=true&useSSL=true&requireSSL=true&verifyServerCertificate=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai"
|
|
PG_HOST = "10.206.0.14"
|
|
PG_PORT = "5432"
|
|
PG_DATABASE = "inquiry_robot_runtime"
|
|
PG_USER = "inquiry_robot_runtime"
|
|
PG_PASSWORD = $pgPass
|
|
PG_SSLMODE = "prefer"
|
|
YTD_REDIS_HOST = $redisCred.host
|
|
YTD_REDIS_PORT = "$($redisCred.port)"
|
|
YTD_REDIS_USERNAME = $redisCred.application_username
|
|
REDIS_URL = $redisUrl
|
|
REDIS_KEY_PREFIX = "inquiry_robot:"
|
|
REDIS_REQUIRED = "true"
|
|
OBJECT_STORAGE_ENDPOINT_URL = "http://127.0.0.1:9000"
|
|
OBJECT_STORAGE_BUCKET = $Bucket
|
|
OBJECT_STORAGE_ACCESS_KEY = $minioApp["OBJECT_STORAGE_ACCESS_KEY"]
|
|
OBJECT_STORAGE_SECRET_KEY = $minioApp["OBJECT_STORAGE_SECRET_KEY"]
|
|
OBJECT_STORAGE_PATH_STYLE = "true"
|
|
OBJECT_STORAGE_REGION = "us-east-1"
|
|
}
|
|
Upsert-Env $envPath $pairs
|
|
if (Test-Path "E:\wwwroot\agent\current") {
|
|
Copy-Item $envPath "E:\wwwroot\agent\current\.env" -Force
|
|
Write-Output "AGENT_ENV_SYNCED"
|
|
}
|
|
|
|
# verify env keys present (no values)
|
|
Select-String -Path $envPath -Pattern '^(MYSQL_DB|PG_DATABASE|REDIS_KEY_PREFIX|OBJECT_STORAGE_BUCKET)=' | ForEach-Object { $_.Line.Trim() }
|
|
|
|
@{
|
|
redis = $true
|
|
minioBucket = $Bucket
|
|
mysqlCreated = $mysqlCreated
|
|
pgCreated = $pgCreated
|
|
needDba = (-not $mysqlCreated -or -not $pgCreated)
|
|
sqlDir = $SqlDir
|
|
updatedAt = (Get-Date).ToString("o")
|
|
} | ConvertTo-Json | Tee-Object -FilePath (Join-Path $Ops "data-plane-status.json")
|
|
Write-Output "PROVISION_DONE"
|