taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

commit 865fc61f06157597ce463e22b9b242574214da3d
parent ebb0b8205b01574ca66db7198c8a9a11f22c7b97
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Sat, 14 Feb 2026 18:38:48 +0100

Add dbinit

Diffstat:
MMakefile.in | 4++++
Acmd/taldir-dbinit/main.go | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcmd/taldir-server/main_test.go | 81+------------------------------------------------------------------------------
Mpkg/taldir/db.go | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 207 insertions(+), 80 deletions(-)

diff --git a/Makefile.in b/Makefile.in @@ -11,6 +11,7 @@ server: tools: ${GO} build -o taler-directory-cli -ldflags "-X main.version=${VERSION} -X main.taldirdatahome=${TALER_DIRECTORY_HOME} -X main.taldirconfdir=${TALER_DIRECTORY_CONFDIR}" ./cmd/taldir-cli ${GO} build -o taler-directory-config -ldflags "-X main.version=${VERSION} -X main.taldirdatahome=${TALER_DIRECTORY_HOME} -X main.taldirconfdir=${TALER_DIRECTORY_CONFDIR}" ./cmd/taldir-config + ${GO} build -o taler-directory-dbinit -ldflags "-X main.version=${VERSION} -X main.taldirdatahome=${TALER_DIRECTORY_HOME} -X main.taldirconfdir=${TALER_DIRECTORY_CONFDIR}" ./cmd/taldir-dbinit install: server tools @@ -19,8 +20,10 @@ install: server tools install ./taler-directory ${DESTDIR}${bindir} install ./taler-directory-cli ${DESTDIR}${bindir} install ./taler-directory-config ${DESTDIR}${bindir} + install ./taler-directory-dbinit ${DESTDIR}${bindir} cp -r ./web ${DESTDIR}${TALER_DIRECTORY_HOME}/ cp -r ./static ${DESTDIR}${TALER_DIRECTORY_HOME}/ + cp -r ./sql ${DESTDIR}${TALER_DIRECTORY_HOME}/ chmod +x scripts/validators/* cp scripts/validators/* ${DESTDIR}${bindir} -mkdir -p ${DESTDIR}${mandir}/man1 @@ -35,6 +38,7 @@ format: ${GO} fmt ./cmd/taldir-server/*.go ${GO} fmt ./cmd/taldir-cli/*.go ${GO} fmt ./cmd/taldir-config/*.go + ${GO} fmt ./cmd/taldir-dbinit/*.go ${GO} fmt ./pkg/taldir/*.go check: diff --git a/cmd/taldir-dbinit/main.go b/cmd/taldir-dbinit/main.go @@ -0,0 +1,122 @@ +// This file is part of taldir, the Taler Directory implementation. +// Copyright (C) 2025 Martin Schanzenbach +// +// Taldir is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// Taldir is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package main + +import ( + "database/sql" + "flag" + "fmt" + "log" + "os" + "path" + + "rsc.io/getopt" + "taler.net/taldir/pkg/taldir" + + "gopkg.in/ini.v1" +) + +var ( + taldirdatahome string + taldirconfdir string +) + +func printHelp() { + fmt.Print("taler-directory-dbinit\n\n") + getopt.PrintDefaults() + fmt.Print("\nReport bugs to gnunet-developers@gnu.org.\n", + "Home page: https://taler.net\n", + "General help using GNU software: http://www.gnu.org/gethelp/\n") +} + +func printKey(key *ini.Key, onlyValue bool) { + if onlyValue { + fmt.Printf("%s\n", key.String()) + return + } + fmt.Printf("%s = %s\n", key.Name(), key.String()) +} + +func printCfgOptions(sec *ini.Section, option *string, onlyValue *bool) { + if len(*option) == 0 { + for _, key := range sec.Keys() { + printKey(key, *onlyValue) + } + return + } + if !sec.HasKey(*option) { + fmt.Printf("Section `%s' does not have option `%s'!\n", sec.Name(), *option) + os.Exit(1) + } + key := sec.Key(*option) + printKey(key, *onlyValue) +} + +func printCfgSections(f *ini.File) { + for _, sec := range f.Sections() { + fmt.Println(sec.Name()) + } +} + +func main() { + var cfg *ini.File + var err error + var cfgFlag = flag.String("c", "", "Configuration file to use") + getopt.Alias("c", "config") + var helpFlag = flag.Bool("h", false, "Print help") + getopt.Alias("h", "help") + + getopt.Parse() + if *helpFlag { + printHelp() + return + } + cfgfile := path.Join(taldirconfdir, "taldir.conf") + if len(*cfgFlag) != 0 { + cfg, err = ini.Load(*cfgFlag) + if err != nil { + fmt.Printf("Failed to read config: %v\n", err) + os.Exit(1) + } + } else { + // FIXME also try in datahome + cfg, err = ini.LooseLoad(cfgfile) + if err != nil { + fmt.Printf("Failed to read config: %v\n", err) + os.Exit(1) + } + } + dbName := cfg.Section("taldir-pq").Key("db_name").MustString("taldir") + psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", + cfg.Section("taldir-pq").Key("host").MustString("localhost"), + cfg.Section("taldir-pq").Key("port").MustInt64(5432), + cfg.Section("taldir-pq").Key("user").MustString("taldir"), + cfg.Section("taldir-pq").Key("password").MustString("secret"), + dbName) + + db, err := sql.Open("postgres", psqlconn) + if err != nil { + log.Panic(err) + } + defer db.Close() + err = taldir.DBInit(db, taldirdatahome, dbName) + if err != nil { + log.Fatalf("%v\n", err) + } +} diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go @@ -27,7 +27,6 @@ import ( "net/http" "net/http/httptest" "os" - "os/exec" "strings" "testing" @@ -98,84 +97,6 @@ const merchantConfigResponse = `{ "version": "18:0:15" }` -func CheckVersioning(db *sql.DB) (bool, error) { - rows, err := db.Query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name='_v';`) - if err != nil { - return false, err - } - defer rows.Close() - if rows.Next() { - fmt.Println("Versioning applied") - return true, nil - } - fmt.Printf("Versioning not applied: %v", rows.Err()) - return false, nil -} - -func CheckPatch(db *sql.DB, patchName string) (bool, error) { - rows, err := db.Query(`SELECT applied_by FROM _v.patches WHERE patch_name=$1 LIMIT 1;`, patchName) - if err != nil { - return false, err - } - defer rows.Close() - if rows.Next() { - return true, nil - } - return false, nil -} - -func RunSQL(db *sql.DB, patchName string) error { - path, err := exec.LookPath("psql") - if err != nil { - return err - } - out, err := exec.Command(path, "taler-directory", "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1").Output() - // FIXME logger - fmt.Printf("Executing `%s %s %s %s %s %s %s`\n", path, "taler-directory", "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1") - if err != nil { - fmt.Printf("%s, %v\n", out, err) - return err - } - return nil - -} - -func DBInit(db *sql.DB) error { - applied, err := CheckVersioning(db) - loadSuffix := "../../sql/" - if err != nil { - fmt.Printf("%v\n", err) - } - if !applied { - err := RunSQL(db, fmt.Sprintf("%s%s", loadSuffix, "versioning.sql")) - if err != nil { - return err - } - } - for i := range 10000 { - patchName := fmt.Sprintf("taler-directory-%04d", i+1) - applied, err := CheckPatch(db, patchName) - if err != nil { - return err - } - if applied { - fmt.Printf("Patch %s already applied\n", patchName) - continue - } - patchFile := fmt.Sprintf("%s%s.sql", loadSuffix, patchName) - if _, err := os.Stat(patchFile); err != nil { - fmt.Printf("Patch %s not found, up-to-date.\n", patchFile) - break - } - fmt.Printf("Applying patch %s\n", patchName) - err = RunSQL(db, patchFile) - if err != nil { - return err - } - } - return nil -} - func TestMain(m *testing.M) { cfg, err := ini.LooseLoad("testdata/taldir-test.conf") if err != nil { @@ -191,7 +112,7 @@ func TestMain(m *testing.M) { log.Panic(err) } defer db.Close() - err = DBInit(db) + err = taldir.DBInit(db, "../..", "taler-directory") if err != nil { log.Fatalf("Failed to apply versioning or patches: %v", err) } diff --git a/pkg/taldir/db.go b/pkg/taldir/db.go @@ -23,6 +23,9 @@ import ( "database/sql" "errors" "fmt" + "os" + "os/exec" + "path/filepath" "time" ) @@ -494,3 +497,80 @@ func InsertEntryIntoDatabase(db *sql.DB, e *Entry) error { defer rows.Close() return nil } + +func CheckVersioning(db *sql.DB) (bool, error) { + rows, err := db.Query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name='_v';`) + if err != nil { + return false, err + } + defer rows.Close() + if rows.Next() { + fmt.Println("Versioning applied") + return true, nil + } + return false, nil +} + +func CheckPatch(db *sql.DB, patchName string) (bool, error) { + rows, err := db.Query(`SELECT applied_by FROM _v.patches WHERE patch_name=$1 LIMIT 1;`, patchName) + if err != nil { + return false, err + } + defer rows.Close() + if rows.Next() { + return true, nil + } + return false, nil +} + +func RunSQL(db *sql.DB, patchName string, dbName string) error { + path, err := exec.LookPath("psql") + if err != nil { + return err + } + _, err = exec.Command(path, dbName, "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1").Output() + fmt.Printf("Running: %s %s %s %s %s %s %s\n", path, dbName, "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1") + if err != nil { + return err + } + return nil + +} + +func DBInit(db *sql.DB, datahome string, dbName string) error { + applied, err := CheckVersioning(db) + loadSuffix := filepath.Join(datahome, "sql") + if err != nil { + fmt.Printf("%v\n", err) + } + if !applied { + err := RunSQL(db, filepath.Join(loadSuffix, "versioning.sql"), dbName) + if err != nil { + return err + } + } + for i := range 10000 { + patchName := fmt.Sprintf("taler-directory-%04d", i+1) + applied, err := CheckPatch(db, patchName) + if err != nil { + return err + } + if applied { + fmt.Printf("Patch %s already applied\n", patchName) + continue + } + patchFile := fmt.Sprintf("%s.sql", filepath.Join(loadSuffix, patchName)) + if _, err := os.Stat(patchFile); err != nil { + fmt.Printf("Patch %s not found, up-to-date.\n", patchFile) + break + } + fmt.Printf("Applying patch %s\n", patchName) + err = RunSQL(db, patchFile, dbName) + if err != nil { + return err + } + } + return nil +} + +