From 2a596d9515d9fbeca7d802de27d579cd48ed445d Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Wed, 20 Jan 2021 23:01:46 -0500 Subject: [PATCH] Tidy up user experience and log messages during first install. Simplify generated passwords. --- db/connections.go | 2 +- db/install.go | 76 ++++++++++++++++++++++++++++++---------------- db/password.go | 52 ++++++++----------------------- db/stop.go | 2 +- go.mod | 4 +-- go.sum | 2 ++ ociinstaller/db.go | 3 -- utils/spinner.go | 2 +- 8 files changed, 69 insertions(+), 74 deletions(-) diff --git a/db/connections.go b/db/connections.go index 6c5574b58..d526d4a97 100644 --- a/db/connections.go +++ b/db/connections.go @@ -39,7 +39,7 @@ func refreshConnections(client *Client) error { numUpdates := len(updates.Update) if numUpdates > 0 { - s := utils.ShowSpinner("Refreshing connections") + s := utils.ShowSpinner("Refreshing connections...") defer utils.StopSpinner(s) connectionQueries = getSchemaQueries(updates.Update) diff --git a/db/install.go b/db/install.go index b324a41f9..6080e3661 100644 --- a/db/install.go +++ b/db/install.go @@ -36,64 +36,88 @@ func EnsureDBInstalled() { } log.Println("[TRACE] calling killPreviousInstanceIfAny") + killPreviousSpinner := utils.ShowSpinner(fmt.Sprintf("Cleanup any Steampipe processes...")) killPreviousInstanceIfAny() log.Println("[TRACE] calling removeRunningInstanceInfo") err := removeRunningInstanceInfo() + utils.StopSpinner(killPreviousSpinner) if err != nil && !os.IsNotExist(err) { - utils.FailOnErrorWithMessage(err, "Installation failed") + utils.FailOnErrorWithMessage(err, "x Cleanup any Steampipe processes... FAILED!") } log.Println("[TRACE] removing previous installation") + dbCleanupSpinner := utils.ShowSpinner(fmt.Sprintf("Prepare database install location...")) err = os.RemoveAll(getDatabaseLocation()) if err != nil { - utils.FailOnErrorWithMessage(err, "Installation failed") + utils.StopSpinner(dbCleanupSpinner) + utils.FailOnErrorWithMessage(err, "x Prepare database install location... FAILED!") } err = os.RemoveAll(getDataLocation()) if err != nil { - utils.FailOnErrorWithMessage(err, "Installation failed") + utils.StopSpinner(dbCleanupSpinner) + utils.FailOnErrorWithMessage(err, "x Prepare database install location... FAILED!") } + utils.StopSpinner(dbCleanupSpinner) - fmt.Printf("\nInstalling dbClient from image: %s\n", constants.DefaultEmbeddedPostgresImage) + dbInstallSpinner := utils.ShowSpinner(fmt.Sprintf("Download & install embedded PostgreSQL database...")) dbImageDigest, err := ociinstaller.InstallDB(constants.DefaultEmbeddedPostgresImage, getDatabaseLocation()) - utils.FailOnErrorWithMessage(err, "dbClient Installation failed") - - fmt.Printf("\nInstalling hub extension from image: %s\n", constants.DefaultFdwImage) - fdwDigest, err := ociinstaller.InstallFdw(constants.DefaultFdwImage, getDatabaseLocation()) - utils.FailOnErrorWithMessage(err, "Hub extension installation failed") - - fmt.Println("Initializing SQL Support...") - err = initDatabase() + utils.StopSpinner(dbInstallSpinner) if err != nil { - fmt.Printf("%v\n", err) + utils.FailOnErrorWithMessage(err, "x Download & install embedded PostgreSQL database... FAILED!") } - utils.FailOnError(err) - fmt.Println("Initializing steampipe Database...") - steampipePassword := generatePassword(64, 8, 8, 8) - rootPassword := generatePassword(64, 8, 8, 8) + fdwInstallSpinner := utils.ShowSpinner(fmt.Sprintf("Download & install Steampipe PostgreSQL FDW...")) + fdwDigest, err := ociinstaller.InstallFdw(constants.DefaultFdwImage, getDatabaseLocation()) + utils.StopSpinner(fdwInstallSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Download & install Steampipe Postgres FDW... FAILED!") + } + dbInitSpinner := utils.ShowSpinner(fmt.Sprintf("Initializing database...")) + err = initDatabase() + utils.StopSpinner(dbInitSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Initializing database... FAILED!") + } + + pwSpinner := utils.ShowSpinner(fmt.Sprintf("Generating database passwords...")) + // Try for passwords of the form dbC-3Ji-d04d + steampipePassword := generatePassword() + rootPassword := generatePassword() // write the passwords that were generated err = writePasswordFile(steampipePassword, rootPassword) - utils.FailOnErrorWithMessage(err, "failed") + utils.StopSpinner(pwSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Generating database passwords... FAILED!") + } + startServiceSpinner := utils.ShowSpinner(fmt.Sprintf("Configuring database...")) StartService(InvokerInstaller) - defer func() { // force stop StopDB(true) }() - err = installSteampipeDatabase(steampipePassword, rootPassword) - utils.FailOnErrorWithMessage(err, "failed") + utils.StopSpinner(startServiceSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Configuring database... FAILED!") + } - fmt.Println("Installing SteampipeHub...") + installSteampipeSpinner := utils.ShowSpinner(fmt.Sprintf("Configuring Steampipe...")) err = installSteampipeHub() - utils.FailOnErrorWithMessage(err, "failed") + utils.StopSpinner(installSteampipeSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Configuring Steampipe... FAILED!") + } // write a signature after everything gets done! // so that we can check for this later on + writeSignaturesSpinner := utils.ShowSpinner(fmt.Sprintf("Updating install records...")) err = writeDownloadedBinarySignature(dbImageDigest, fdwDigest) - utils.FailOnErrorWithMessage(err, "failed") + utils.StopSpinner(writeSignaturesSpinner) + if err != nil { + utils.FailOnErrorWithMessage(err, "x Updating install records... FAILED!") + } } func installSteampipeDatabase(withSteampipePassword string, withRootPassword string) error { @@ -221,7 +245,7 @@ func StartService(invoker Invoker) { break } if time.Since(startedAt) > constants.SpinnerShowTimeout && !spinnerShown { - s := utils.ShowSpinner("Waiting for service startup") + s := utils.ShowSpinner("Waiting for database to start...") spinnerShown = true defer utils.StopSpinner(s) } @@ -233,7 +257,7 @@ func StartService(invoker Invoker) { select { case <-timeoutAfter: - utils.ShowError(fmt.Errorf("could not start service")) + utils.ShowError(fmt.Errorf("x Waiting for database to start... FAILED!")) return case <-startedChannel: // do nothing diff --git a/db/password.go b/db/password.go index fb1cd2360..c5fc5d43d 100644 --- a/db/password.go +++ b/db/password.go @@ -4,8 +4,8 @@ import ( "encoding/json" "io/ioutil" "log" - "math/rand" - "strings" + + "github.com/google/uuid" ) // Passwords :: structure for working with DB passwords @@ -40,43 +40,15 @@ func getPasswords() (*Passwords, error) { return passwords, nil } -func generatePassword(passwordLength, minSpecialChar, minNum, minUpperCase int) string { - const ( - lowerCharSet = "abcdedfghijklmnopqrst" - upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - specialCharSet = "#$*" - numberSet = "0123456789" - allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet - ) - - var password strings.Builder - - // set special character - for i := 0; i < minSpecialChar; i++ { - random := rand.Intn(len(specialCharSet)) - password.WriteString(string(specialCharSet[random])) +func generatePassword() string { + // Create a simple, random password of the form f9fe-442f-90fb + // Simple to read / write, and has a strength rating of 4 per https://lowe.github.io/tryzxcvbn/ + // Yes, this UUIDv4 does always include a 4, but good enough for our needs. + u, err := uuid.NewRandom() + if err != nil { + // Should never happen? + panic(err) } - - // set numeric - for i := 0; i < minNum; i++ { - random := rand.Intn(len(numberSet)) - password.WriteString(string(numberSet[random])) - } - - // set uppercase - for i := 0; i < minUpperCase; i++ { - random := rand.Intn(len(upperCharSet)) - password.WriteString(string(upperCharSet[random])) - } - - remainingLength := passwordLength - minSpecialChar - minNum - minUpperCase - for i := 0; i < remainingLength; i++ { - random := rand.Intn(len(allCharSet)) - password.WriteString(string(allCharSet[random])) - } - inRune := []rune(password.String()) - rand.Shuffle(len(inRune), func(i, j int) { - inRune[i], inRune[j] = inRune[j], inRune[i] - }) - return string(inRune) + s := u.String() + return s[9:23] } diff --git a/db/stop.go b/db/stop.go index fbbbe7469..9e9ba9791 100644 --- a/db/stop.go +++ b/db/stop.go @@ -96,7 +96,7 @@ func StopDB(force bool) (StopStatus, error) { break } if time.Since(signalSentAt) > constants.SpinnerShowTimeout && !spinnerShown { - s := utils.ShowSpinner("Shutting down service") + s := utils.ShowSpinner("Shutting down...") defer utils.StopSpinner(s) spinnerShown = true } diff --git a/go.mod b/go.mod index 18de07cdf..5cbe31dad 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/fatih/color v1.7.0 github.com/gertd/go-pluralize v0.1.7 github.com/go-ole/go-ole v1.2.4 // indirect - github.com/google/uuid v1.1.2 + github.com/google/uuid v1.1.5 github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-plugin v1.3.0 github.com/hashicorp/go-version v1.2.1 @@ -39,7 +39,7 @@ require ( github.com/turbot/go-kit v0.0.0-20210119154454-db924443f736 github.com/turbot/steampipe-plugin-sdk v0.0.0-20210119154656-52569ae6f10d github.com/ulikunitz/xz v0.5.8 - github.com/zclconf/go-cty v1.7.0 + github.com/zclconf/go-cty v1.7.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gotest.tools/v3 v3.0.3 // indirect ) diff --git a/go.sum b/go.sum index 0f016a689..260ca58da 100644 --- a/go.sum +++ b/go.sum @@ -188,6 +188,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= diff --git a/ociinstaller/db.go b/ociinstaller/db.go index 31ab7dd7d..f1e296dbe 100644 --- a/ociinstaller/db.go +++ b/ociinstaller/db.go @@ -2,7 +2,6 @@ package ociinstaller import ( "context" - "fmt" "path/filepath" "time" @@ -17,14 +16,12 @@ func InstallDB(imageRef string, dest string) (string, error) { imageDownloader := NewOciDownloader(context.Background()) // Download the blobs - fmt.Println("Downloading...") image, err := imageDownloader.Download(imageRef, "db", tempDir.Path) if err != nil { return "", err } // install the files - fmt.Printf("\nInstalling...") if err = extractDbFiles(image, tempDir.Path, dest); err != nil { return "", err } diff --git a/utils/spinner.go b/utils/spinner.go index e7727b657..0e43b2b7c 100644 --- a/utils/spinner.go +++ b/utils/spinner.go @@ -14,7 +14,7 @@ func ShowSpinner(msg string) *spinner.Spinner { spinner.CharSets[14], 100*time.Millisecond, spinner.WithWriter(os.Stdout), - spinner.WithSuffix(fmt.Sprintf(" %s", msg)), + spinner.WithSuffix(fmt.Sprintf(" %s", msg)), ) s.Start() return s