Browse Source

Add --idempotent to make --install idempotent

Kailash Nadh 3 years ago
parent
commit
3847c67087
3 changed files with 32 additions and 15 deletions
  1. 3 2
      cmd/init.go
  2. 28 12
      cmd/install.go
  3. 1 1
      cmd/main.go

+ 3 - 2
cmd/init.go

@@ -84,13 +84,14 @@ func initFlags() {
 	// Register the commandline flags.
 	f.StringSlice("config", []string{"config.toml"},
 		"path to one or more config files (will be merged in order)")
-	f.Bool("install", false, "run first time installation")
+	f.Bool("install", false, "setup database (first time)")
+	f.Bool("idempotent", false, "make --install run only if the databse isn't already setup")
 	f.Bool("upgrade", false, "upgrade database to the current version")
 	f.Bool("version", false, "current version of the build")
 	f.Bool("new-config", false, "generate sample config file")
 	f.String("static-dir", "", "(optional) path to directory with static files")
 	f.String("i18n-dir", "", "(optional) path to directory with i18n language files")
-	f.Bool("yes", false, "assume 'yes' to prompts, eg: during --install")
+	f.Bool("yes", false, "assume 'yes' to prompts during --install/upgrade")
 	if err := f.Parse(os.Args[1:]); err != nil {
 		lo.Fatalf("error loading flags: %v", err)
 	}

+ 28 - 12
cmd/install.go

@@ -17,13 +17,17 @@ import (
 
 // install runs the first time setup of creating and
 // migrating the database and creating the super user.
-func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
+func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt, idempotent bool) {
 	qMap, _ := initQueries(queryFilePath, db, fs, false)
 
 	fmt.Println("")
-	fmt.Println("** first time installation **")
-	fmt.Printf("** IMPORTANT: This will wipe existing listmonk tables and types in the DB '%s' **",
-		ko.String("db.database"))
+	if !idempotent {
+		fmt.Println("** first time installation **")
+		fmt.Printf("** IMPORTANT: This will wipe existing listmonk tables and types in the DB '%s' **",
+			ko.String("db.database"))
+	} else {
+		fmt.Println("** first time (idempotent) installation **")
+	}
 	fmt.Println("")
 
 	if prompt {
@@ -38,10 +42,22 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
 		}
 	}
 
+	// If idempotence is on, check if the DB is already setup.
+	if idempotent {
+		if _, err := db.Exec("SELECT count(*) FROM settings"); err != nil {
+			// If "settings" doesn't exist, assume it's a fresh install.
+			if pqErr, ok := err.(*pq.Error); ok && pqErr.Code != "42P01" {
+				lo.Fatalf("error checking existing DB schema: %v", err)
+			}
+		} else {
+			lo.Println("skipping install as database appears to be already setup")
+			os.Exit(0)
+		}
+	}
+
 	// Migrate the tables.
-	err := installSchema(lastVer, db, fs)
-	if err != nil {
-		lo.Fatalf("Error migrating DB schema: %v", err)
+	if err := installSchema(lastVer, db, fs); err != nil {
+		lo.Fatalf("error migrating DB schema: %v", err)
 	}
 
 	// Load the queries.
@@ -62,7 +78,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
 		models.ListOptinSingle,
 		pq.StringArray{"test"},
 	); err != nil {
-		lo.Fatalf("Error creating list: %v", err)
+		lo.Fatalf("error creating list: %v", err)
 	}
 
 	if err := q.CreateList.Get(&optinList, uuid.Must(uuid.NewV4()),
@@ -71,7 +87,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
 		models.ListOptinDouble,
 		pq.StringArray{"test"},
 	); err != nil {
-		lo.Fatalf("Error creating list: %v", err)
+		lo.Fatalf("error creating list: %v", err)
 	}
 
 	// Sample subscriber.
@@ -93,7 +109,7 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
 		pq.Int64Array{int64(optinList)},
 		models.SubscriptionStatusUnconfirmed,
 		true); err != nil {
-		lo.Fatalf("Error creating subscriber: %v", err)
+		lo.Fatalf("error creating subscriber: %v", err)
 	}
 
 	// Default template.
@@ -132,8 +148,8 @@ func install(lastVer string, db *sqlx.DB, fs stuffbin.FileSystem, prompt bool) {
 		lo.Fatalf("error creating sample campaign: %v", err)
 	}
 
-	lo.Printf("Setup complete")
-	lo.Printf(`Run the program and access the dashboard at %s`, ko.MustString("app.address"))
+	lo.Printf("setup complete")
+	lo.Printf(`run the program and access the dashboard at %s`, ko.MustString("app.address"))
 }
 
 // installSchema executes the SQL schema and creates the necessary tables and types.

+ 1 - 1
cmd/main.go

@@ -122,7 +122,7 @@ func init() {
 	// as the installer needs to work on an empty DB.
 	if ko.Bool("install") {
 		// Save the version of the last listed migration.
-		install(migList[len(migList)-1].version, db, fs, !ko.Bool("yes"))
+		install(migList[len(migList)-1].version, db, fs, !ko.Bool("yes"), ko.Bool("idempotent"))
 		os.Exit(0)
 	}