feat(migration): add Migrate-To-V2.ps1 idempotent migration runner
This commit is contained in:
59
scripts/migration/Migrate-To-V2.ps1
Normal file
59
scripts/migration/Migrate-To-V2.ps1
Normal file
@@ -0,0 +1,59 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Idempotent migration runner that takes the OtOpcUaConfig database from the v1 schema
|
||||
(with ConfigGeneration / ClusterNodeGenerationState) to the v2 hosting-aligned schema
|
||||
(with Deployment / NodeDeploymentState / ConfigEdit / DataProtectionKeys).
|
||||
|
||||
.DESCRIPTION
|
||||
Backs the database up, applies the idempotent EF migration script, then validates that
|
||||
expected tables exist and legacy tables are gone. Safe to re-run — the EF script itself
|
||||
is idempotent, and the backup picks a unique filename per invocation.
|
||||
|
||||
.PARAMETER ConnectionString
|
||||
Mandatory. Full ADO.NET connection string with permissions to BACKUP DATABASE and
|
||||
apply DDL on the target ConfigDb.
|
||||
|
||||
.PARAMETER BackupPath
|
||||
Optional. Full path for the backup file. Defaults to a timestamped path under $env:TEMP.
|
||||
|
||||
.EXAMPLE
|
||||
.\Migrate-To-V2.ps1 -ConnectionString "Server=sql01;Database=OtOpcUaConfig;Trusted_Connection=True;TrustServerCertificate=True"
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)][string] $ConnectionString,
|
||||
[string] $BackupPath = "$env:TEMP\OtOpcUa-V1-Backup-$(Get-Date -Format yyyyMMddHHmmss).bak"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not (Get-Command Invoke-Sqlcmd -ErrorAction SilentlyContinue)) {
|
||||
throw "Invoke-Sqlcmd not available. Install module: Install-Module SqlServer -Scope CurrentUser"
|
||||
}
|
||||
|
||||
Write-Host "Step 1/4 — Backup ConfigDb to $BackupPath" -ForegroundColor Cyan
|
||||
Invoke-Sqlcmd -ConnectionString $ConnectionString `
|
||||
-Query "BACKUP DATABASE [OtOpcUaConfig] TO DISK = '$BackupPath' WITH FORMAT, COMPRESSION"
|
||||
|
||||
Write-Host "Step 2/4 — Row counts (before)" -ForegroundColor Cyan
|
||||
$beforeCounts = Invoke-Sqlcmd -ConnectionString $ConnectionString -InputFile "$PSScriptRoot\count-rows.sql"
|
||||
$beforeCounts | Format-Table
|
||||
|
||||
Write-Host "Step 3/4 — Apply Migrate-To-V2.sql" -ForegroundColor Cyan
|
||||
Invoke-Sqlcmd -ConnectionString $ConnectionString -InputFile "$PSScriptRoot\Migrate-To-V2.sql" -QueryTimeout 1800
|
||||
|
||||
Write-Host "Step 4/4 — Row counts (after) + validation" -ForegroundColor Cyan
|
||||
$afterCounts = Invoke-Sqlcmd -ConnectionString $ConnectionString -InputFile "$PSScriptRoot\count-rows.sql"
|
||||
$afterCounts | Format-Table
|
||||
|
||||
$tablesNow = (Invoke-Sqlcmd -ConnectionString $ConnectionString `
|
||||
-Query "SELECT name FROM sys.tables ORDER BY name").name
|
||||
|
||||
foreach ($t in 'Deployment','NodeDeploymentState','ConfigEdit','DataProtectionKeys') {
|
||||
if ($tablesNow -notcontains $t) { throw "Expected v2 table $t missing." }
|
||||
}
|
||||
foreach ($t in 'ConfigGeneration','ClusterNodeGenerationState') {
|
||||
if ($tablesNow -contains $t) { throw "Legacy v1 table $t still present." }
|
||||
}
|
||||
|
||||
Write-Host "Migration complete. Backup at $BackupPath" -ForegroundColor Green
|
||||
3259
scripts/migration/Migrate-To-V2.sql
Normal file
3259
scripts/migration/Migrate-To-V2.sql
Normal file
File diff suppressed because it is too large
Load Diff
26
scripts/migration/count-rows.sql
Normal file
26
scripts/migration/count-rows.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Per-table row counts for pre/post-migration audit.
|
||||
-- Covers every table relevant to the v1 -> v2 transition so the operator can confirm
|
||||
-- live-edit data was preserved and v2 tables came up empty.
|
||||
|
||||
SELECT TableName = t.name, [Rows] = SUM(p.[rows])
|
||||
FROM sys.tables t
|
||||
JOIN sys.partitions p ON p.object_id = t.object_id AND p.index_id IN (0,1)
|
||||
WHERE t.name IN (
|
||||
-- Live-edit configuration (rows must survive)
|
||||
'ServerCluster','ClusterNode','ClusterNodeCredential',
|
||||
'Namespace','UnsArea','UnsLine',
|
||||
'DriverInstance','Device','Equipment','Tag','PollGroup','VirtualTag',
|
||||
'NodeAcl','ExternalIdReservation',
|
||||
'Script','ScriptedAlarm','ScriptedAlarmState',
|
||||
'LdapGroupRoleMapping',
|
||||
'EquipmentImportBatch','EquipmentImportRow',
|
||||
-- Status tables (rebuilt at runtime; counts informational)
|
||||
'DriverHostStatus','DriverInstanceResilienceStatus',
|
||||
-- Audit (preserved)
|
||||
'ConfigAuditLog',
|
||||
-- v2 deploy model (empty pre-migration, populated post)
|
||||
'Deployment','NodeDeploymentState','ConfigEdit','DataProtectionKeys'
|
||||
)
|
||||
GROUP BY t.name
|
||||
ORDER BY t.name;
|
||||
GO
|
||||
Reference in New Issue
Block a user