prow/initupload/run.go
func processCloneLog(logfile string, uploadTargets map[string]gcs.UploadFunc) (bool, string, error) {
	var cloneRecords []clone.Record
	data, err := ioutil.ReadFile(logfile)
	if err != nil {
		return true, "", fmt.Errorf("could not read clone log: %v", err)
	}
	if err = json.Unmarshal(data, &cloneRecords); err != nil {
		return true, "", fmt.Errorf("could not unmarshal clone records: %v", err)
	}
	// Do not read from cloneLog directly. Instead create multiple readers from cloneLog so it can
	// be uploaded to both clone-log.txt and build-log.txt on failure.
	cloneLog := bytes.Buffer{}
	var failed bool
	var mainRefSHA string
	for idx, record := range cloneRecords {
		cloneLog.WriteString(clone.FormatRecord(record))
		failed = failed || record.Failed
		// fill in mainRefSHA with FinalSHA from the first record
		if idx == 0 {
			mainRefSHA = record.FinalSHA
		}

	}
	uploadTargets["clone-log.txt"] = gcs.DataUpload(bytes.NewReader(cloneLog.Bytes()))
	uploadTargets["clone-records.json"] = gcs.FileUpload(logfile)

	if failed {
		uploadTargets["build-log.txt"] = gcs.DataUpload(bytes.NewReader(cloneLog.Bytes()))

		passed := !failed
		now := time.Now().Unix()
		finished := gcs.Finished{
			Timestamp: &now,
			Passed:    &passed,
			Result:    "FAILURE",
		}
		finishedData, err := json.Marshal(&finished)
		if err != nil {
			return true, mainRefSHA, fmt.Errorf("could not marshal finishing data: %v", err)
		}
		uploadTargets["finished.json"] = gcs.DataUpload(bytes.NewReader(finishedData))
	}
	return failed, mainRefSHA, nil
}
func processCloneLog(logfile string, uploadTargets map[string]gcs.UploadFunc) (bool, string, error) {
	var cloneRecords []clone.Record
	data, err := ioutil.ReadFile(logfile)
	if err != nil {
		return true, "", fmt.Errorf("could not read clone log: %v", err)
	}
	if err = json.Unmarshal(data, &cloneRecords); err != nil {
		return true, "", fmt.Errorf("could not unmarshal clone records: %v", err)
	}
	// Do not read from cloneLog directly. Instead create multiple readers from cloneLog so it can
	// be uploaded to both clone-log.txt and build-log.txt on failure.
	cloneLog := bytes.Buffer{}
	var failed bool
	var mainRefSHA string
	for idx, record := range cloneRecords {
		cloneLog.WriteString(clone.FormatRecord(record))
		failed = failed || record.Failed
		// fill in mainRefSHA with FinalSHA from the first record
		if idx == 0 {
			mainRefSHA = record.FinalSHA
		}

	}
	uploadTargets["clone-log.txt"] = gcs.DataUpload(bytes.NewReader(cloneLog.Bytes()))
	uploadTargets["clone-records.json"] = gcs.FileUpload(logfile)

	if failed {
		uploadTargets["build-log.txt"] = gcs.DataUpload(bytes.NewReader(cloneLog.Bytes()))

		passed := !failed
		now := time.Now().Unix()
		finished := gcs.Finished{
			Timestamp: &now,
			Passed:    &passed,
			Result:    "FAILURE",
		}
		finishedData, err := json.Marshal(&finished)
		if err != nil {
			return true, mainRefSHA, fmt.Errorf("could not marshal finishing data: %v", err)
		}
		uploadTargets["finished.json"] = gcs.DataUpload(bytes.NewReader(finishedData))
	}
	return failed, mainRefSHA, nil
}
// processCloneLog checks if clone operation successed or failed for a ref
// and upload clone logs as build log upon failures.
// returns: bool - clone status
//          string - final main ref SHA on a successful clone
//          error - when unexpected file operation happens