refactor: Wszystkie pakiety przeniesione do internal
This commit is contained in:
75
internal/cfg/config.go
Normal file
75
internal/cfg/config.go
Normal file
@ -0,0 +1,75 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Connection struct {
|
||||
Host string `json:"Host,omitempty"`
|
||||
User string `json:"User,omitempty"`
|
||||
Port uint `json:"Port,omitempty"`
|
||||
DbName string `json:"DbName,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
PassFile string `json:"Passfile"`
|
||||
PsqlExec string `json:"PsqlExec"`
|
||||
SqlDir string `json:"SqlDir"`
|
||||
Defaults Connection `json:"Defaults"`
|
||||
Connections []Connection `json:"Connections"`
|
||||
}
|
||||
|
||||
func GetConfig(params Parameters) (config Config, err error) {
|
||||
bytes, err := os.ReadFile(params.ConfigFile)
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("błąd odczytu pliku konfiguracji: %w", err)
|
||||
}
|
||||
err = json.Unmarshal(bytes, &config)
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("błąd parsowania pliku konfiguracji: %w", err)
|
||||
}
|
||||
if config.PassFile == "" && params.Passfile != "" {
|
||||
config.PassFile = params.Passfile
|
||||
}
|
||||
if config.PassFile == "" {
|
||||
return config, fmt.Errorf("nie podano Passfile (dodaj w konfiguracji lub użyj flag --passfile)")
|
||||
}
|
||||
|
||||
if config.SqlDir == "" && params.SqlDir != "" {
|
||||
config.SqlDir = params.SqlDir
|
||||
}
|
||||
|
||||
err = fixConnections(&config)
|
||||
return config, err
|
||||
}
|
||||
|
||||
func fixConnections(config *Config) error {
|
||||
def := config.Defaults
|
||||
for i := range config.Connections {
|
||||
con := &config.Connections[i]
|
||||
|
||||
if con.DbName == "" {
|
||||
con.DbName = def.DbName
|
||||
}
|
||||
if con.User == "" {
|
||||
con.User = def.User
|
||||
}
|
||||
if con.Host == "" {
|
||||
con.Host = def.Host
|
||||
}
|
||||
if con.Port == 0 {
|
||||
if def.Port != 0 {
|
||||
con.Port = def.Port
|
||||
} else {
|
||||
con.Port = 5432 // domyślny dla PostgreSQL
|
||||
}
|
||||
}
|
||||
if con.DbName == "" || con.Host == "" || con.User == "" {
|
||||
return fmt.Errorf("opis połączenia z pozycji %d jest niekompletny: %#v", i+1, con)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
137
internal/cfg/params.go
Normal file
137
internal/cfg/params.go
Normal file
@ -0,0 +1,137 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const MULTISQLPASS = "MULTISQLPASS" // nazwa ziennej środowiskowej z hasłem
|
||||
|
||||
type Parameters struct {
|
||||
ConfigFile string
|
||||
Passfile string
|
||||
OutDir string
|
||||
SqlDir string
|
||||
LogFile string
|
||||
Verbose bool
|
||||
AskPass bool
|
||||
Version bool
|
||||
}
|
||||
|
||||
var params Parameters
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(¶ms.Verbose, "verbose", false, "Dodatkowe komunkaty diagnostyczne")
|
||||
flag.StringVar(¶ms.ConfigFile, "config", "multisql.conf", "Plik konfiguracji")
|
||||
flag.StringVar(¶ms.Passfile, "passfile", "", "Plik pgpass z hasłami do baz")
|
||||
flag.StringVar(¶ms.OutDir, "outdir", "", "Katalog (istniejący i z prawem do zapisu), do którego generowane są wyniki")
|
||||
flag.StringVar(¶ms.SqlDir, "sqldir", "scripts", "Katalog, w którym znajdują się skrypty do uruchomienia")
|
||||
flag.StringVar(¶ms.LogFile, "log", "", "Plik, do którego zostanie dopisany log programu")
|
||||
flag.BoolVar(¶ms.AskPass, "P", false, "Pytaj o hasło. Jeśli nie podane wymaga się hasła w ")
|
||||
flag.BoolVar(¶ms.Version, "version", false, "Wypisuje wersję i kończy działanie")
|
||||
|
||||
flag.Usage = printUsage
|
||||
flag.Parse()
|
||||
}
|
||||
func GetParams() Parameters {
|
||||
return params
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
`
|
||||
multisql - uruchamia zestaw skryptów na skonfigurowanej liście baz danych.
|
||||
|
||||
Program, dla każdej z baz wskazanych w konfiguracji, uruchamia kolejno każdy
|
||||
z plików sql wskazanych parametrem lub w pliku konfiguracji poprzez
|
||||
uruchomienie narzędzia psql:
|
||||
|
||||
psql -f skrypt.sql -w -L log.txt _szczegóły_połaczenia_ 2>stderr.txt > stdout.txt
|
||||
|
||||
Skrypty mogą mieć dowolne rozszerzenia ale podkatalogi nie są obsługiwane.
|
||||
Pliki wynikowe zapisywane są do katalogu wskazanego w outdir w strukturze o postaci
|
||||
output:
|
||||
-> data-godzina-pid
|
||||
-> host-port-baza-user
|
||||
-> skrypt.sql
|
||||
-> log.txt
|
||||
-> stdout.txt
|
||||
-> stderr.txt
|
||||
|
||||
|
||||
Użycie:
|
||||
multisql -outdir /tmp -sqldir /data/skrypty -passfile ./hasla
|
||||
|
||||
Użycie w trybie szyfrowania:
|
||||
multisql encrypt plik.wynikowy.zaszyfrowany plik.zródłowy
|
||||
lub
|
||||
multisql [-P] decrypt plik.źródłowy.zaszyfrowany [plik.wynikowy.jawny]
|
||||
|
||||
Opis flag:
|
||||
`,
|
||||
)
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintf(os.Stderr,
|
||||
`
|
||||
Plik passfile ma standardowy format .pgpass PostgreSQLa.
|
||||
|
||||
Plik multisql.conf ma format JSON, np:
|
||||
> `,
|
||||
)
|
||||
|
||||
c := Config{
|
||||
PassFile: "mypgpass",
|
||||
PsqlExec: "/usr/local/bin/psql",
|
||||
SqlDir: "/data/skrypty-sql",
|
||||
Defaults: Connection{
|
||||
Port: 5433,
|
||||
User: "myapp",
|
||||
DbName: "moja-db",
|
||||
},
|
||||
Connections: []Connection{
|
||||
{
|
||||
Host: "10.20.30.01",
|
||||
},
|
||||
{
|
||||
Host: "10.20.30.02",
|
||||
DbName: "innadb",
|
||||
User: "innyuser",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(&c)
|
||||
var out bytes.Buffer
|
||||
_ = json.Indent(&out, b, "> ", "\t")
|
||||
_, _ = out.WriteTo(os.Stderr)
|
||||
|
||||
fmt.Fprintf(os.Stderr, `
|
||||
|
||||
PsqlExec jest opcjonalny - jesli nie zostanie podany wyszukuje się programu psql.exe w PATH.
|
||||
|
||||
Każde połączenie w tablicy Connections może zawierać Host, Port, User i DbName.
|
||||
Brakujące wartości są uzupełniane z sekcji Defaults (która ma taki sam
|
||||
format jak obiekt w Connections).
|
||||
|
||||
Parametry mają pierwszeństwo przed wartościami w konfiguracji.
|
||||
|
||||
Format pliku passfile jest następujący:
|
||||
|
||||
hostname:port:database:username:password
|
||||
|
||||
W szczegółach opisano go na https://www.postgresql.org/docs/current/libpq-pgpass.html.
|
||||
|
||||
Aktualnie obsługiwana jest jedynie zaszyforowana postać pliku. Przed użyciem należy
|
||||
plik zaszyfrować:
|
||||
|
||||
multsql encrypt pgpass.encrypted pgpass
|
||||
|
||||
Przy użyciu (odszyfrowaniu) pliku, hasło jest pobierane ze zmiennej
|
||||
środowiskowej %s lub z klawiatury, jeśli użyto opcji -P.
|
||||
|
||||
`, MULTISQLPASS)
|
||||
|
||||
}
|
132
internal/mgr/mgr.go
Normal file
132
internal/mgr/mgr.go
Normal file
@ -0,0 +1,132 @@
|
||||
package mgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"baal.ar76.eu/x/pub/multisql/internal/cfg"
|
||||
"baal.ar76.eu/x/pub/multisql/internal/pass"
|
||||
"baal.ar76.eu/x/pub/multisql/internal/psql"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
config cfg.Config
|
||||
runDir string
|
||||
verbose bool
|
||||
askPass bool
|
||||
}
|
||||
|
||||
func Create(verbose bool, askPass bool, outdir string, config cfg.Config) (Manager, error) {
|
||||
runDir := createRunDir(outdir)
|
||||
|
||||
manager := Manager{
|
||||
config: config,
|
||||
runDir: runDir,
|
||||
verbose: verbose,
|
||||
askPass: askPass,
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func createRunDir(out string) string {
|
||||
t := time.Now()
|
||||
dataPart := t.Format("2006-01-02T15_04_05")
|
||||
newDir := fmt.Sprintf("%s-%d", dataPart, os.Getpid())
|
||||
dir := path.Join(out, newDir)
|
||||
return dir
|
||||
}
|
||||
|
||||
func (self Manager) GetScripts() ([]string, error) {
|
||||
entries, err := os.ReadDir(self.config.SqlDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("błąd wczytywania skryptów: %w", err)
|
||||
}
|
||||
scripts := make([]string, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
scrpt := path.Join(self.config.SqlDir, e.Name())
|
||||
_, err := os.Stat(scrpt)
|
||||
if err != nil {
|
||||
log.Printf("Skrypt zostanie pominięty z powodu błedu: %v\n", err)
|
||||
continue
|
||||
}
|
||||
scripts = append(scripts, scrpt)
|
||||
}
|
||||
return scripts, err
|
||||
}
|
||||
|
||||
func (self Manager) Run() error {
|
||||
err := os.Mkdir(self.runDir, 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd tworzenia katalogu z wynikami: %w", err)
|
||||
}
|
||||
scripts, err := self.GetScripts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var passdb *pass.PassDb
|
||||
|
||||
if self.config.PassFile != "" {
|
||||
password := pass.GetMasterPass(self.askPass)
|
||||
passdb, err = pass.Load(self.config.PassFile, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var group sync.WaitGroup
|
||||
|
||||
for _, con := range self.config.Connections {
|
||||
dbDir, err := self.createDirPerCon(con)
|
||||
if err != nil {
|
||||
log.Printf("Problem z utworzeniem katalogu wynikow dla połaczenie: %v", dbDir)
|
||||
log.Printf("Pomijam połaczenie")
|
||||
continue
|
||||
}
|
||||
|
||||
group.Add(1)
|
||||
stream := make(chan psql.Result)
|
||||
go self.Logger(&group, stream, con)
|
||||
|
||||
sql := psql.Create(
|
||||
passdb,
|
||||
dbDir,
|
||||
scripts,
|
||||
con,
|
||||
self.config.PsqlExec,
|
||||
self.verbose,
|
||||
)
|
||||
go sql.Exec(stream)
|
||||
}
|
||||
|
||||
group.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self Manager) createDirPerCon(con cfg.Connection) (string, error) {
|
||||
dbDir := fmt.Sprintf("%s_%d_%s_%s", con.Host, con.Port, con.DbName, con.User)
|
||||
dir := path.Join(self.runDir, dbDir)
|
||||
err := os.Mkdir(dir, 0o755)
|
||||
return dir, err
|
||||
}
|
||||
|
||||
func (self Manager) Logger(group *sync.WaitGroup, stream <-chan psql.Result, con cfg.Connection) {
|
||||
for event := range stream {
|
||||
if event.Err != nil {
|
||||
scr := path.Base(event.Script)
|
||||
log.Printf("%s:%d:%s:%s Skrypt: %s Błąd: %v\n", con.Host, con.Port, con.DbName, con.User, scr, event.Err)
|
||||
} else if self.verbose {
|
||||
scr := path.Base(event.Script)
|
||||
log.Printf("%s:%d:%s:%s Skrypt %s zakończony poprawnie\n", con.Host, con.Port, con.DbName, con.User, scr)
|
||||
}
|
||||
}
|
||||
group.Done()
|
||||
}
|
65
internal/pass/crypt.go
Normal file
65
internal/pass/crypt.go
Normal file
@ -0,0 +1,65 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/armor"
|
||||
)
|
||||
|
||||
func DecryptFile(password []byte, file string) ([]byte, error) {
|
||||
identity, err := age.NewScryptIdentity(string(password))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("błąd przetwarzania hasła: %w", err)
|
||||
}
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("błąd otwarcia pliku: %w", err)
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
in := armor.NewReader(fd)
|
||||
|
||||
r, err := age.Decrypt(in, identity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("błąd deszyfrowania #01: %w", err)
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
if _, err := io.Copy(out, r); err != nil {
|
||||
return nil, fmt.Errorf("błąd deszyfrowania #02: %v", err)
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func EncryptFile(password []byte, file string, data []byte) error {
|
||||
recipient, err := age.NewScryptRecipient(string(password))
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd przetwarzania hasła: %w", err)
|
||||
}
|
||||
fd, err := os.OpenFile(file, os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd tworzenia pliku: %w", err)
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
out := armor.NewWriter(fd) // armor
|
||||
defer out.Close()
|
||||
|
||||
w, err := age.Encrypt(out, recipient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd szyfrowania #01: %w", err)
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd szyfrowania #02: %w", err)
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("błąd szyfrowania #03: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
80
internal/pass/pass.go
Normal file
80
internal/pass/pass.go
Normal file
@ -0,0 +1,80 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type pgpassrow struct {
|
||||
hostname string
|
||||
port string
|
||||
database string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type PassDb struct {
|
||||
db []pgpassrow
|
||||
cache map[string]*pgpassrow
|
||||
}
|
||||
|
||||
func Load(file string, master []byte) (*PassDb, error) {
|
||||
|
||||
data, err := DecryptFile(master, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := bytes.Split(data, []byte{'\n'})
|
||||
|
||||
var rows []pgpassrow
|
||||
|
||||
for i, line := range lines {
|
||||
line = bytes.TrimRight(line, "\n\r \t")
|
||||
if len(line) == 0 || line[0] == byte('#') {
|
||||
continue
|
||||
}
|
||||
fields := bytes.Split(line, []byte{':'})
|
||||
if len(fields) != 5 {
|
||||
return nil, fmt.Errorf("błąd w pliku hasłeł %s:%d: niewłaściwa liczba pól", file, i)
|
||||
}
|
||||
rows = append(rows, pgpassrow{
|
||||
hostname: string(fields[0]),
|
||||
port: string(fields[1]),
|
||||
database: string(fields[2]),
|
||||
username: string(fields[3]),
|
||||
password: string(fields[4]),
|
||||
})
|
||||
}
|
||||
|
||||
return &PassDb{
|
||||
db: rows,
|
||||
cache: make(map[string]*pgpassrow),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func match(value string, pattern string) bool {
|
||||
return pattern == "*" || pattern == value
|
||||
}
|
||||
|
||||
func (self *PassDb) FindPassword(host string, port uint, database string, user string) string {
|
||||
myport := fmt.Sprintf("%d", port)
|
||||
key := fmt.Sprintf("%s:%s:%s:%s", host, myport, database, user)
|
||||
cached, ok := self.cache[key]
|
||||
if ok {
|
||||
return cached.password
|
||||
}
|
||||
|
||||
// pierwszy pasujący do wzorca
|
||||
for i := range self.db {
|
||||
if match(host, self.db[i].hostname) &&
|
||||
match(myport, self.db[i].port) &&
|
||||
match(database, self.db[i].database) &&
|
||||
match(user, self.db[i].username) {
|
||||
|
||||
self.cache[key] = &self.db[i]
|
||||
return self.db[i].password
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
55
internal/pass/term.go
Normal file
55
internal/pass/term.go
Normal file
@ -0,0 +1,55 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"baal.ar76.eu/x/pub/multisql/internal/cfg"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func GetMasterPass(askPass bool) []byte {
|
||||
if askPass {
|
||||
res, err := termGetPassword("Podaj hasło")
|
||||
if err != nil {
|
||||
log.Fatalln("Nie udało się wczytać hasła z konsoli")
|
||||
}
|
||||
return res
|
||||
}
|
||||
password := os.Getenv(cfg.MULTISQLPASS)
|
||||
|
||||
if password != "" {
|
||||
return []byte(password)
|
||||
}
|
||||
|
||||
log.Fatalf("Nieustalone hasło. Użyj flagi -P lub ustaw hasło w zmiennej %s", cfg.MULTISQLPASS)
|
||||
return nil
|
||||
}
|
||||
|
||||
func EnterMasterPass() []byte {
|
||||
p1, err := termGetPassword("Podaj nowe hasło (min 8 znaków)")
|
||||
if err != nil {
|
||||
log.Fatalf("Błąd wczytywania hasła: %v", err)
|
||||
}
|
||||
if len(p1) < 8 {
|
||||
log.Fatalf("podano zbyt krótkie hasło")
|
||||
}
|
||||
p2, err := termGetPassword("Powtórz nowe hasło")
|
||||
if err != nil {
|
||||
log.Fatalf("Błąd wczytywania hasła: %v", err)
|
||||
}
|
||||
if !bytes.Equal(p1, p2) {
|
||||
log.Fatalf("podane hasła są różne")
|
||||
}
|
||||
|
||||
return p1
|
||||
}
|
||||
|
||||
func termGetPassword(prompt string) ([]byte, error) {
|
||||
fmt.Fprintf(os.Stderr, "%s: ", prompt)
|
||||
passwd, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Fprintln(os.Stderr)
|
||||
return passwd, err
|
||||
}
|
121
internal/psql/psql.go
Normal file
121
internal/psql/psql.go
Normal file
@ -0,0 +1,121 @@
|
||||
package psql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"baal.ar76.eu/x/pub/multisql/internal/cfg"
|
||||
"baal.ar76.eu/x/pub/multisql/internal/pass"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
outdir string
|
||||
scripts []string
|
||||
pasdb *pass.PassDb
|
||||
psqlExe string
|
||||
conn cfg.Connection
|
||||
verbose bool
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Script string
|
||||
Err error
|
||||
}
|
||||
|
||||
func Create(passdb *pass.PassDb, outdir string, scripts []string, conn cfg.Connection, psqlExe string, verbose bool) (instnace Instance) {
|
||||
|
||||
instnace = Instance{
|
||||
outdir: outdir,
|
||||
scripts: scripts,
|
||||
pasdb: passdb,
|
||||
conn: conn,
|
||||
psqlExe: psqlExe,
|
||||
verbose: verbose,
|
||||
}
|
||||
return instnace
|
||||
}
|
||||
|
||||
func (self Instance) Exec(result chan<- Result) {
|
||||
|
||||
|
||||
connString := fmt.Sprintf("host=%s port=%d dbname=%s user=%s", self.conn.Host, self.conn.Port, self.conn.DbName, self.conn.User)
|
||||
|
||||
for _, scr := range self.scripts {
|
||||
srcName := path.Base(scr)
|
||||
outdir := path.Join(self.outdir, srcName)
|
||||
err := os.Mkdir(outdir, 0o775)
|
||||
if err != nil {
|
||||
result <- Result{
|
||||
Script: scr,
|
||||
Err: err,
|
||||
}
|
||||
continue
|
||||
}
|
||||
password := self.pasdb.FindPassword(self.conn.Host, self.conn.Port, self.conn.DbName, self.conn.User)
|
||||
err = self.cmdPsql(outdir, connString, scr, password)
|
||||
result <- Result{
|
||||
Script: scr,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
close(result)
|
||||
}
|
||||
|
||||
func (self Instance) cmdPsql(outdir string, connString string, script string, password string) error {
|
||||
absScript, err := filepath.Abs(script)
|
||||
if err != nil {
|
||||
return fmt.Errorf("problem ze zbudowaniem ściezki do skryptu: %w", err)
|
||||
}
|
||||
psqlExe := "psql"
|
||||
if runtime.GOOS == "windows" {
|
||||
psqlExe = "psql.exe"
|
||||
}
|
||||
if self.psqlExe != "" {
|
||||
psqlExe = self.psqlExe
|
||||
}
|
||||
stdoutFile := path.Join(outdir, "stdout.txt")
|
||||
stderrFile := path.Join(outdir, "stderr.txt")
|
||||
logFile := "log.txt"
|
||||
|
||||
fhOut, err := os.Create(stdoutFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Problem utworzenie pliku wyników: %v", err)
|
||||
}
|
||||
defer fhOut.Close()
|
||||
|
||||
fhErr, err := os.Create(stderrFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Problem utworzenie pliku błędów: %v", err)
|
||||
}
|
||||
defer fhErr.Close()
|
||||
|
||||
cmd := exec.Command(psqlExe,
|
||||
"-w",
|
||||
"-f", absScript,
|
||||
"-L", logFile,
|
||||
connString,
|
||||
)
|
||||
cmd.Stderr = fhErr
|
||||
cmd.Stdout = fhOut
|
||||
cmd.Dir = outdir
|
||||
|
||||
if password != "" {
|
||||
env := os.Environ()
|
||||
env = append(env, "PGPASSWORD="+password)
|
||||
cmd.Env = env
|
||||
}
|
||||
|
||||
if self.verbose {
|
||||
log.Printf("Uruchamiam %s", cmd.String())
|
||||
}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Błąd uruchamiania psql: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user