Ovaj članak je isključivo praktičan i posvećen je mojoj tužnoj priči
Spremam se za Zero Touch PROD za RDS (MS SQL), o kojem su nam sve uši brujale, napravio sam prezentaciju (POC - Proof Of Concept) automatizacije: skup powershell skripti. Nakon prezentacije, kada je buran, dugotrajan aplauz utihnuo, prešao u neprestani aplauz, rekli su mi - sve je to dobro, ali samo iz ideoloških razloga, svi naši robovi Jenkinsa rade na Linuxu!
Je li to moguće? Uzmite tako toplu, lampu DBA ispod Windowsa i zataknite je u samu vrućinu powershell-a pod Linuxom? Nije li ovo okrutno?
Morao sam se uroniti u ovu čudnu kombinaciju tehnologija. Naravno, svih mojih 30+ skripti su prestale da rade. Na moje iznenađenje, uspjela sam sve popraviti u jednom radnom danu. Pišem u žurbi. Dakle, na koje zamke možete naići kada prenosite powershell skripte sa Windowsa na Linux?
sqlcmd vs Invoke-SqlCmd
Dozvolite mi da vas podsjetim na glavnu razliku između njih. Dobri stari uslužni program sqlcmd Također radi pod Linuxom, sa gotovo identičnom funkcionalnošću. Prosljeđujemo upit za izvršavanje -Q, ulaznu datoteku kao -i, a izlaznu kao -o. Ali imena datoteka, naravno, razlikuju se velika i mala slova. Ako koristite -i, onda u datoteci napišite na kraju:
GO
EXIT
Ako na kraju nema EXIT-a, tada će sqlcmd nastaviti čekati na unos, a ako prije IZLAZ neće GO, tada posljednja komanda neće raditi. Izlazna datoteka sadrži sav izlaz, odabire, poruke, ispis, itd.
Invoke-SqlCmd proizvodi rezultat kao DataSet, DataTables ili DataRows. Stoga, ako obradite rezultat jednostavnog odabira, možete ga koristiti sqlcmd, nakon raščlanjivanja njegovog izlaza, gotovo je nemoguće izvesti nešto složeno: jer ovo postoji Invoke-SqlCmd. Ali ova ekipa ima i svoje šale:
Ako joj prenesete datoteku putem -InputFile, onda IZLAZ nije potrebno, štoviše, proizvodi sintaksičku grešku
-OutputFile ne, komanda vam vraća rezultat kao objekat
Postoje dvije sintakse za određivanje servera: -ServerInstance -Korisničko ime -Lozinka -Baza podataka i kroz -ConnectionString. Čudno, u prvom slučaju nije moguće navesti port osim 1433.
izlaz teksta, ukucajte PRINT, koji je jednostavno "uhvaćen" sqlcmdza Invoke-SqlCmdje problem
sqlcmd ima zamjenu varijable koristeći -v, na primjer ovako:
# $conn содержит начало команды sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res
-v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmd
U SQL skripti koristimo zamjene:
set @spid=$(spid)
set @age=$(age)
Evo ga. U *nix zamjene varijabli ne rade. Parametar -v ignorisano. U Invoke-SqlCmd ignorisano -Varijable. Iako je parametar koji specificira same varijable zanemaren, same zamjene funkcioniraju—možete koristiti bilo koje varijable iz Shell-a. Međutim, uvrijedile su me varijable i odlučio sam da uopće ne ovisim o njima, te sam postupio grubo i primitivno, jer su SQL skripte kratke:
Ovo je, kao što razumijete, test već iz Unix verzije.
Učitavanje fajlova
U Windows verziji, svaka operacija je bila praćena revizijom: pokrenuli smo sqlcmd, primili neku vrstu zloupotrebe u izlaznoj datoteci, priložili ovu datoteku na ploču za reviziju. Srećom, SQL server je radio na istom serveru kao i Jenkins, urađeno je otprilike ovako:
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
return
Dakle, u potpunosti progutamo BCP datoteku i uguramo je u nvarchar(max) polje tabele revizije. Naravno, ceo ovaj sistem se raspao jer sam umesto SQL servera dobio RDS, a BULK INSERT uopste ne radi preko UNC-a zbog pokusaja preuzimanja ekskluzivne brave na fajlu, a sa RDS-om je to generalno osudjeno na propast. samom početku. Zato sam odlučio da promenim dizajn sistema, čuvajući reviziju red po red:
CREATE TABLE AuditOut (
ID int NULL,
TextLine nvarchar(max) NULL,
n int IDENTITY(1,1) PRIMARY KEY
)
I zapišite u ovu tabelu ovako:
function WriteAudit([string]$Filename, [string]$ConnStr,
[string]$Tabname, [string]$Jobname)
{
# get $lastid of the last execution -- проскипано для статьи
#create grid and populate it with data from file
$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)
}
# write it to table
$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()
}
Da biste odabrali sadržaj, potrebno je da odaberete po ID-u, birajući redom n (identitet).
U sljedećem članku ću detaljnije o tome kako sve ovo djeluje s Jenkinsom.