From a64c02cd78e2bff39ede8bbacf731dafdd655d1c Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Sun, 4 Sep 2022 14:01:01 +0200 Subject: [PATCH] internal: Improve error message consistency --- cmd/blind_update.go | 2 +- cmd/system_update.go | 40 ++++++++++++++++++++------------------ env/stack_sql.go | 41 +++++++++++++-------------------------- env/stack_triplestore.go | 16 +++++++-------- internal/execx/compose.go | 11 ----------- internal/execx/look.go | 15 ++++++++++++++ internal/stack/stack.go | 18 +++++++++++++---- 7 files changed, 72 insertions(+), 71 deletions(-) delete mode 100644 internal/execx/compose.go create mode 100644 internal/execx/look.go diff --git a/cmd/blind_update.go b/cmd/blind_update.go index a72734b..18fc5fd 100644 --- a/cmd/blind_update.go +++ b/cmd/blind_update.go @@ -7,7 +7,7 @@ import ( "github.com/tkw1536/goprogram/exit" ) -// BlindUpdate is the 'blind-update' command +// BlindUpdate is the 'blind_update' command var BlindUpdate wisski_distillery.Command = blindUpdate{} type blindUpdate struct { diff --git a/cmd/system_update.go b/cmd/system_update.go index f6a7101..74cf883 100644 --- a/cmd/system_update.go +++ b/cmd/system_update.go @@ -53,16 +53,26 @@ func (s systemupdate) AfterParse() error { return nil } -var errFailedToCreateDirectory = exit.Error{ +var errBoostrapFailedToCreateDirectory = exit.Error{ Message: "failed to create directory %s: %s", ExitCode: exit.ExitGeneric, } -var errFailedRuntime = exit.Error{ +var errBootstrapFailedRuntime = exit.Error{ Message: "failed to update runtime: %s", ExitCode: exit.ExitGeneric, } +var errBootstrapTriplestore = exit.Error{ + Message: "Unable to bootstrap Triplestore: %s", + ExitCode: exit.ExitGeneric, +} + +var errBootstrapSQL = exit.Error{ + Message: "Unable to bootstrap SQL: %s", + ExitCode: exit.ExitGeneric, +} + func (si systemupdate) Run(context wisski_distillery.Context) error { dis := context.Environment @@ -76,7 +86,7 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { } { context.Println(d) if err := os.MkdirAll(d, os.ModeDir); err != nil { - return errFailedToCreateDirectory.WithMessageF(d, err) + return errBoostrapFailedToCreateDirectory.WithMessageF(d, err) } } @@ -95,6 +105,7 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { if err := si.mustExec(context, "", "apt-get", "install", "curl"); err != nil { return err } + // TODO: Download directly if err := si.mustExec(context, "", "/bin/sh", "-c", "curl -fsSL https://get.docker.com -o - | /bin/sh"); err != nil { return err } @@ -130,32 +141,23 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { } if err := logging.LogOperation(func() error { - if err := distillery.InstallResource(dis.RuntimeDir(), filepath.Join("resources", "runtime"), func(dst, src string) { + return distillery.InstallResource(dis.RuntimeDir(), filepath.Join("resources", "runtime"), func(dst, src string) { context.Printf("[copy] %s\n", dst) - }); err != nil { - return errFailedRuntime.WithMessageF(err) - } - return nil + }) }, context.IOStream, "Unpacking Runtime Components"); err != nil { - return err + return errBootstrapFailedRuntime.WithMessageF(err) } if err := logging.LogOperation(func() error { - if err := dis.SQLBootstrap(context.IOStream); err != nil { - return err - } - return nil + return dis.SQLBootstrap(context.IOStream) }, context.IOStream, "Bootstraping SQL database"); err != nil { - return err + return errBootstrapSQL.WithMessageF(err) } if err := logging.LogOperation(func() error { - if err := dis.TriplestoreBootstrap(context.IOStream); err != nil { - return err - } - return nil + return dis.TriplestoreBootstrap(context.IOStream) }, context.IOStream, "Bootstraping Triplestore"); err != nil { - return err + return errBootstrapTriplestore.WithMessageF(err) } logging.LogMessage(context.IOStream, "System has been updated") diff --git a/env/stack_sql.go b/env/stack_sql.go index 7c1b0a6..e17a907 100644 --- a/env/stack_sql.go +++ b/env/stack_sql.go @@ -11,7 +11,6 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/stack" "github.com/FAU-CDI/wisski-distillery/internal/wait" "github.com/pkg/errors" - "github.com/tkw1536/goprogram/exit" "github.com/tkw1536/goprogram/stream" "gorm.io/driver/mysql" "gorm.io/gorm" @@ -54,11 +53,6 @@ func (env Distillery) sqlOpen(database string, config *gorm.Config) (*gorm.DB, e return db, nil } -var errSQL = exit.Error{ - Message: "error querying sql database: %s", - ExitCode: exit.ExitGeneric, -} - // sqlBkTable returns a gorm connection to the bookkeeping database. func (dis *Distillery) sqlBkTable(silent bool) (*gorm.DB, error) { @@ -70,13 +64,13 @@ func (dis *Distillery) sqlBkTable(silent bool) (*gorm.DB, error) { // open the database db, err := dis.sqlOpen(dis.Config.DistilleryBookkeepingDatabase, config) if err != nil { - return nil, errSQL.WithMessageF(err) + return nil, err } // load the table table := db.Table(dis.Config.DistilleryBookkeepingTable) if table.Error != nil { - return nil, errSQL.WithMessageF(err) + return nil, err } return table, nil @@ -87,11 +81,6 @@ func (dis *Distillery) SQLShell(io stream.IOStream, argv ...string) (int, error) return dis.SQLStack().Exec(io, "sql", "mysql", argv...) } -var errSQLBootstrap = exit.Error{ - Message: "Unable to boostrap SQL: %s", - ExitCode: exit.ExitGeneric, -} - const waitSQLInterval = 1 * time.Second // SQLWaitForShell waits for the sql database to be reachable via a docker-compose shell @@ -140,10 +129,7 @@ func (dis *Distillery) SQLProvision(name, user, password string) error { return nil } -var errSQLPurgeUser = exit.Error{ - Message: "Unable to delete user", - ExitCode: exit.ExitGeneric, -} +var errSQLPurgeUser = errors.New("unable to delete user") // SQLPurgeUser deletes the specified user from the database func (dis *Distillery) SQLPurgeUser(user string) error { @@ -154,10 +140,7 @@ func (dis *Distillery) SQLPurgeUser(user string) error { return nil } -var errSQLPurgeDB = exit.Error{ - Message: "Unable to delete database", - ExitCode: exit.ExitGeneric, -} +var errSQLPurgeDB = errors.New("unable to drop database") // SQLPurgeDatabase deletes the specified db from the database func (dis *Distillery) SQLPurgeDatabase(db string) error { @@ -170,10 +153,14 @@ func (dis *Distillery) SQLPurgeDatabase(db string) error { return nil } +var errSQLUnableToCreateUser = errors.New("unable to create administrative user") +var errSQLUnsafeDatabaseName = errors.New("Bookkeeping database has an unsafe name") +var errSQLUnableToCreate = errors.New("unable to create bookkeeping database") + // SQLBootstrap bootstraps the SQL database, and makes sure that the bookkeeping table is up-to-date func (dis *Distillery) SQLBootstrap(io stream.IOStream) error { if err := dis.SQLWaitForShell(); err != nil { - return errSQLBootstrap.WithMessageF(err) + return err } // create the admin user @@ -182,7 +169,7 @@ func (dis *Distillery) SQLBootstrap(io stream.IOStream) error { username := dis.Config.MysqlAdminUser password := dis.Config.MysqlAdminPassword if !dis.sqlRaw("CREATE USER IF NOT EXISTS ?@'%' IDENTIFIED BY ?; GRANT ALL PRIVILEGES ON *.* TO ?@`%` WITH GRANT OPTION; FLUSH PRIVILEGES;", username, password, username) { - return errSQLBootstrap.WithMessageF("Unable to create administrative user") + return errSQLUnableToCreateUser } } @@ -190,11 +177,11 @@ func (dis *Distillery) SQLBootstrap(io stream.IOStream) error { logging.LogMessage(io, "Creating sql database") { if !sqle.IsSafeDatabaseName(dis.Config.DistilleryBookkeepingDatabase) { - return errSQLBootstrap.WithMessageF("Unsafe database name") + return errSQLUnsafeDatabaseName } createDBSQL := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`;", dis.Config.DistilleryBookkeepingDatabase) if !dis.sqlRaw(createDBSQL) { - return errSQLBootstrap.WithMessageF(createDBSQL) + return errSQLUnableToCreate } } @@ -207,11 +194,11 @@ func (dis *Distillery) SQLBootstrap(io stream.IOStream) error { { db, err := dis.sqlBkTable(false) if err != nil { - return errSQLBootstrap.WithMessageF(err) + return fmt.Errorf("unable to access bookkeeping table: %s", err) } if err := db.AutoMigrate(&bookkeeping.Instance{}); err != nil { - return errSQLBootstrap.WithMessageF(err) + return fmt.Errorf("unable to migrate bookkeeping table: %s", err) } } diff --git a/env/stack_triplestore.go b/env/stack_triplestore.go index d8027c9..85e4d58 100644 --- a/env/stack_triplestore.go +++ b/env/stack_triplestore.go @@ -3,6 +3,7 @@ package env import ( "bytes" "encoding/json" + "fmt" "io" "io/fs" "mime/multipart" @@ -49,11 +50,6 @@ type TriplestoreUserAppSettings struct { ExecuteCount bool `json:"EXECUTE_COUNT"` } -var errTriplestoreBootstrap = exit.Error{ - Message: "Unable to bootstrap Triplestore: %s", - ExitCode: exit.ExitGeneric, -} - const triplestoreBaseURL = "http://127.0.0.1:7200" const waitTSInterval = 1 * time.Second @@ -204,6 +200,8 @@ func (dis *Distillery) TriplestorePurgeRepo(repo string) error { return nil } +var errTriplestoreFailedSecurity = errors.New("failed to enable triplestore security: request did not succeed with HTTP 200 OK") + func (dis *Distillery) TriplestoreBootstrap(io stream.IOStream) error { logging.LogMessage(io, "Waiting for Triplestore") if err := dis.TriplestoreWaitForConnection(); err != nil { @@ -224,7 +222,7 @@ func (dis *Distillery) TriplestoreBootstrap(io stream.IOStream) error { GrantedAuthorities: []string{"ROLE_ADMIN"}, }, "", "") if err != nil { - return errTriplestoreBootstrap.WithMessageF(err) + return fmt.Errorf("failed to create triplestore user: %s", err) } defer res.Body.Close() @@ -238,7 +236,7 @@ func (dis *Distillery) TriplestoreBootstrap(io stream.IOStream) error { logging.LogMessage(io, "Security is already enabled") return nil default: - return errTriplestoreBootstrap.WithMessageF("Unable to set administrative password") + return fmt.Errorf("failed to create triplestore user: %s", err) } } @@ -246,12 +244,12 @@ func (dis *Distillery) TriplestoreBootstrap(io stream.IOStream) error { { res, err := dis.triplestoreRequest("POST", "/rest/security", true, "", "") if err != nil { - return errTriplestoreBootstrap.WithMessageF(err) + return fmt.Errorf("failed to enable triplestore security: %s", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return errTriplestoreBootstrap.WithMessageF("Unable to enable security") + return errTriplestoreFailedSecurity } return nil diff --git a/internal/execx/compose.go b/internal/execx/compose.go deleted file mode 100644 index 1601b20..0000000 --- a/internal/execx/compose.go +++ /dev/null @@ -1,11 +0,0 @@ -package execx - -import ( - "github.com/tkw1536/goprogram/stream" -) - -// Compose runs a docker-compose command in a specific directory, with the provided arguments and streams. -// It then waits for the process to exit, and returns the exit code. -func Compose(io stream.IOStream, workdir string, args ...string) int { - return Exec(io, workdir, "docker", append([]string{"compose"}, args...)...) -} diff --git a/internal/execx/look.go b/internal/execx/look.go new file mode 100644 index 0000000..e816519 --- /dev/null +++ b/internal/execx/look.go @@ -0,0 +1,15 @@ +package execx + +import ( + "os/exec" + "path/filepath" +) + +// LookPathAbs is like [exec.LookPath], but always returns an absolute path +func LookPathAbs(file string) (string, error) { + path, err := exec.LookPath(file) + if err != nil { + return "", err + } + return filepath.Abs(path) +} diff --git a/internal/stack/stack.go b/internal/stack/stack.go index 81a9f84..8a4b19f 100644 --- a/internal/stack/stack.go +++ b/internal/stack/stack.go @@ -15,6 +15,8 @@ import ( // In the future the idea is to replace this with a native docker compose client. type Stack struct { Dir string // Directory this Stack is located in + + DockerExecutable string // Path to the native docker executable to use } var errStackUpdatePull = errors.New("Stack.Update: Pull returned non-zero exit code") @@ -131,9 +133,17 @@ func (ds Stack) Down(io stream.IOStream) error { return nil } -// Compose executes a 'docker compose' command on this stack. -// TODO: This should be removed and replaced by an internal call directly to libcompose. +// compose executes a 'docker compose' command on this stack. +// +// NOTE(twiesing): Check if this can be replaced by an internal call to libcompose. +// But probably not. func (ds Stack) compose(io stream.IOStream, args ...string) (int, error) { - // TODO: can we migrate to a built-in version of this? - return execx.Compose(io, ds.Dir, args...), nil + if ds.DockerExecutable == "" { + var err error + ds.DockerExecutable, err = execx.LookPathAbs("docker") + if err != nil { + return execx.ExecCommandError, err + } + } + return execx.Exec(io, ds.Dir, ds.DockerExecutable, append([]string{"compose"}, args...)...), nil }