From 1818d629ef5bb3d3aabda9323b1b0423dd004303 Mon Sep 17 00:00:00 2001
From: Renan DelValle <renanidelvalle+noreply@gmail.com>
Date: Tue, 23 Oct 2018 12:55:46 -0700
Subject: [PATCH] Created new verb force. Moved snapshot and backup to this new
 verb as it reflects the nature of the command. Added implicit and explicit
 reconciliation.

---
 README.md    |   6 +++
 cmd/fetch.go |   2 +-
 cmd/force.go | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++
 cmd/start.go |  41 ---------------
 4 files changed, 152 insertions(+), 42 deletions(-)
 create mode 100644 cmd/force.go

diff --git a/README.md b/README.md
index e09109c..9870190 100644
--- a/README.md
+++ b/README.md
@@ -46,5 +46,11 @@ Use "australis [command] --help" for more information about a command.
 ### Taking hosts out of DRAIN (End maintenance):
 `australis stop drain [HOST 1] [HOST 2]...[HOST N]`
 
+### Force a snapshot
+`australis force snapshot`
+
+### Force a backup
+`australis force backup`
+
 ## Status
 Australis is a work in progress and does not support all the features of Apache Aurora.
diff --git a/cmd/fetch.go b/cmd/fetch.go
index 7338171..7eb4fb9 100644
--- a/cmd/fetch.go
+++ b/cmd/fetch.go
@@ -121,7 +121,7 @@ func fetchLeader(cmd *cobra.Command, args []string) {
 		os.Exit(1)
 	}
 
-	fmt.Print(url)
+	fmt.Println(url)
 }
 
 // TODO: Expand this to be able to filter by job name and environment.
diff --git a/cmd/force.go b/cmd/force.go
new file mode 100644
index 0000000..c3cabe3
--- /dev/null
+++ b/cmd/force.go
@@ -0,0 +1,145 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+	"strconv"
+
+	"github.com/spf13/cobra"
+)
+
+func init() {
+	rootCmd.AddCommand(forceCmd)
+
+	// Sub-commands
+	forceCmd.AddCommand(forceSnapshotCmd)
+    forceCmd.AddCommand(forceBackupCmd)
+
+	// Recon sub-commands
+	forceCmd.AddCommand(reconCmd)
+	reconCmd.AddCommand(forceImplicitReconCmd)
+	reconCmd.AddCommand(forceExplicitReconCmd)
+}
+
+var forceCmd = &cobra.Command{
+	Use:   "force",
+	Short: "Force the scheduler to do a snapshot, a backup, or a task reconciliation.",
+}
+
+var forceSnapshotCmd = &cobra.Command{
+	Use:   "snapshot",
+	Short: "Force the leading scheduler to perform a Snapshot.",
+	Long: `Takes a Snapshot of the in memory state of the Apache Aurora cluster and
+writes it to the Mesos replicated log. This should NOT be confused with a backup.`,
+	Run:  snapshot,
+}
+
+func snapshot(cmd *cobra.Command, args []string) {
+	fmt.Println("Forcing scheduler to write snapshot to Mesos replicated log")
+	err := client.Snapshot()
+	if err != nil {
+		fmt.Printf("error: %+v\n", err.Error())
+		os.Exit(1)
+	} else {
+		fmt.Println("Snapshot started successfully")
+	}
+}
+
+var forceBackupCmd = &cobra.Command{
+	Use:   "backup",
+	Short: "Force the leading scheduler to perform a Backup.",
+	Long: `Force the Aurora Scheduler to write a backup of the latest snapshot to the filesystem 
+of the leading scheduler.`,
+	Run:  backup,
+}
+
+func backup(cmd *cobra.Command, args []string) {
+	fmt.Println("Forcing scheduler to write a Backup of latest Snapshot to file system")
+	err := client.PerformBackup()
+	if err != nil {
+		fmt.Printf("error: %+v\n", err.Error())
+		os.Exit(1)
+	} else {
+		fmt.Println("Backup started successfully")
+	}
+}
+
+var reconCmd = &cobra.Command{
+	Use:   "recon",
+	Short: "Force the leading scheduler to perform a reconciliation.",
+	Long: `Force the Aurora Scheduler to perform a task reconciliation.
+Explicit Recon:
+Aurora will send a list of non-terminal task IDs and the master responds
+with the latest state for each task, if possible.
+Implicit Recon:
+Aurora will send an empty list of tasks and the master responds with the latest
+state for all currently known non-terminal tasks.
+`,
+}
+
+var forceExplicitReconCmd = &cobra.Command{
+	Use:   "explicit",
+	Short: "Force the leading scheduler to perform an explicit recon.",
+	Long: `Aurora will send a list of non-terminal task IDs
+and the master responds with the latest state for each task, if possible.
+`,
+	Run:  explicitRecon,
+}
+
+func explicitRecon(cmd *cobra.Command, args []string) {
+	var batchSize *int32
+
+	fmt.Println("Forcing scheduler to perform an explicit reconciliation with Mesos")
+
+	if len(args) > 1 || len(args) < 0 {
+		fmt.Println("Only provide a batch size.")
+		os.Exit(1)
+	}
+
+	switch len(args) {
+	case 0:
+		fmt.Println("Using default batch size for explicit recon.")
+	case 1:
+		fmt.Printf("Using %v as batch size for explicit recon.\n", args[0])
+
+	    // Get batch size from args and convert it to the right format
+		batchInt, err := strconv.Atoi(args[0])
+        if err != nil {
+            fmt.Printf("error: %+v\n", err.Error())
+            os.Exit(1)
+        }
+		batchInt32 := int32(batchInt)
+		batchSize = &batchInt32
+    default:
+        fmt.Println("Provide 0 arguments to use default batch size or one argument to use a custom batch size.")
+        os.Exit(1)
+	}
+
+	err := client.ForceExplicitTaskReconciliation(batchSize)
+	if err != nil {
+		fmt.Printf("error: %+v\n", err.Error())
+		os.Exit(1)
+	} else {
+		fmt.Println("Explicit reconciliation started successfully")
+	}
+
+}
+
+var forceImplicitReconCmd = &cobra.Command{
+	Use:   "implicit",
+	Short: "Force the leading scheduler to perform an implicit recon.",
+	Long: `Force the `,
+	Run:  implicitRecon,
+}
+
+func implicitRecon(cmd *cobra.Command, args []string) {
+
+	fmt.Println("Forcing scheduler to perform an implicit reconciliation with Mesos")
+	err := client.PerformBackup()
+	if err != nil {
+		fmt.Printf("error: %+v\n", err.Error())
+		os.Exit(1)
+	} else {
+		fmt.Println("Implicit reconciliation started successfully")
+	}
+}
diff --git a/cmd/start.go b/cmd/start.go
index 9d6b81b..81b4751 100644
--- a/cmd/start.go
+++ b/cmd/start.go
@@ -14,8 +14,6 @@ func init() {
 
 	// Sub-commands
 	startCmd.AddCommand(startMaintCmd)
-	startCmd.AddCommand(startSnapshotCmd)
-    startCmd.AddCommand(startBackupCmd)
 
 	// Maintenance specific flags
 	startMaintCmd.Flags().IntVar(&monitorInterval,"interval", 5, "Interval at which to poll scheduler.")
@@ -68,42 +66,3 @@ func drain(cmd *cobra.Command, args []string) {
 	fmt.Println(result.String())
 }
 
-var startSnapshotCmd = &cobra.Command{
-	Use:   "snapshot",
-	Short: "Force the leading scheduler to perform a Snapshot.",
-	Long: `Takes a Snapshot of the in memory state of the Apache Aurora cluster and
-writes it to the Mesos replicated log. This should NOT be confused with a backup.`,
-	Run:  snapshot,
-}
-
-func snapshot(cmd *cobra.Command, args []string) {
-
-	fmt.Println("Forcing scheduler to write snapshot to Mesos replicated log")
-	err := client.Snapshot()
-	if err != nil {
-		fmt.Printf("error: %+v\n", err.Error())
-		os.Exit(1)
-	} else {
-		fmt.Println("Snapshot started successfully")
-	}
-}
-
-var startBackupCmd = &cobra.Command{
-	Use:   "backup",
-	Short: "Force the leading scheduler to perform a Backup.",
-	Long: `Force the Aurora Scheduler to write a backup of the latest snapshot to the filesystem 
-of the leading scheduler.`,
-	Run:  backup,
-}
-
-func backup(cmd *cobra.Command, args []string) {
-
-	fmt.Println("Forcing scheduler to write a Backup of latest Snapshot to file system")
-	err := client.PerformBackup()
-	if err != nil {
-		fmt.Printf("error: %+v\n", err.Error())
-		os.Exit(1)
-	} else {
-		fmt.Println("Backup started successfully")
-	}
-}