Updating to current ToT

BUG=None
TEST=unit

Change-Id: I700c1f49396f83949e9ec71bb2fad009fc4d20fa
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/3158672
Reviewed-by: Otabek Kasimov <otabek@google.com>
Auto-Submit: Jaques Clapauch <jaquesc@google.com>
Tested-by: Jaques Clapauch <jaquesc@google.com>
Commit-Queue: Jaques Clapauch <jaquesc@google.com>
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/crosservice/crosservice.go b/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/crosservice/crosservice.go
index 3337a7e..7d56ef7 100644
--- a/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/crosservice/crosservice.go
+++ b/src/chromiumos/test/provision/cmd/provisionserver/bootstrap/services/crosservice/crosservice.go
@@ -56,6 +56,25 @@
 }
 
 /*
+	Constant Variables
+*/
+
+const curlWithRetries = "curl -S -s -v -# -C - --retry 3 --retry-delay 60"
+
+const pipeStatusHandler = `
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching %[1]s failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing %[1]s failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to %[2]s failed." >&2
+  exit 1
+fi`
+
+/*
 	The following run specific commands related to CrOS installation.
 */
 
@@ -172,7 +191,7 @@
 		return fmt.Errorf("failed to get GS Cache URL, %s", err)
 	}
 	fmt.Printf("URL used to install zipped image: %s\n", url)
-	_, err = c.connection.RunCmd(ctx, "curl", []string{url, "|", "gzip -d", "|", fmt.Sprintf("dd of=%s obs=2M", outputFile)})
+	_, err = c.connection.RunCmd(ctx, curlWithRetries, []string{url, "|", "gzip -d", "|", fmt.Sprintf("dd of=%s obs=2M", outputFile), fmt.Sprintf(pipeStatusHandler, url, outputFile)})
 	return err
 }
 
@@ -252,7 +271,7 @@
 	_, err = c.connection.RunCmd(ctx, "", []string{
 		fmt.Sprintf("rm -rf %[1]s %[2]s/var_new %[2]s/dev_image_new", info.UpdateStatefulFilePath, info.StatefulPath),
 		"&&",
-		fmt.Sprintf("curl %s | tar --ignore-command-error --overwrite --directory=%s -xzf -", url, info.StatefulPath),
+		fmt.Sprintf(curlWithRetries+" %s | tar --ignore-command-error --overwrite --directory=%s -xzf -", url, info.StatefulPath),
 		"&&",
 		fmt.Sprintf("echo -n clobber > %s", info.UpdateStatefulFilePath),
 	})
@@ -302,7 +321,7 @@
 	if _, err := c.connection.RunCmd(ctx, "", []string{
 		"mkdir", "-p", dlcOutputSlotDir,
 		"&&",
-		"curl", "--output", dlcOutputImage, url,
+		curlWithRetries, "--output", dlcOutputImage, url,
 	}); err != nil {
 		return fmt.Errorf("failed to provision DLC %s, %s", dlcID, err)
 	}
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/main.go b/src/chromiumos/test/provision/cmd/provisionserver/main.go
index 54d58d4..6d8cb47 100644
--- a/src/chromiumos/test/provision/cmd/provisionserver/main.go
+++ b/src/chromiumos/test/provision/cmd/provisionserver/main.go
@@ -22,9 +22,9 @@
 var Version = "<unknown>"
 
 // createLogFile creates a file and its parent directory for logging purpose.
-func createLogFile() (*os.File, error) {
+func createLogFile(logPath string) (*os.File, error) {
 	t := time.Now()
-	fullPath := filepath.Join("/tmp/provisionservice/", t.Format("20060102-150405"))
+	fullPath := filepath.Join(logPath, t.Format("20060102-150405"))
 	if err := os.MkdirAll(fullPath, 0755); err != nil {
 		return nil, fmt.Errorf("failed to create directory %v: %v", fullPath, err)
 	}
@@ -56,6 +56,9 @@
 	// Input and output json pb files.
 	inputPath  string
 	outputPath string
+
+	// log
+	logPath string
 }
 
 func (a *args) addCommonFlags(fs *flag.FlagSet) {
@@ -63,6 +66,8 @@
 
 	fs.StringVar(&a.dutServiceAddr, "dut-service-address", "", "grcp address for dut-service.")
 	fs.StringVar(&a.wiringServiceAddr, "wiring-service-address", "", "wiring address TLW.")
+
+	fs.StringVar(&a.logPath, "log-path", "/tmp/provisionservice/", "The path to the log file.")
 }
 
 func (a *args) verifyCommon() error {
@@ -109,58 +114,70 @@
 	return nil
 }
 
+func (a *args) setupLogging() (*log.Logger, error) {
+	logFile, err := createLogFile(a.logPath)
+	if err != nil {
+		return nil, err
+	}
+	logger := newLogger(logFile)
+	logger.Println("Starting provisionservice version ", Version)
+
+	return logger, nil
+}
+
 func mainInternal(ctx context.Context) int {
 	if len(os.Args) < 2 {
 		log.Fatalln("please provide arguments")
 		return 2
 	}
 
-	logFile, err := createLogFile()
-	if err != nil {
-		log.Fatalln("Failed to create log file: ", err)
-		return 1
-	}
-	defer logFile.Close()
-	logger := newLogger(logFile)
-	logger.Println("Starting provisionservice version ", Version)
-
 	a := &args{}
 	switch os.Args[1] {
 	case "cli":
 		if err := a.verifyCLIInput(os.Args[2:]); err != nil {
-			logger.Fatalln("Failed verify input: ", err)
+			fmt.Printf("failed verify input: %s", err)
+			return 2
+		}
+		logger, err := a.setupLogging()
+		if err != nil {
+			fmt.Printf("could not set up logging, %s", err)
 			return 2
 		}
 		p, closer, err := newProvision(logger, a.dutName, a.dutServiceAddr, a.wiringServiceAddr)
 		defer closer()
 		if err != nil {
-			logger.Fatalln("Failed to create provision: ", err)
+			fmt.Printf("failed to create provision, %s", err)
 			return 2
 		}
 		if err := p.runCLI(ctx, a.inputPath, a.outputPath); err != nil {
-			logger.Fatalln("Failed to perform provision: ", err)
+			fmt.Printf("failed to perform provision, %s", err)
 			return 1
 		}
 	case "server":
 		if err := a.verifyServerInput(os.Args[2:]); err != nil {
-			logger.Fatalln("Failed verify input: ", err)
+			fmt.Printf("failed verify input, %s", err)
+			return 2
+		}
+		logger, err := a.setupLogging()
+		if err != nil {
+			fmt.Printf("could not set up logging, %s", err)
 			return 2
 		}
 		p, closer, err := newProvision(logger, a.dutName, a.dutServiceAddr, a.wiringServiceAddr)
 		defer closer()
 		if err != nil {
-			logger.Fatalln("Failed to create provision: ", err)
+			fmt.Printf("failed to create provision, %s", err)
 			return 2
 		}
 		if err := p.startServer(a.serverPort); err != nil {
-			logger.Fatalln("Failed to perform provision: ", err)
+			fmt.Printf("failed to perform provision, %s", err)
 			return 1
 		}
 	case "version":
-		logger.Printf("Provisionservice version: %s", Version)
+		fmt.Printf("Provisionservice version: %s", Version)
 		return 0
 	default:
-		logger.Fatalln("Expected 'cli' or 'server' as subcommands.")
+		fmt.Printf("expected 'cli' or 'server' as subcommands.")
 		return 2
 	}
 	return 0
diff --git a/src/chromiumos/test/provision/cmd/provisionserver/provisionserver_test.go b/src/chromiumos/test/provision/cmd/provisionserver/provisionserver_test.go
index aa23a29..f4312e0 100644
--- a/src/chromiumos/test/provision/cmd/provisionserver/provisionserver_test.go
+++ b/src/chromiumos/test/provision/cmd/provisionserver/provisionserver_test.go
@@ -56,8 +56,32 @@
 	)
 	// Concurrent portion
 	sam.EXPECT().CopyData(gomock.Any(), gomock.Any()).Return("1", nil).AnyTimes()
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M"})).Times(1).Return("", nil)
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M"})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot5 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot4 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
 	// Serial Portion
 	gomock.InOrder(
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{
@@ -85,7 +109,7 @@
 		sam.EXPECT().Restart(gomock.Any()).Return(nil),
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("stop"), gomock.Eq([]string{"ui"})).Return("", nil),
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("stop"), gomock.Eq([]string{"update-engine"})).Return("", nil),
-		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{"rm -rf /mnt/stateful_partition/.update_available /mnt/stateful_partition/var_new /mnt/stateful_partition/dev_image_new", "&&", "curl 1 | tar --ignore-command-error --overwrite --directory=/mnt/stateful_partition -xzf -", "&&", "echo -n clobber > /mnt/stateful_partition/.update_available"})).Return("", nil),
+		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{"rm -rf /mnt/stateful_partition/.update_available /mnt/stateful_partition/var_new /mnt/stateful_partition/dev_image_new", "&&", "curl -S -s -v -# -C - --retry 3 --retry-delay 60 1 | tar --ignore-command-error --overwrite --directory=/mnt/stateful_partition -xzf -", "&&", "echo -n clobber > /mnt/stateful_partition/.update_available"})).Return("", nil),
 		sam.EXPECT().Restart(gomock.Any()).Return(nil),
 	)
 
@@ -111,7 +135,7 @@
 	// Concurrent Portion
 	// Return not verfied so we can test full case:
 	sam.EXPECT().PathExists(gomock.Any(), "/var/lib/dlcservice/dlc/1/dlc_a/verified").Return(false, nil)
-	sam.EXPECT().RunCmd(gomock.Any(), "", []string{"mkdir", "-p", "/var/cache/dlc/1/package/dlc_a", "&&", "curl", "--output", "/var/cache/dlc/1/package/dlc_a/dlc.img", "1"}).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), "", []string{"mkdir", "-p", "/var/cache/dlc/1/package/dlc_a", "&&", "curl -S -s -v -# -C - --retry 3 --retry-delay 60", "--output", "/var/cache/dlc/1/package/dlc_a/dlc.img", "1"}).Return("", nil)
 	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("start"), gomock.Eq([]string{"dlcservice"})).Times(1).Return("", nil)
 
 	if err := st.Execute(ctx); err != nil {
@@ -158,8 +182,32 @@
 	)
 	// Concurrent portion
 	sam.EXPECT().CopyData(gomock.Any(), gomock.Any()).Return("1", nil).AnyTimes()
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M"})).Times(1).Return("", nil)
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M"})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot5 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot4 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
 	// Serial Portion
 	gomock.InOrder(
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{
@@ -217,8 +265,32 @@
 	)
 	// Concurrent portion
 	sam.EXPECT().CopyData(gomock.Any(), gomock.Any()).Return("1", nil).AnyTimes()
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M"})).Times(1).Return("", nil)
-	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M"})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot5 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot5 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
+	sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("curl -S -s -v -# -C - --retry 3 --retry-delay 60"), gomock.Eq([]string{"1", "|", "gzip -d", "|", "dd of=root_diskroot4 obs=2M",
+		`
+pipestatus=("${PIPESTATUS[@]}")
+if [[ "${pipestatus[0]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Fetching 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[1]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Decompressing 1 failed." >&2
+  exit 1
+elif [[ "${pipestatus[2]}" -ne 0 ]]; then
+  echo "$(date --rfc-3339=seconds) ERROR: Writing to root_diskroot4 failed." >&2
+  exit 1
+fi`})).Times(1).Return("", nil)
 	// Serial Portion
 	gomock.InOrder(
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{
@@ -267,7 +339,7 @@
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("stop"), gomock.Eq([]string{"ui"})).Return("", nil),
 		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq("stop"), gomock.Eq([]string{"update-engine"})).Return("", nil),
 		sam.EXPECT().CopyData(gomock.Any(), gomock.Eq("path/to/image/stateful.tgz")).Return("url", nil),
-		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{"rm -rf /mnt/stateful_partition/.update_available /mnt/stateful_partition/var_new /mnt/stateful_partition/dev_image_new", "&&", "curl url | tar --ignore-command-error --overwrite --directory=/mnt/stateful_partition -xzf -", "&&", "echo -n clobber > /mnt/stateful_partition/.update_available"})).Return("", nil),
+		sam.EXPECT().RunCmd(gomock.Any(), gomock.Eq(""), gomock.Eq([]string{"rm -rf /mnt/stateful_partition/.update_available /mnt/stateful_partition/var_new /mnt/stateful_partition/dev_image_new", "&&", "curl -S -s -v -# -C - --retry 3 --retry-delay 60 url | tar --ignore-command-error --overwrite --directory=/mnt/stateful_partition -xzf -", "&&", "echo -n clobber > /mnt/stateful_partition/.update_available"})).Return("", nil),
 		sam.EXPECT().Restart(gomock.Any()).Return(nil),
 	)