Den hÀr artikeln Àr helt praktisk och handlar om min sorgliga historia
Inför Zero Touch PROD för RDS (MS SQL), om vilket vi fĂ„tt höra allt för mycket, gjorde jag en presentation (POC â Proof Of Concept) för automatisering: en uppsĂ€ttning powershell-skript. Efter presentationen, nĂ€r de stormiga och lĂ„ngvariga applĂ„derna avtog och övergick i oavbrutna ovationer, sa de till mig â allt detta Ă€r bra, men av ideologiska skĂ€l arbetar alla vĂ„ra Jenkins slaves pĂ„ Linux!
Kan man verkligen göra sĂ„? Ta en sĂ„ vĂ€nlig, ljus DBA frĂ„n Windows och slĂ€nga in honom i det brinnande infernot av powershell pĂ„ Linux? Ăr inte det grymt?

Jag var tvungen att dyka ner i denna mÀrkliga kombination av teknologier. SjÀlvklart slutade alla mina 30+ skript att fungera. Till min förvÄning lyckades jag under en arbetsdag fixa allt. Jag skriver medan jag fortfarande har det fÀrskt i minnet. SÄ, vilka fallgropar kan ni stöta pÄ nÀr ni flyttar powershell-skript frÄn Windows till Linux?
sqlcmd vs Invoke-SqlCmd
LÄt mig pÄminna er om den grundlÀggande skillnaden mellan dem. Den gamla hederliga verktygen sqlcmd fungerar Àven pÄ Linux, med nÀstan identisk funktionalitet. FrÄgan som ska utföras anger vi med -Q, indatafilen som -i och utdata som -o. Det enda Àr att filnamnen, förstÄs, blir skiftlÀgeskÀnsliga. Om du anvÀnder -i, skriv i slutet av filen:
GO
EXITOm slutet inte har EXIT, sÄ kommer sqlcmd att vÀnta pÄ inmatning, och om innan EXIT inte GO, sÄ kommer sista kommandot inte att köras. All utdata, selects, meddelanden, print och sÄ vidare gÄr till utdatafilen.
Invoke-SqlCmd ger resultatet som DataSet, DataTables eller DataRows. SÄ om du kan behandla resultatet av en enkel select, kan du göra det Àven genom sqlcmd, men att fÄ ut nÄgot mer komplicerat Àr nÀstan omöjligt: det Àr det Invoke-SqlCmd. Men den hÀr kommandot har sina egna knep:
- Om du skickar en fil via -InputFile, sÄ EXIT Àr inte nödvÀndigt, dessutom ger det en syntaktisk fel
- -OutputFile nej, kommandot ger dig resultatet som ett objekt
- För att ange server finns det tvÄ syntaxer: -ServerInstance -Username -Password -Database och via -ConnectionString. Konstigt nog kan vi inte ange en port som skiljer sig frÄn 1433 i första fallet.
- Textutdata, sÄsom PRINT, som enkelt kan "fÄngas" sqlcmd, för Invoke-SqlCmd
- Och viktigast av allt:
Och det Àr det största problemet. Först i mars blev detta cmdlet , och Àntligen kan vi gÄ framÄt!
VariabelersÀttning
I sqlcmd finns det variabelinmatning via -v, till exempel sÄ hÀr:
# $conn ŃĐŸĐŽĐ”ŃĐ¶ĐžŃ ĐœĐ°ŃĐ°Đ»ĐŸ ĐșĐŸĐŒĐ°ĐœĐŽŃ sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res
-v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmdI ett SQL-skript anvÀnder vi inmatningar:
set @spid=$(spid)
set @age=$(age)SĂ„ hĂ€r Ă€r det. I *nix fungerar inte variabelinmatningar. Parametern -v ignoreras. Hos Invoke-SqlCmd ignoreras -Variabler. Ăven om parametern som definierar variablerna ignoreras, fungerar inmatningarna â du kan anvĂ€nda vilka variabler som helst frĂ„n Shell. Men jag blev besviken pĂ„ variablerna och bestĂ€mde mig för att helt avstĂ„ frĂ„n dem, och gjorde det enkelt och primitivt, tack och lov Ă€r SQL-skript korta:
# prepend the parameters
"declare @age int, @spid int" | Add-Content "q.sql"
"set @spid=" + $spid | Add-Content "q.sql"
"set @age=" + $age | Add-Content "q.sql"
foreach ($line in Get-Content "Sqlserver/Automation/KillSpid.sql") {
$line | Add-Content "q.sql"
}
$cmd = "/opt/mssql-tools/bin/" + $conn + " -i q.sql -o res.log"Det hÀr, som du förstÄr, Àr ett test med den unix-versionen.
Filuppladdning
I Windows-versionen följdes varje operation av en granskning: vi körde sqlcmd, fick nÄgot klagomÄl i output-filen, fÀste den filen till granskningstabellen. Tack och lov körde SQL-servern pÄ samma server som Jenkins, sÄ hÀr gjordes det:
CREATE procedure AuditUpload
@id int, @filename varchar(256)
as
set nocount on
declare @sql varchar(max)
CREATE TABLE #multi (filer NVARCHAR(MAX))
set @sql='BULK INSERT #multi FROM '''+@filename
+''' WITH (ROWTERMINATOR = '' '',CODEPAGE = ''ACP'')'
exec (@sql)
select @sql=filer from #multi
update JenkinsAudit set multiliner=@sql where ID=@id
returnSÄledes laddar vi hela BCP-filen och placera den i nvarchar(max)-fÀltet i granskningsbordet. Naturligtvis kollapsade hela detta system eftersom jag istÀllet för SQL-server fick RDS, och BULK INSERT fungerar överhuvudtaget inte via UNC pÄ grund av försöket att ta en exklusiv lÄs pÄ filen, och med RDS Àr det överhuvudtaget frÄn början dömt att misslyckas. SÄ jag beslutade mig för att förÀndra systemdesignen, och lagra granskning rad för rad:
CREATE TABLE AuditOut (
ID int NULL,
TextLine nvarchar(max) NULL,
n int IDENTITY(1,1) PRIMARY KEY
)Och skriva till denna tabell sÄ hÀr:
function WriteAudit([string]$Filename, [string]$ConnStr,
[string]$Tabname, [string]$Jobname)
{
# hÀmta $lastid frÄn senaste körning -- hoppar över för artikeln
# skapa grid och fylla den med data frÄn fil
$audit = Get-Content $Filename
$DT = new-object Data.DataTable
$COL1 = new-object Data.DataColumn;
$COL1.ColumnName = "ID";
$COL1.DataType = [System.Type]::GetType("System.Int32")
$COL2 = new-object Data.DataColumn;
$COL2.ColumnName = "TextLine";
$COL2.DataType = [System.Type]::GetType("System.String")
$DT.Columns.Add($COL1)
$DT.Columns.Add($COL2)
foreach ($line in $audit)
{
$DR = $dt.NewRow()
$DR.Item("ID") = $lastid
$DR.Item("TextLine") = $line
$DT.Rows.Add($DR)
}
# skriv till tabellen
$conn=new-object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = $ConnStr
$conn.Open()
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnStr
$bulkCopy.DestinationTableName = $Tabname
$bulkCopy.BatchSize = 50000
$bulkCopy.BulkCopyTimeout = 0
$bulkCopy.WriteToServer($DT)
$conn.Close()
}
För att hÀmta innehÄllet mÄste man göra en select baserat pÄ ID, i ordning n (identity).
I nÀsta artikel kommer jag att gÄ mer i detalj pÄ hur allt detta interagerar med Jenkins.
KĂ€lla: habr.com
