<commit_before>/*
 * s3verify (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"fmt"
	"strings"

	"github.com/cheggaaa/pb"
	"github.com/minio/mc/pkg/console"
)

// Set up global cursor channel.
var cursorCh = cursorAnimate()

// Set up a constant width to follow.
const (
	messageWidth = 50
)

/******************************** Scan Bar ************************************/
// fixateScanBar truncates long text to fit within the terminal size.
func fixateScanBar(text string, width int) string {
	if len([]rune(text)) > width {
		// Trim text to fit within the screen
		trimSize := len([]rune(text)) - width + 3 //"..."
		if trimSize < len([]rune(text)) {
			text = "..." + text[trimSize:]
		}
	}
	return text
}

// Progress bar function report objects being scaned.
type scanBarFunc func(string)

// scanBarFactory returns a progress bar function to report URL scanning.
func scanBarFactory() scanBarFunc {
	prevLineSize := 0
	termWidth, e := pb.GetTerminalWidth()
	if e != nil {
		console.Fatalln("Unable to get terminal size. Please use --quiet option.")
	}

	return func(message string) {
		scanPrefix := fmt.Sprintf("%s", message)
		padding := messageWidth - len([]rune(scanPrefix))

		message = fixateScanBar(message, termWidth-len([]rune(scanPrefix))-1)
		barText := scanPrefix + strings.Repeat(" ", padding) + string(<-cursorCh)

		if prevLineSize != 0 { // erase previous line
			console.PrintC("\r")
		}
		console.PrintC(barText + " \b ") // Remove only the cursor from the text.
		prevLineSize = len([]rune(barText))
	}
}
<commit_msg>Fix termWidth to 80 if TIOCGWINSZ is not supported (#241)<commit_after>/*
 * s3verify (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"fmt"
	"strings"

	"github.com/cheggaaa/pb"
	"github.com/minio/mc/pkg/console"
)

// Set up global cursor channel.
var cursorCh = cursorAnimate()

// Set up a constant width to follow.
const (
	messageWidth = 50
)

/******************************** Scan Bar ************************************/
// fixateScanBar truncates long text to fit within the terminal size.
func fixateScanBar(text string, width int) string {
	if len([]rune(text)) > width {
		// Trim text to fit within the screen
		trimSize := len([]rune(text)) - width + 3 //"..."
		if trimSize < len([]rune(text)) {
			text = "..." + text[trimSize:]
		}
	}
	return text
}

// Progress bar function report objects being scaned.
type scanBarFunc func(string)

// scanBarFactory returns a progress bar function to report URL scanning.
func scanBarFactory() scanBarFunc {
	prevLineSize := 0
	var termWidth int
	termWidth, e := pb.GetTerminalWidth()
	if e != nil {
		// Underlying terminal may not support this operation,
		// falling back to default 80 width.
		termWidth = 80
	}

	return func(message string) {
		scanPrefix := fmt.Sprintf("%s", message)
		padding := messageWidth - len([]rune(scanPrefix))

		message = fixateScanBar(message, termWidth-len([]rune(scanPrefix))-1)
		barText := scanPrefix + strings.Repeat(" ", padding) + string(<-cursorCh)

		if prevLineSize != 0 { // erase previous line
			console.PrintC("\r")
		}
		console.PrintC(barText + " \b ") // Remove only the cursor from the text.
		prevLineSize = len([]rune(barText))
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"log"
	"io/ioutil"
	"os"
	"fmt"
	"archive/zip"
	"github.com/gosuri/uilive"
	"time"
	"strings"
	"github.com/fatih/color"
	"path/filepath"
	"gopkg.in/yaml.v2"
)

var allResFiles = map[string]bool{"LICENSE.txt":true, "NOT_A_CONTRIBUTION.txt":true, "README.txt":true,
	"update-descriptor.yaml":true, "instructions.txt":true}

var (
	patchFilesMap map[string]bool
	distFileMap map[string]bool
	addedFilesMap map[string]bool
)

func Validate(patchLocation, distributionLocation string, logsEnabled bool) {

	patchFilesMap = make(map[string]bool)
	distFileMap = make(map[string]bool)
	addedFilesMap = make(map[string]bool)

	if (!logsEnabled) {
		log.SetOutput(ioutil.Discard)
	} else {
		log.Println("Logs enabled")
	}
	log.Println("validate command called")

	log.Println("Patch Loc: " + patchLocation)
	if !isAZipFile(patchLocation) {
		fmt.Println("Patch file should be a zip file.")
		os.Exit(1)
	}
	patchLocationExists := checkFile(patchLocation)
	if patchLocationExists {
		log.Println("Patch location exists.")
	} else {
		color.Set(color.FgRed)
		fmt.Println("Patch location does not exist. Enter a valid file location.")
		color.Unset()
		os.Exit(1)
	}

	log.Println("Reading patch zip...")
	readPatchZip(patchLocation, logsEnabled)
	log.Println("Patch zip successfully read.")
	log.Println("Entries in patch zip: ", patchFilesMap)

	log.Println("Distribution Loc: " + distributionLocation)
	if isAZipFile(distributionLocation) {
		zipFileExists := checkFile(distributionLocation)
		if zipFileExists {
			log.Println("Distribution location exists.")
			readDistZip(distributionLocation, logsEnabled)
		} else {
			color.Set(color.FgRed)
			fmt.Println("Distribution zip does not exist. Enter a valid location.")
			color.Unset()
			os.Exit(1)
		}
	} else {
		distributionLocationExists := checkDir(distributionLocation)
		if distributionLocationExists {
			log.Println("Distribution location exists.")
			readDistDir(distributionLocation, logsEnabled)
		} else {
			color.Set(color.FgRed)
			fmt.Println("Distribution location does not exist. Enter a valid location.")
			color.Unset()
			os.Exit(1)
		}
	}
	validate()
}

func validate() {
	for patchLoc, _ := range patchFilesMap {
		log.Println("Checking location: ", patchLoc)
		_, found := distFileMap[patchLoc]
		if found {
			log.Println(patchLoc, "found in distFileMap")
		} else {
			log.Println(patchLoc, "not found in distFileMap")
			_, found := addedFilesMap[patchLoc]
			if found {
				log.Println(patchLoc, "found in addedFilesMap")
			} else {
				log.Println(patchLoc, "not found in addedFilesMap")
				log.Println("addedFilesMap: ",addedFilesMap)
				color.Set(color.FgRed)
				fmt.Println(patchLoc, "not found in distribution and it is not a newly added file.")
				fmt.Println("If it is a new file, please check the entry in", _DESCRIPTOR_YAML_NAME,
					"file")
				fmt.Println("\nValidation FAILED\n")
				color.Unset()
				os.Exit(1)
			}
		}
	}
	fmt.Println("\nValidation SUCCESSFUL\n")
}

//This function reads the files of the given patch zip
func readPatchZip(zipLocation string, logsEnabled bool) (bool, error) {
	log.Println("Zip file reading started: ", zipLocation)

	patchName := strings.TrimSuffix(zipLocation, ".zip")
	if lastIndex := strings.LastIndex(patchName, string(os.PathSeparator)); lastIndex > -1 {
		patchName = patchName[lastIndex + 1:]
	}

	log.Println("Patch name: ", patchName)

	if !strings.HasPrefix(patchName, _PATCH_NAME_PREFIX) {
		fmt.Println("Patch file does not have", _PATCH_NAME_PREFIX, "prefix")
		os.Exit(0)
	} else {
		log.Println("Patch file does have", _PATCH_NAME_PREFIX, "prefix")
	}

	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(zipLocation)

	if err != nil {
		fmt.Println(err)
		log.Fatal(err)
	}
	defer zipReader.Close()

	totalFiles := len(zipReader.Reader.File)
	log.Println("File count in zip: ", totalFiles)

	filesRead := 0

	writer := uilive.New()
	//start listening for updates and render
	writer.Start()

	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		filesRead++
		if (!logsEnabled) {
			fmt.Fprintf(writer, "Reading files .. (%d/%d)\n", filesRead, totalFiles)
			time.Sleep(time.Millisecond * 5)
		}

		log.Println("Checking file: ", file.Name)
		_, found := allResFiles[file.FileInfo().Name()]

		if !found {
			containsCarbonHome := strings.Contains(file.Name, _CARBON_HOME)
			if (!containsCarbonHome) {
				fmt.Println("Does not have a", _CARBON_HOME, "folder")
				os.Exit(1)
			}
			log.Println("Have a", _CARBON_HOME, "folder")

			temp := strings.TrimPrefix(file.Name, patchName + string(os.PathSeparator) + _CARBON_HOME)
			log.Println("Entry: ", temp)
			patchFilesMap[temp] = true
		} else {
			log.Println(file.FileInfo().Name(), "was found in resource map")
			delete(allResFiles, file.FileInfo().Name())
			log.Println(file.FileInfo().Name(), "was removed from the map")

			if file.FileInfo().Name() == _DESCRIPTOR_YAML_NAME {
				yamlFile, err := file.Open()
				if err != nil {
					color.Set(color.FgRed)
					fmt.Println("Error occurred while reading the", _DESCRIPTOR_YAML_NAME, "file:",
						err)
					color.Unset()
				}
				data, err := ioutil.ReadAll(yamlFile)
				if err != nil {
					log.Fatal(err)
				}
				descriptor := update_descriptor{}

				err = yaml.Unmarshal(data, &descriptor)
				if err != nil {
					color.Set(color.FgRed)
					fmt.Println("Error occurred while unmarshalling the yaml:", err)
					color.Unset()
				}
				fmt.Println(descriptor)

				for _, addedFile := range descriptor.File_changes.Added_files {

					addedFilesMap[addedFile] = true
					//_, found := allResFiles[addedFile]
					//if found {
					//	log.Println(file, "found in patch")
					//
					//} else {
					//	fmt.Println(file, "not found in patch. But it is added as an entry in" +
					//	"'added_file' section in", _DESCRIPTOR_YAML_NAME)
					//	os.Exit(1)
					//}
				}
			}
		}
	}

	log.Println("Resource map:", allResFiles)
	log.Println(patchFilesMap)
	if (len(allResFiles) != 0) {
		writer.Stop()
		fmt.Println("Following resource file(s) were not found in the patch: ")
		for key, _ := range allResFiles {
			fmt.Println("\t", key)
		}
		os.Exit(1)
	}

	writer.Stop()
	log.Println("Zip file reading finished")
	log.Println("Total files read: ", filesRead)
	if totalFiles == filesRead {
		log.Println("All files read")
		return true, nil
	} else {
		color.Set(color.FgRed)
		fmt.Println("All files not read from zip file")
		color.Unset()
		os.Exit(1)
		return false, nil
	}
}

//This function reads the files of the given patch zip
func readDistZip(zipLocation string, logsEnabled bool) (bool, error) {
	log.Println("Zip file reading started: ", zipLocation)

	distName := strings.TrimSuffix(zipLocation, ".zip")
	if lastIndex := strings.LastIndex(distName, string(os.PathSeparator)); lastIndex > -1 {
		distName = distName[lastIndex + 1:]
	}
	//
	//log.Println("Patch name: ", patchName)
	//
	//if !strings.HasPrefix(patchName, _PATCH_NAME_PREFIX) {
	//	fmt.Println("Patch file does not have", _PATCH_NAME_PREFIX, "prefix")
	//	os.Exit(0)
	//} else {
	//	log.Println("Patch file does have", _PATCH_NAME_PREFIX, "prefix")
	//}

	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(zipLocation)

	if err != nil {
		fmt.Println(err)
		log.Fatal(err)
	}
	defer zipReader.Close()

	totalFiles := len(zipReader.Reader.File)
	log.Println("File count in zip: ", totalFiles)

	filesRead := 0

	writer := uilive.New()
	//start listening for updates and render
	writer.Start()

	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		filesRead++
		if (!logsEnabled) {
			fmt.Fprintf(writer, "Reading files .. (%d/%d)\n", filesRead, totalFiles)
			time.Sleep(time.Millisecond * 2)
		}

		log.Println("Checking file: ", file.Name)
		//_, found := allResFiles[file.FileInfo().Name()]

		//if !found {
		//	containsCarbonHome := strings.Contains(file.Name, _CARBON_HOME)
		//	if (!containsCarbonHome) {
		//		fmt.Println("Does not have a", _CARBON_HOME, "folder")
		//		os.Exit(1)
		//	}
		//	log.Println("Have a", _CARBON_HOME, "folder")

		temp := strings.TrimPrefix(file.Name, distName)
		log.Println("Entry: ", temp)
		distFileMap[temp] = true
		//} else {
		//	log.Println(file.FileInfo().Name(), "was found in resource map")
		//	delete(allResFiles, file.FileInfo().Name())
		//	log.Println(file.FileInfo().Name(), "was removed from the map")
		//}
	}

	//log.Println("Resource map:", allResFiles)
	//log.Println(patchFilesMap)
	//if (len(allResFiles) != 0) {
	//	writer.Stop()
	//	fmt.Println("Following resource file(s) were not found in the patch: ")
	//	for key, _ := range allResFiles {
	//		fmt.Println("\t", key)
	//	}
	//	os.Exit(1)
	//}

	writer.Stop()
	log.Println("Zip file reading finished")
	log.Println("Total files read: ", filesRead)
	if totalFiles == filesRead {
		log.Println("All files read")
		return true, nil
	} else {
		color.Set(color.FgRed)
		fmt.Println("All files not read from zip file")
		color.Unset()
		os.Exit(1)
		return false, nil
	}
}
func readDistDir(distributionLocation string, logsEnabled bool) {

	err := filepath.Walk(distributionLocation, func(path string, fileInfo os.FileInfo, err error) error {

		log.Println("Walking: ", path)

		//if !fileInfo.IsDir() {
		// Create and write files to the archive, which in turn
		// are getting written to the underlying writer to the
		// .zip file we created at the beginning

		//header, err := zip.FileInfoHeader(fileInfo)
		//if err != nil {
		//	color.Set(color.FgRed)
		//	fmt.Println("Error occurred while creating the zip file: ", err)
		//	return err
		//	color.Unset()
		//}
		//
		//header.Name = _PATCH_NAME + string(os.PathSeparator) + strings.TrimPrefix(path, _TEMP_DIR_NAME + string(os.PathSeparator))

		temp := strings.TrimPrefix(path, distributionLocation)
		log.Println("Entry: ", temp)
		distFileMap[temp] = true

		//}
		//log.Printf("Visited: %s\n", path)
		return nil
	})

	if err != nil {
		color.Set(color.FgRed)
		fmt.Println("Error occurred while reading the zip file: ", err)
		os.Exit(1)
		color.Unset()
	}
}
<commit_msg>Error messages and logs changed<commit_after>package cmd

import (
	"log"
	"io/ioutil"
	"os"
	"fmt"
	"archive/zip"
	"github.com/gosuri/uilive"
	"time"
	"strings"
	"github.com/fatih/color"
	"path/filepath"
	"gopkg.in/yaml.v2"
)

var allResFiles = map[string]bool{"LICENSE.txt":true, "NOT_A_CONTRIBUTION.txt":true, "README.txt":true,
	"update-descriptor.yaml":true, "instructions.txt":true}

var (
	patchFilesMap map[string]bool
	distFileMap map[string]bool
	addedFilesMap map[string]bool
)

func Validate(patchLocation, distributionLocation string, logsEnabled bool) {

	patchFilesMap = make(map[string]bool)
	distFileMap = make(map[string]bool)
	addedFilesMap = make(map[string]bool)

	if (!logsEnabled) {
		log.SetOutput(ioutil.Discard)
	} else {
		log.Println("Logs enabled")
	}
	log.Println("validate command called")

	log.Println("Patch Loc: " + patchLocation)
	if !isAZipFile(patchLocation) {
		fmt.Println("Patch file should be a zip file.")
		os.Exit(1)
	}
	patchLocationExists := checkFile(patchLocation)
	if patchLocationExists {
		log.Println("Patch location exists.")
	} else {
		color.Set(color.FgRed)
		fmt.Println("Patch location does not exist. Enter a valid file location.")
		color.Unset()
		os.Exit(1)
	}

	log.Println("Reading patch zip...")
	readPatchZip(patchLocation, logsEnabled)
	log.Println("Patch zip successfully read.")
	log.Println("Entries in patch zip: ", patchFilesMap)

	log.Println("Distribution Loc: " + distributionLocation)
	if isAZipFile(distributionLocation) {
		zipFileExists := checkFile(distributionLocation)
		if zipFileExists {
			log.Println("Distribution location exists.")
			readDistZip(distributionLocation, logsEnabled)
		} else {
			color.Set(color.FgRed)
			fmt.Println("Distribution zip does not exist. Enter a valid location.")
			color.Unset()
			os.Exit(1)
		}
	} else {
		distributionLocationExists := checkDir(distributionLocation)
		if distributionLocationExists {
			log.Println("Distribution location exists.")
			readDistDir(distributionLocation, logsEnabled)
		} else {
			color.Set(color.FgRed)
			fmt.Println("Distribution location does not exist. Enter a valid location.")
			color.Unset()
			os.Exit(1)
		}
	}
	validate()
}

func validate() {
	for patchLoc, _ := range patchFilesMap {
		log.Println("Checking location: ", patchLoc)
		_, found := distFileMap[patchLoc]
		if found {
			log.Println(patchLoc, "found in distFileMap")
		} else {
			log.Println(patchLoc, "not found in distFileMap")
			_, found := addedFilesMap[patchLoc]
			if found {
				log.Println(patchLoc, "found in addedFilesMap")
			} else {
				log.Println(patchLoc, "not found in addedFilesMap")
				log.Println("addedFilesMap: ", addedFilesMap)
				color.Set(color.FgRed)
				fmt.Println(patchLoc, "not found in distribution and it is not a newly added file.")
				fmt.Println("If it is a new file, please check the entry in", _DESCRIPTOR_YAML_NAME,
					"file")
				fmt.Println("\nValidation FAILED\n")
				color.Unset()
				os.Exit(1)
			}
		}
	}
	fmt.Println("\nValidation SUCCESSFUL\n")
}

//This function reads the files of the given patch zip
func readPatchZip(zipLocation string, logsEnabled bool) (bool, error) {
	log.Println("Zip file reading started: ", zipLocation)

	patchName := strings.TrimSuffix(zipLocation, ".zip")
	if lastIndex := strings.LastIndex(patchName, string(os.PathSeparator)); lastIndex > -1 {
		patchName = patchName[lastIndex + 1:]
	}

	log.Println("Patch name: ", patchName)

	if !strings.HasPrefix(patchName, _PATCH_NAME_PREFIX) {
		fmt.Println("Patch file does not have", _PATCH_NAME_PREFIX, "prefix")
		os.Exit(0)
	} else {
		log.Println("Patch file does have", _PATCH_NAME_PREFIX, "prefix")
	}

	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(zipLocation)

	if err != nil {
		fmt.Println(err)
		log.Fatal(err)
	}
	defer zipReader.Close()

	totalFiles := len(zipReader.Reader.File)
	log.Println("File count in zip: ", totalFiles)

	filesRead := 0

	writer := uilive.New()
	//start listening for updates and render
	writer.Start()

	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		filesRead++
		if (!logsEnabled) {
			fmt.Fprintf(writer, "Reading files from patch zip: (%d/%d)\n", filesRead, totalFiles)
			time.Sleep(time.Millisecond * 5)
		}

		log.Println("Checking file: ", file.Name)
		_, found := allResFiles[file.FileInfo().Name()]

		if !found {
			containsCarbonHome := strings.Contains(file.Name, _CARBON_HOME)
			if (!containsCarbonHome) {
				fmt.Println("Does not have a", _CARBON_HOME, "folder")
				os.Exit(1)
			}
			log.Println("Have a", _CARBON_HOME, "folder")

			temp := strings.TrimPrefix(file.Name, patchName + string(os.PathSeparator) + _CARBON_HOME)
			log.Println("Entry: ", temp)
			patchFilesMap[temp] = true
		} else {
			log.Println(file.FileInfo().Name(), "was found in resource map")
			delete(allResFiles, file.FileInfo().Name())
			log.Println(file.FileInfo().Name(), "was removed from the map")

			if file.FileInfo().Name() == _DESCRIPTOR_YAML_NAME {
				yamlFile, err := file.Open()
				if err != nil {
					color.Set(color.FgRed)
					fmt.Println("Error occurred while reading the", _DESCRIPTOR_YAML_NAME, "file:",
						err)
					color.Unset()
				}
				data, err := ioutil.ReadAll(yamlFile)
				if err != nil {
					log.Fatal(err)
				}
				descriptor := update_descriptor{}

				err = yaml.Unmarshal(data, &descriptor)
				if err != nil {
					color.Set(color.FgRed)
					fmt.Println("Error occurred while unmarshalling the yaml:", err)
					color.Unset()
				}

				log.Println("descriptor:", descriptor)

				for _, addedFile := range descriptor.File_changes.Added_files {

					addedFilesMap[addedFile] = true
					//_, found := allResFiles[addedFile]
					//if found {
					//	log.Println(file, "found in patch")
					//
					//} else {
					//	fmt.Println(file, "not found in patch. But it is added as an entry in" +
					//	"'added_file' section in", _DESCRIPTOR_YAML_NAME)
					//	os.Exit(1)
					//}
				}
			}
		}
	}

	log.Println("Resource map:", allResFiles)
	log.Println(patchFilesMap)
	if (len(allResFiles) != 0) {
		writer.Stop()
		fmt.Println("Following resource file(s) were not found in the patch: ")
		for key, _ := range allResFiles {
			fmt.Println("\t", key)
		}
		os.Exit(1)
	}

	writer.Stop()
	log.Println("Zip file reading finished")
	log.Println("Total files read: ", filesRead)
	if totalFiles == filesRead {
		log.Println("All files read")
		return true, nil
	} else {
		color.Set(color.FgRed)
		fmt.Println("All files not read from zip file")
		color.Unset()
		os.Exit(1)
		return false, nil
	}
}

//This function reads the files of the given patch zip
func readDistZip(zipLocation string, logsEnabled bool) (bool, error) {
	log.Println("Zip file reading started: ", zipLocation)

	distName := strings.TrimSuffix(zipLocation, ".zip")
	if lastIndex := strings.LastIndex(distName, string(os.PathSeparator)); lastIndex > -1 {
		distName = distName[lastIndex + 1:]
	}
	//
	//log.Println("Patch name: ", patchName)
	//
	//if !strings.HasPrefix(patchName, _PATCH_NAME_PREFIX) {
	//	fmt.Println("Patch file does not have", _PATCH_NAME_PREFIX, "prefix")
	//	os.Exit(0)
	//} else {
	//	log.Println("Patch file does have", _PATCH_NAME_PREFIX, "prefix")
	//}

	// Create a reader out of the zip archive
	zipReader, err := zip.OpenReader(zipLocation)

	if err != nil {
		fmt.Println(err)
		log.Fatal(err)
	}
	defer zipReader.Close()

	totalFiles := len(zipReader.Reader.File)
	log.Println("File count in zip: ", totalFiles)

	filesRead := 0

	writer := uilive.New()
	//start listening for updates and render
	writer.Start()

	// Iterate through each file/dir found in
	for _, file := range zipReader.Reader.File {
		filesRead++
		if (!logsEnabled) {
			fmt.Fprintf(writer, "Reading files from distribution zip: (%d/%d)\n", filesRead, totalFiles)
			time.Sleep(time.Millisecond * 2)
		}

		log.Println("Checking file: ", file.Name)
		//_, found := allResFiles[file.FileInfo().Name()]

		//if !found {
		//	containsCarbonHome := strings.Contains(file.Name, _CARBON_HOME)
		//	if (!containsCarbonHome) {
		//		fmt.Println("Does not have a", _CARBON_HOME, "folder")
		//		os.Exit(1)
		//	}
		//	log.Println("Have a", _CARBON_HOME, "folder")

		temp := strings.TrimPrefix(file.Name, distName)
		log.Println("Entry: ", temp)
		distFileMap[temp] = true
		//} else {
		//	log.Println(file.FileInfo().Name(), "was found in resource map")
		//	delete(allResFiles, file.FileInfo().Name())
		//	log.Println(file.FileInfo().Name(), "was removed from the map")
		//}
	}

	//log.Println("Resource map:", allResFiles)
	//log.Println(patchFilesMap)
	//if (len(allResFiles) != 0) {
	//	writer.Stop()
	//	fmt.Println("Following resource file(s) were not found in the patch: ")
	//	for key, _ := range allResFiles {
	//		fmt.Println("\t", key)
	//	}
	//	os.Exit(1)
	//}

	writer.Stop()
	log.Println("Zip file reading finished")
	log.Println("Total files read: ", filesRead)
	if totalFiles == filesRead {
		log.Println("All files read")
		return true, nil
	} else {
		color.Set(color.FgRed)
		fmt.Println("All files not read from zip file")
		color.Unset()
		os.Exit(1)
		return false, nil
	}
}
func readDistDir(distributionLocation string, logsEnabled bool) {

	writer := uilive.New()
	//start listening for updates and render
	writer.Start()
	filesRead := 0
	err := filepath.Walk(distributionLocation, func(path string, fileInfo os.FileInfo, err error) error {
		filesRead++;
		if (!logsEnabled) {
			fmt.Fprintf(writer, "Reading files from distribution zip: %d files read\n", filesRead)
			time.Sleep(time.Millisecond * 2)
		}

		log.Println("Walking: ", path)

		//if !fileInfo.IsDir() {
		// Create and write files to the archive, which in turn
		// are getting written to the underlying writer to the
		// .zip file we created at the beginning

		//header, err := zip.FileInfoHeader(fileInfo)
		//if err != nil {
		//	color.Set(color.FgRed)
		//	fmt.Println("Error occurred while creating the zip file: ", err)
		//	return err
		//	color.Unset()
		//}
		//
		//header.Name = _PATCH_NAME + string(os.PathSeparator) + strings.TrimPrefix(path, _TEMP_DIR_NAME + string(os.PathSeparator))

		temp := strings.TrimPrefix(path, distributionLocation)
		log.Println("Entry: ", temp)
		distFileMap[temp] = true

		//}
		//log.Printf("Visited: %s\n", path)
		return nil
	})

	if err != nil {
		color.Set(color.FgRed)
		fmt.Println("Error occurred while reading the zip file: ", err)
		os.Exit(1)
		color.Unset()
	}
	log.Println("Total files read:", filesRead)
	writer.Stop()
}
<|endoftext|>


<commit_before>// Package convertkit provides a client to the ConvertKit API v3.
// See http://help.convertkit.com/article/33-api-documentation-v3
package convertkit

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

// Config is used to configure the creation of the client.
type Config struct {
	Endpoint string
	Key      string
	Secret   string

	HTTPClient *http.Client
}

// DefaultConfig returns a default configuration for the client. It parses the
// environment variables CONVERTKIT_ENDPOINT, CONVERTKIT_API_KEY, and
// CONVERTKIT_API_SECRET.
func DefaultConfig() *Config {
	c := Config{
		Endpoint:   "https://api.convertkit.com",
		HTTPClient: http.DefaultClient,
	}
	if v := os.Getenv("CONVERTKIT_ENDPOINT"); v != "" {
		c.Endpoint = v
	}
	if v := os.Getenv("CONVERTKIT_API_KEY"); v != "" {
		c.Key = v
	}
	if v := os.Getenv("CONVERTKIT_API_SECRET"); v != "" {
		c.Secret = v
	}
	return &c
}

// Client is the client to the ConvertKit API. Create a client with NewClient.
type Client struct {
	config *Config
}

// NewClient returns a new client for the given configuration.
func NewClient(c *Config) (*Client, error) {
	defConfig := DefaultConfig()
	if c.Endpoint == "" {
		c.Endpoint = defConfig.Endpoint
	}
	if c.Key == "" {
		c.Key = defConfig.Key
	}
	if c.Secret == "" {
		c.Secret = defConfig.Secret
	}
	if c.HTTPClient == nil {
		c.HTTPClient = defConfig.HTTPClient
	}
	return &Client{config: c}, nil
}

// Subscriber describes a ConvertKit subscriber.
type Subscriber struct {
	ID           int               `json:"id"`
	FirstName    string            `json:"first_name"`
	EmailAddress string            `json:"email_address"`
	State        string            `json:"state"`
	CreatedAt    time.Time         `json:"created_at"`
	Fields       map[string]string `json:"fields"`
}

type subscriberResponse struct {
	TotalSubscribers int          `json:"total_subscribers"`
	Page             int          `json:"page"`
	TotalPages       int          `json:"total_pages"`
	Subscribers      []Subscriber `json:"subscribers"`
}

// Subscribers returns a list of all confirmed subscribers.
func (c *Client) Subscribers() ([]Subscriber, error) {
	var subscribers []Subscriber
	page := 1

	for {
		url := fmt.Sprintf("%s/v3/subscribers?api_secret=%s&page=%d",
			c.config.Endpoint, c.config.Secret, page)

		var resp subscriberResponse
		if err := c.sendRequest("GET", url, nil, &resp); err != nil {
			return nil, err
		}
		subscribers = append(subscribers, resp.Subscribers...)

		if page >= resp.TotalPages {
			break
		}
		page++
	}

	return subscribers, nil
}

// TotalSubscribers returns the number of confirmed subscribers.
func (c *Client) TotalSubscribers() (int, error) {
	url := fmt.Sprintf("%s/v3/subscribers?api_secret=%s",
		c.config.Endpoint, c.config.Secret)

	var resp subscriberResponse
	if err := c.sendRequest("GET", url, nil, &resp); err != nil {
		return 0, err
	}

	return resp.TotalSubscribers, nil
}

func (c *Client) sendRequest(method, url string, body io.Reader, out interface{}) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}

	resp, err := c.config.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP error: %s", resp.Status)
	}

	return json.NewDecoder(resp.Body).Decode(out)
}
<commit_msg>Add TODO<commit_after>// Package convertkit provides a client to the ConvertKit API v3.
// See http://help.convertkit.com/article/33-api-documentation-v3
package convertkit

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

// Config is used to configure the creation of the client.
type Config struct {
	Endpoint string
	Key      string
	Secret   string

	HTTPClient *http.Client
}

// DefaultConfig returns a default configuration for the client. It parses the
// environment variables CONVERTKIT_ENDPOINT, CONVERTKIT_API_KEY, and
// CONVERTKIT_API_SECRET.
func DefaultConfig() *Config {
	c := Config{
		Endpoint:   "https://api.convertkit.com",
		HTTPClient: http.DefaultClient,
	}
	if v := os.Getenv("CONVERTKIT_ENDPOINT"); v != "" {
		c.Endpoint = v
	}
	if v := os.Getenv("CONVERTKIT_API_KEY"); v != "" {
		c.Key = v
	}
	if v := os.Getenv("CONVERTKIT_API_SECRET"); v != "" {
		c.Secret = v
	}
	return &c
}

// Client is the client to the ConvertKit API. Create a client with NewClient.
type Client struct {
	config *Config
}

// NewClient returns a new client for the given configuration.
func NewClient(c *Config) (*Client, error) {
	defConfig := DefaultConfig()
	if c.Endpoint == "" {
		c.Endpoint = defConfig.Endpoint
	}
	if c.Key == "" {
		c.Key = defConfig.Key
	}
	if c.Secret == "" {
		c.Secret = defConfig.Secret
	}
	if c.HTTPClient == nil {
		c.HTTPClient = defConfig.HTTPClient
	}
	return &Client{config: c}, nil
}

// Subscriber describes a ConvertKit subscriber.
type Subscriber struct {
	ID           int               `json:"id"`
	FirstName    string            `json:"first_name"`
	EmailAddress string            `json:"email_address"`
	State        string            `json:"state"`
	CreatedAt    time.Time         `json:"created_at"`
	Fields       map[string]string `json:"fields"`
}

type subscriberResponse struct {
	TotalSubscribers int          `json:"total_subscribers"`
	Page             int          `json:"page"`
	TotalPages       int          `json:"total_pages"`
	Subscribers      []Subscriber `json:"subscribers"`
}

// Subscribers returns a list of all confirmed subscribers.
func (c *Client) Subscribers() ([]Subscriber, error) {
	var subscribers []Subscriber
	page := 1

	// TODO: speed up operation by processing subscriber pages concurrently
	for {
		url := fmt.Sprintf("%s/v3/subscribers?api_secret=%s&page=%d",
			c.config.Endpoint, c.config.Secret, page)

		var resp subscriberResponse
		if err := c.sendRequest("GET", url, nil, &resp); err != nil {
			return nil, err
		}
		subscribers = append(subscribers, resp.Subscribers...)

		if page >= resp.TotalPages {
			break
		}
		page++
	}

	return subscribers, nil
}

// TotalSubscribers returns the number of confirmed subscribers.
func (c *Client) TotalSubscribers() (int, error) {
	url := fmt.Sprintf("%s/v3/subscribers?api_secret=%s",
		c.config.Endpoint, c.config.Secret)

	var resp subscriberResponse
	if err := c.sendRequest("GET", url, nil, &resp); err != nil {
		return 0, err
	}

	return resp.TotalSubscribers, nil
}

func (c *Client) sendRequest(method, url string, body io.Reader, out interface{}) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}

	resp, err := c.config.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP error: %s", resp.Status)
	}

	return json.NewDecoder(resp.Body).Decode(out)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"
	"strings"

	cmds "github.com/ipfs/go-ipfs/commands"
	config "github.com/ipfs/go-ipfs/repo/config"
)

type VersionOutput struct {
	Version string
	Commit string
}

var VersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Shows ipfs version information",
		ShortDescription: "Returns the current version of ipfs and exits.",
	},

	Options: []cmds.Option{
		cmds.BoolOption("number", "n", "Only show the version number"),
		cmds.BoolOption("commit", "Show the commit hash"),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		res.SetOutput(&VersionOutput{
			Version: config.CurrentVersionNumber,
			Commit: config.CurrentCommit,
		})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			v := res.Output().(*VersionOutput)

			commit, found, err := res.Request().Option("commit").Bool()
			commitTxt := ""
			if err != nil {
				return nil, err
			}
			if found && commit {
				commitTxt = "-" + v.Commit
			}

			number, found, err := res.Request().Option("number").Bool()
			if err != nil {
				return nil, err
			}
			if found && number {
				return strings.NewReader(fmt.Sprintln(v.Version + commitTxt)), nil
			}

			return strings.NewReader(fmt.Sprintf("ipfs version %s%s\n", v.Version, commitTxt)), nil
		},
	},
	Type: VersionOutput{},
}
<commit_msg>add option to version command to print repo version<commit_after>package commands

import (
	"fmt"
	"io"
	"strings"

	cmds "github.com/ipfs/go-ipfs/commands"
	config "github.com/ipfs/go-ipfs/repo/config"
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
)

type VersionOutput struct {
	Version string
	Commit  string
	Repo    string
}

var VersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Shows ipfs version information",
		ShortDescription: "Returns the current version of ipfs and exits.",
	},

	Options: []cmds.Option{
		cmds.BoolOption("number", "n", "Only show the version number"),
		cmds.BoolOption("commit", "Show the commit hash"),
		cmds.BoolOption("repo", "Show repo version"),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		res.SetOutput(&VersionOutput{
			Version: config.CurrentVersionNumber,
			Commit:  config.CurrentCommit,
			Repo:    fsrepo.RepoVersion,
		})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			v := res.Output().(*VersionOutput)

			repo, _, err := res.Request().Option("repo").Bool()
			if err != nil {
				return nil, err
			}

			if repo {
				return strings.NewReader(v.Repo + "\n"), nil
			}

			commit, found, err := res.Request().Option("commit").Bool()
			commitTxt := ""
			if err != nil {
				return nil, err
			}
			if found && commit {
				commitTxt = "-" + v.Commit
			}

			number, found, err := res.Request().Option("number").Bool()
			if err != nil {
				return nil, err
			}
			if found && number {
				return strings.NewReader(fmt.Sprintln(v.Version + commitTxt)), nil
			}

			return strings.NewReader(fmt.Sprintf("ipfs version %s%s\n", v.Version, commitTxt)), nil
		},
	},
	Type: VersionOutput{},
}
<|endoftext|>


<commit_before>/*

Client for interacting with ts4 API.

*/

package ts4

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
)

// ErrBlobNotFound is returned from a get/stat request
// if the blob does not exist.
var ErrBlobNotFound = errors.New("blob not found")

var defaultServerAddr = "http://localhost:8010"

type BlobStore struct {
	ServerAddr string
	client     *http.Client
}

func New(serverAddr string) *BlobStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &BlobStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
	}
}

type BlobInfo struct {
	Hash string `json:"hash"`
	Time string `json:"time"`
}

// Query send all blobs from start to end over the blobs channel,
// start default to 0 and end to time.Now().UTC() if left empty.
func (bs *BlobStore) Query(start, end string, blobs chan<- []byte) error {
	//TODO(tsileo)
	// In a for loop decode json from /blobs into []*BlobInfo
	// and send them over the channel, make as many requests as necessary under the hood
	for {
		res, err := bs.query(start, end)
		if err != nil {
			return err
		}
		if len(res) == 0 {
			break
		}
		for _, blobinfo := range res {
			start = blobinfo.Time
			blob, err := bs.Get(blobinfo.Hash)
			if err != nil {
				return err
			}
			blobs <- blob
		}
	}
	return nil
}

func (bs *BlobStore) query(start, end string) ([]*BlobInfo, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/blobs?start="+start+"&end="+end, nil)
	if err != nil {
		return nil, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	res := []*BlobInfo{}
	if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return res, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to query blobs")
	}
}

// Get fetch the given blob.
func (bs *BlobStore) Get(hash string) ([]byte, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/blob/"+hash, nil)
	if err != nil {
		return nil, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return body, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get blob %v: %v", hash, string(body))
	}
}

// Put upload the given blob
func (bs *BlobStore) Put(blob []byte) error {
	hash := fmt.Sprintf("%x", sha1.Sum(blob))
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(hash, hash)
	if err != nil {
		return err
	}
	if _, err := part.Write(blob); err != nil {
		return err
	}
	if err := writer.Close(); err != nil {
		return err
	}
	request, err := http.NewRequest("POST", bs.ServerAddr+"/upload", body)
	if err != nil {
		return err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	body.Reset()
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to put blob %v", body.String())
	}
	return nil
}
<commit_msg>better client API<commit_after>/*

Client for interacting with ts4 API.

*/

package ts4

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
)

// ErrBlobNotFound is returned from a get/stat request
// if the blob does not exist.
var ErrBlobNotFound = errors.New("blob not found")

var defaultServerAddr = "http://localhost:8010"

type BlobStore struct {
	ServerAddr string
	client     *http.Client
}

func New(serverAddr string) *BlobStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &BlobStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
	}
}

type BlobInfo struct {
	Hash string `json:"hash"`
	Time string `json:"time"`
}

type QueryResp struct {
	Blobs []*BlobInfo `json:"blobs"`
	Start string      `json:"start"`
	End   string      `json:"end"`
}

// Iter sends all blobs from start to end over the blobs channel,
// start default to 0 and end to time.Now().UTC() if left empty.
func (bs *BlobStore) Iter(start, end string, blobs chan<- []byte) error {
	for {
		res, err := bs.Query(start, end)
		if err != nil {
			return err
		}
		if len(res.Blobs) == 0 {
			break
		}
		for _, blobinfo := range res.Blobs {
			start = blobinfo.Time
			blob, err := bs.Get(blobinfo.Hash)
			if err != nil {
				return err
			}
			blobs <- blob
		}
	}
	return nil
}

// Query returns a QueryResp containing blobs hash and time.
// start default to 0 and end to time.Now().UTC() if left empty.
func (bs *BlobStore) Query(start, end string) (*QueryResp, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/blobs?start="+start+"&end="+end, nil)
	if err != nil {
		return nil, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	res := &QueryResp{}
	if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return res, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to query blobs")
	}
}

// Get fetch the given blob.
func (bs *BlobStore) Get(hash string) ([]byte, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/blob/"+hash, nil)
	if err != nil {
		return nil, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return body, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get blob %v: %v", hash, string(body))
	}
}

// Put upload the given blob
func (bs *BlobStore) Put(blob []byte) error {
	hash := fmt.Sprintf("%x", sha1.Sum(blob))
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(hash, hash)
	if err != nil {
		return err
	}
	if _, err := part.Write(blob); err != nil {
		return err
	}
	if err := writer.Close(); err != nil {
		return err
	}
	request, err := http.NewRequest("POST", bs.ServerAddr+"/upload", body)
	if err != nil {
		return err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	body.Reset()
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to put blob %v", body.String())
	}
	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"reflect"
	"sync"
	"time"

	log "github.com/mondough/slog"
	"github.com/mondough/terrors"
	tperrors "github.com/mondough/terrors/proto"
	tmsg "github.com/mondough/typhon/message"
	"github.com/nu7hatch/gouuid"

	"github.com/mondough/mercury"
	"github.com/mondough/mercury/marshaling"
	"github.com/mondough/mercury/transport"
)

const defaultTimeout = 10 * time.Second

var (
	defaultMiddleware  []ClientMiddleware
	defaultMiddlewareM sync.RWMutex
)

type clientCall struct {
	uid      string           // unique identifier within a client
	req      mercury.Request  // may be nil in the case of a request marshaling failure
	rsp      mercury.Response // set when a response is received
	rspProto interface{}      // shared with rsp when unmarshaled
	err      *terrors.Error   // execution error or unmarshalled (remote) error
}

type client struct {
	sync.RWMutex
	calls      map[string]clientCall // uid: call
	doneC      chan struct{}         // closed when execution has finished; immutable
	execC      chan struct{}         // closed when execution begins; immutable
	execOnce   sync.Once             // ensures execution only happens once
	timeout    time.Duration         // default: defaultTimeout
	trans      transport.Transport   // defaults to the global default
	middleware []ClientMiddleware    // applied in-order for requests, reverse-order for responses
}

func NewClient() Client {
	defaultMiddlewareM.RLock()
	middleware := defaultMiddleware
	defaultMiddlewareM.RUnlock()

	return &client{
		calls:      make(map[string]clientCall),
		doneC:      make(chan struct{}),
		execC:      make(chan struct{}),
		timeout:    defaultTimeout,
		middleware: middleware,
	}
}

func SetDefaultMiddleware(middleware []ClientMiddleware) {
	defaultMiddlewareM.Lock()
	defer defaultMiddlewareM.Unlock()
	defaultMiddleware = middleware
}

func (c *client) transport() transport.Transport {
	// Callers must hold (at least) a read lock
	if c.trans != nil {
		return c.trans
	} else {
		return transport.DefaultTransport()
	}
}

func (c *client) addCall(cc clientCall) {
	select {
	case <-c.execC:
		log.Warn(cc.req, "[Mercury:Client] Request added after client execution; discarding")
		return
	default:
	}

	c.Lock()
	defer c.Unlock()
	c.calls[cc.uid] = cc
}

func (c *client) Add(cl Call) Client {
	cc := clientCall{
		uid:      cl.Uid,
		rspProto: cl.Response,
	}
	req, err := cl.Request()
	if err != nil {
		cc.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else {
		cc.req = req
	}
	c.addCall(cc)
	return c
}

func (c *client) AddRequest(uid string, req mercury.Request) Client {
	c.addCall(clientCall{
		uid:      uid,
		req:      req,
		rspProto: nil,
	})
	return c
}

func (c *client) Errors() ErrorSet {
	c.RLock()
	defer c.RUnlock()
	errs := ErrorSet(nil)
	for uid, call := range c.calls {
		if call.err != nil {
			err := call.err
			err.Params[errUidField] = uid
			if call.req != nil {
				err.Params[errServiceField] = call.req.Service()
				err.Params[errEndpointField] = call.req.Endpoint()
			}
			errs = append(errs, err)
		}
	}
	return errs
}

func (c *client) unmarshaler(rsp mercury.Response, protocol interface{}) tmsg.Unmarshaler {
	result := marshaling.Unmarshaler(rsp.Headers()[marshaling.ContentTypeHeader], protocol)
	if result == nil { // Default to proto
		result = marshaling.Unmarshaler(marshaling.ProtoContentType, protocol)
	}
	return result
}

// performCall executes a single Call, unmarshals the response (if there is a response proto), and pushes the updted
// clientCall down the response channel
func (c *client) performCall(call clientCall, middleware []ClientMiddleware, trans transport.Transport,
	timeout time.Duration, completion chan<- clientCall) {

	req := call.req

	// Ensure we have a request ID before the request middleware is executed
	if id := req.Id(); id == "" {
		_uuid, err := uuid.NewV4()
		if err != nil {
			log.Error(call.req, "[Mercury:Client] Failed to generate request uuid: %v", err)
			call.err = terrors.Wrap(err, nil).(*terrors.Error)
			completion <- call
			return
		}
		req.SetId(_uuid.String())
	}

	// Apply request middleware
	for _, md := range middleware {
		req = md.ProcessClientRequest(req)
	}

	log.Debug(call.req, "[Mercury:Client] Sending request to %s/%s…", req.Service(), req.Endpoint())

	rsp_, err := trans.Send(req, timeout)
	if err != nil {
		call.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else if rsp_ != nil {
		rsp := mercury.FromTyphonResponse(rsp_)

		// For error responses, unmarshal the error, leaving the call's response nil
		if rsp.IsError() {
			errRsp := rsp.Copy()
			if unmarshalErr := c.unmarshaler(rsp, &tperrors.Error{}).UnmarshalPayload(errRsp); unmarshalErr != nil {
				call.err = terrors.WrapWithCode(unmarshalErr, nil, terrors.ErrBadResponse).(*terrors.Error)
			} else {
				err := errRsp.Body().(*tperrors.Error)
				call.err = terrors.Unmarshal(err)
			}

			// Set the response Body to a nil – but typed – interface to avoid type conversion panics if Body
			// properties are accessed in spite of the error
			// Relevant: http://golang.org/doc/faq#nil_error
			if call.rspProto != nil {
				bodyT := reflect.TypeOf(call.rspProto)
				rsp.SetBody(reflect.New(bodyT.Elem()).Interface())
			}

		} else if call.rspProto != nil {
			rsp.SetBody(call.rspProto)
			if err := c.unmarshaler(rsp, call.rspProto).UnmarshalPayload(rsp); err != nil {
				call.err = terrors.WrapWithCode(err, nil, terrors.ErrBadResponse).(*terrors.Error)
			}
		}

		call.rsp = rsp
	}

	// Apply response/error middleware (in reverse order)
	for i := len(middleware) - 1; i >= 0; i-- {
		mw := middleware[i]
		if call.err != nil {
			mw.ProcessClientError(call.err, call.req)
		} else {
			call.rsp = mw.ProcessClientResponse(call.rsp, call.req)
		}
	}

	completion <- call
}

// exec actually executes the requests; called by Go() within a sync.Once.
func (c *client) exec() {
	defer close(c.doneC)

	c.RLock()
	timeout := c.timeout
	calls := c.calls // We don't need to make a copy as calls cannot be mutated once execution begins
	trans := c.transport()
	middleware := c.middleware
	c.RUnlock()

	completedCallsC := make(chan clientCall, len(calls))
	for _, call := range calls {
		if call.err != nil {
			completedCallsC <- call
			continue
		} else if trans == nil {
			call.err = terrors.InternalService("no_transport", "Client has no transport", nil)
			completedCallsC <- call
			continue
		}
		go c.performCall(call, middleware, trans, timeout, completedCallsC)
	}

	// Collect completed calls into a new map
	completedCalls := make(map[string]clientCall, cap(completedCallsC))
	for i := 0; i < cap(completedCallsC); i++ {
		call := <-completedCallsC
		completedCalls[call.uid] = call
	}
	close(completedCallsC)

	c.Lock()
	defer c.Unlock()
	c.calls = completedCalls
}

func (c *client) Go() Client {
	c.execOnce.Do(func() {
		close(c.execC)
		go c.exec()
	})
	return c
}

func (c *client) WaitC() <-chan struct{} {
	return c.doneC
}

func (c *client) Wait() Client {
	<-c.WaitC()
	return c
}

func (c *client) Execute() Client {
	return c.Go().Wait()
}

func (c *client) Response(uid string) mercury.Response {
	c.RLock()
	defer c.RUnlock()
	if call, ok := c.calls[uid]; ok {
		return call.rsp
	}
	return nil
}

func (c *client) SetTimeout(to time.Duration) Client {
	c.Lock()
	defer c.Unlock()
	c.timeout = to
	return c
}

func (c *client) SetTransport(trans transport.Transport) Client {
	c.Lock()
	defer c.Unlock()
	c.trans = trans
	return c
}

func (c *client) Middleware() []ClientMiddleware {
	c.RLock()
	defer c.RUnlock()
	return c.middleware
}

func (c *client) AddMiddleware(md ClientMiddleware) Client {
	c.Lock()
	defer c.Unlock()
	c.middleware = append(c.middleware, md)
	return c
}

func (c *client) SetMiddleware(mds []ClientMiddleware) Client {
	c.Lock()
	defer c.Unlock()
	c.middleware = mds
	return c
}
<commit_msg>Remove unnecessary log line<commit_after>package client

import (
	"reflect"
	"sync"
	"time"

	log "github.com/mondough/slog"
	"github.com/mondough/terrors"
	tperrors "github.com/mondough/terrors/proto"
	tmsg "github.com/mondough/typhon/message"
	"github.com/nu7hatch/gouuid"

	"github.com/mondough/mercury"
	"github.com/mondough/mercury/marshaling"
	"github.com/mondough/mercury/transport"
)

const defaultTimeout = 10 * time.Second

var (
	defaultMiddleware  []ClientMiddleware
	defaultMiddlewareM sync.RWMutex
)

type clientCall struct {
	uid      string           // unique identifier within a client
	req      mercury.Request  // may be nil in the case of a request marshaling failure
	rsp      mercury.Response // set when a response is received
	rspProto interface{}      // shared with rsp when unmarshaled
	err      *terrors.Error   // execution error or unmarshalled (remote) error
}

type client struct {
	sync.RWMutex
	calls      map[string]clientCall // uid: call
	doneC      chan struct{}         // closed when execution has finished; immutable
	execC      chan struct{}         // closed when execution begins; immutable
	execOnce   sync.Once             // ensures execution only happens once
	timeout    time.Duration         // default: defaultTimeout
	trans      transport.Transport   // defaults to the global default
	middleware []ClientMiddleware    // applied in-order for requests, reverse-order for responses
}

func NewClient() Client {
	defaultMiddlewareM.RLock()
	middleware := defaultMiddleware
	defaultMiddlewareM.RUnlock()

	return &client{
		calls:      make(map[string]clientCall),
		doneC:      make(chan struct{}),
		execC:      make(chan struct{}),
		timeout:    defaultTimeout,
		middleware: middleware,
	}
}

func SetDefaultMiddleware(middleware []ClientMiddleware) {
	defaultMiddlewareM.Lock()
	defer defaultMiddlewareM.Unlock()
	defaultMiddleware = middleware
}

func (c *client) transport() transport.Transport {
	// Callers must hold (at least) a read lock
	if c.trans != nil {
		return c.trans
	} else {
		return transport.DefaultTransport()
	}
}

func (c *client) addCall(cc clientCall) {
	select {
	case <-c.execC:
		log.Warn(cc.req, "[Mercury:Client] Request added after client execution; discarding")
		return
	default:
	}

	c.Lock()
	defer c.Unlock()
	c.calls[cc.uid] = cc
}

func (c *client) Add(cl Call) Client {
	cc := clientCall{
		uid:      cl.Uid,
		rspProto: cl.Response,
	}
	req, err := cl.Request()
	if err != nil {
		cc.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else {
		cc.req = req
	}
	c.addCall(cc)
	return c
}

func (c *client) AddRequest(uid string, req mercury.Request) Client {
	c.addCall(clientCall{
		uid:      uid,
		req:      req,
		rspProto: nil,
	})
	return c
}

func (c *client) Errors() ErrorSet {
	c.RLock()
	defer c.RUnlock()
	errs := ErrorSet(nil)
	for uid, call := range c.calls {
		if call.err != nil {
			err := call.err
			err.Params[errUidField] = uid
			if call.req != nil {
				err.Params[errServiceField] = call.req.Service()
				err.Params[errEndpointField] = call.req.Endpoint()
			}
			errs = append(errs, err)
		}
	}
	return errs
}

func (c *client) unmarshaler(rsp mercury.Response, protocol interface{}) tmsg.Unmarshaler {
	result := marshaling.Unmarshaler(rsp.Headers()[marshaling.ContentTypeHeader], protocol)
	if result == nil { // Default to proto
		result = marshaling.Unmarshaler(marshaling.ProtoContentType, protocol)
	}
	return result
}

// performCall executes a single Call, unmarshals the response (if there is a response proto), and pushes the updted
// clientCall down the response channel
func (c *client) performCall(call clientCall, middleware []ClientMiddleware, trans transport.Transport,
	timeout time.Duration, completion chan<- clientCall) {

	req := call.req

	// Ensure we have a request ID before the request middleware is executed
	if id := req.Id(); id == "" {
		_uuid, err := uuid.NewV4()
		if err != nil {
			log.Error(call.req, "[Mercury:Client] Failed to generate request uuid: %v", err)
			call.err = terrors.Wrap(err, nil).(*terrors.Error)
			completion <- call
			return
		}
		req.SetId(_uuid.String())
	}

	// Apply request middleware
	for _, md := range middleware {
		req = md.ProcessClientRequest(req)
	}

	rsp_, err := trans.Send(req, timeout)
	if err != nil {
		call.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else if rsp_ != nil {
		rsp := mercury.FromTyphonResponse(rsp_)

		// For error responses, unmarshal the error, leaving the call's response nil
		if rsp.IsError() {
			errRsp := rsp.Copy()
			if unmarshalErr := c.unmarshaler(rsp, &tperrors.Error{}).UnmarshalPayload(errRsp); unmarshalErr != nil {
				call.err = terrors.WrapWithCode(unmarshalErr, nil, terrors.ErrBadResponse).(*terrors.Error)
			} else {
				err := errRsp.Body().(*tperrors.Error)
				call.err = terrors.Unmarshal(err)
			}

			// Set the response Body to a nil – but typed – interface to avoid type conversion panics if Body
			// properties are accessed in spite of the error
			// Relevant: http://golang.org/doc/faq#nil_error
			if call.rspProto != nil {
				bodyT := reflect.TypeOf(call.rspProto)
				rsp.SetBody(reflect.New(bodyT.Elem()).Interface())
			}

		} else if call.rspProto != nil {
			rsp.SetBody(call.rspProto)
			if err := c.unmarshaler(rsp, call.rspProto).UnmarshalPayload(rsp); err != nil {
				call.err = terrors.WrapWithCode(err, nil, terrors.ErrBadResponse).(*terrors.Error)
			}
		}

		call.rsp = rsp
	}

	// Apply response/error middleware (in reverse order)
	for i := len(middleware) - 1; i >= 0; i-- {
		mw := middleware[i]
		if call.err != nil {
			mw.ProcessClientError(call.err, call.req)
		} else {
			call.rsp = mw.ProcessClientResponse(call.rsp, call.req)
		}
	}

	completion <- call
}

// exec actually executes the requests; called by Go() within a sync.Once.
func (c *client) exec() {
	defer close(c.doneC)

	c.RLock()
	timeout := c.timeout
	calls := c.calls // We don't need to make a copy as calls cannot be mutated once execution begins
	trans := c.transport()
	middleware := c.middleware
	c.RUnlock()

	completedCallsC := make(chan clientCall, len(calls))
	for _, call := range calls {
		if call.err != nil {
			completedCallsC <- call
			continue
		} else if trans == nil {
			call.err = terrors.InternalService("no_transport", "Client has no transport", nil)
			completedCallsC <- call
			continue
		}
		go c.performCall(call, middleware, trans, timeout, completedCallsC)
	}

	// Collect completed calls into a new map
	completedCalls := make(map[string]clientCall, cap(completedCallsC))
	for i := 0; i < cap(completedCallsC); i++ {
		call := <-completedCallsC
		completedCalls[call.uid] = call
	}
	close(completedCallsC)

	c.Lock()
	defer c.Unlock()
	c.calls = completedCalls
}

func (c *client) Go() Client {
	c.execOnce.Do(func() {
		close(c.execC)
		go c.exec()
	})
	return c
}

func (c *client) WaitC() <-chan struct{} {
	return c.doneC
}

func (c *client) Wait() Client {
	<-c.WaitC()
	return c
}

func (c *client) Execute() Client {
	return c.Go().Wait()
}

func (c *client) Response(uid string) mercury.Response {
	c.RLock()
	defer c.RUnlock()
	if call, ok := c.calls[uid]; ok {
		return call.rsp
	}
	return nil
}

func (c *client) SetTimeout(to time.Duration) Client {
	c.Lock()
	defer c.Unlock()
	c.timeout = to
	return c
}

func (c *client) SetTransport(trans transport.Transport) Client {
	c.Lock()
	defer c.Unlock()
	c.trans = trans
	return c
}

func (c *client) Middleware() []ClientMiddleware {
	c.RLock()
	defer c.RUnlock()
	return c.middleware
}

func (c *client) AddMiddleware(md ClientMiddleware) Client {
	c.Lock()
	defer c.Unlock()
	c.middleware = append(c.middleware, md)
	return c
}

func (c *client) SetMiddleware(mds []ClientMiddleware) Client {
	c.Lock()
	defer c.Unlock()
	c.middleware = mds
	return c
}
<|endoftext|>


<commit_before>package controller

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"time"

	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/rpcplus"
	"github.com/flynn/strowger/types"
)

func New(uri string) (*Client, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	return &Client{url: uri, addr: u.Host}, nil
}

type Client struct {
	url  string
	addr string
}

var ErrNotFound = errors.New("controller: not found")

func (c *Client) send(method, path string, in, out interface{}) error {
	data, err := json.Marshal(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(method, c.url+path, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	if out != nil {
		return json.NewDecoder(res.Body).Decode(out)
	}
	return nil
}

func (c *Client) put(path string, in, out interface{}) error {
	return c.send("PUT", path, in, out)
}

func (c *Client) post(path string, in, out interface{}) error {
	return c.send("POST", path, in, out)
}

func (c *Client) get(path string, out interface{}) error {
	res, err := http.Get(c.url + path)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		if res.StatusCode == 404 {
			return ErrNotFound
		}
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func (c *Client) StreamFormations(since *time.Time) (<-chan *ct.ExpandedFormation, *error) {
	if since == nil {
		*since = time.Unix(0, 0)
	}
	// TODO: handle TLS
	client, err := rpcplus.DialHTTP("tcp", c.addr)
	if err != nil {
		return nil, &err
	}
	ch := make(chan *ct.ExpandedFormation)
	return ch, &client.StreamGo("Controller.StreamFormations", since, ch).Error
}

func (c *Client) CreateArtifact(artifact *ct.Artifact) error {
	return c.post("/artifacts", artifact, artifact)
}

func (c *Client) CreateRelease(release *ct.Release) error {
	return c.post("/releases", release, release)
}

func (c *Client) CreateApp(app *ct.App) error {
	return c.post("/apps", app, app)
}

func (c *Client) CreateProvider(provider *ct.Provider) error {
	return c.post("/providers", provider, provider)
}

func (c *Client) ProvisionResource(req *ct.ResourceReq) (*ct.Resource, error) {
	if req.ProviderID == "" {
		return nil, errors.New("controller: missing provider id")
	}
	res := &ct.Resource{}
	err := c.post(fmt.Sprintf("/providers/%s/resources", req.ProviderID), req, res)
	return res, err
}

func (c *Client) PutResource(resource *ct.Resource) error {
	if resource.ID == "" || resource.ProviderID == "" {
		return errors.New("controller: missing id and/or provider id")
	}
	return c.put(fmt.Sprintf("/providers/%s/resources/%s", resource.ProviderID, resource.ID), resource, resource)
}

func (c *Client) PutFormation(formation *ct.Formation) error {
	if formation.AppID == "" || formation.ReleaseID == "" {
		return errors.New("controller: missing app id and/or release id")
	}
	return c.put(fmt.Sprintf("/apps/%s/formations/%s", formation.AppID, formation.ReleaseID), formation, formation)
}

func (c *Client) SetAppRelease(appID, releaseID string) error {
	return c.put(fmt.Sprintf("/apps/%s/release", appID), &ct.Release{ID: releaseID}, nil)
}

func (c *Client) GetAppRelease(appID string) (*ct.Release, error) {
	release := &ct.Release{}
	return release, c.get(fmt.Sprintf("/apps/%s/release", appID), release)
}

func (c *Client) CreateRoute(appID string, route *strowger.Route) error {
	return c.post(fmt.Sprintf("/apps/%s/routes", appID), route, route)
}
<commit_msg>controller/client: Implement discoverd+http scheme for http reqs<commit_after>package controller

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"time"

	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/go-discoverd"
	"github.com/flynn/go-discoverd/dialer"
	"github.com/flynn/rpcplus"
	"github.com/flynn/strowger/types"
)

func New(uri string) (*Client, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	c := &Client{
		url:  uri,
		addr: u.Host,
		http: http.DefaultClient,
	}
	if u.Scheme == "discoverd+http" {
		if err := discoverd.Connect(""); err != nil {
			return nil, err
		}
		c.http = dialer.NewHTTPClient(discoverd.DefaultClient)
		u.Scheme = "http"
		c.url = u.String()
		// TODO: fix StreamFormations to use this
	}
	return c, nil
}

type Client struct {
	url  string
	addr string
	http *http.Client
}

var ErrNotFound = errors.New("controller: not found")

func (c *Client) send(method, path string, in, out interface{}) error {
	data, err := json.Marshal(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(method, c.url+path, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := c.http.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	if out != nil {
		return json.NewDecoder(res.Body).Decode(out)
	}
	return nil
}

func (c *Client) put(path string, in, out interface{}) error {
	return c.send("PUT", path, in, out)
}

func (c *Client) post(path string, in, out interface{}) error {
	return c.send("POST", path, in, out)
}

func (c *Client) get(path string, out interface{}) error {
	res, err := c.http.Get(c.url + path)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		if res.StatusCode == 404 {
			return ErrNotFound
		}
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func (c *Client) StreamFormations(since *time.Time) (<-chan *ct.ExpandedFormation, *error) {
	if since == nil {
		*since = time.Unix(0, 0)
	}
	// TODO: handle TLS
	client, err := rpcplus.DialHTTP("tcp", c.addr)
	if err != nil {
		return nil, &err
	}
	ch := make(chan *ct.ExpandedFormation)
	return ch, &client.StreamGo("Controller.StreamFormations", since, ch).Error
}

func (c *Client) CreateArtifact(artifact *ct.Artifact) error {
	return c.post("/artifacts", artifact, artifact)
}

func (c *Client) CreateRelease(release *ct.Release) error {
	return c.post("/releases", release, release)
}

func (c *Client) CreateApp(app *ct.App) error {
	return c.post("/apps", app, app)
}

func (c *Client) CreateProvider(provider *ct.Provider) error {
	return c.post("/providers", provider, provider)
}

func (c *Client) ProvisionResource(req *ct.ResourceReq) (*ct.Resource, error) {
	if req.ProviderID == "" {
		return nil, errors.New("controller: missing provider id")
	}
	res := &ct.Resource{}
	err := c.post(fmt.Sprintf("/providers/%s/resources", req.ProviderID), req, res)
	return res, err
}

func (c *Client) PutResource(resource *ct.Resource) error {
	if resource.ID == "" || resource.ProviderID == "" {
		return errors.New("controller: missing id and/or provider id")
	}
	return c.put(fmt.Sprintf("/providers/%s/resources/%s", resource.ProviderID, resource.ID), resource, resource)
}

func (c *Client) PutFormation(formation *ct.Formation) error {
	if formation.AppID == "" || formation.ReleaseID == "" {
		return errors.New("controller: missing app id and/or release id")
	}
	return c.put(fmt.Sprintf("/apps/%s/formations/%s", formation.AppID, formation.ReleaseID), formation, formation)
}

func (c *Client) SetAppRelease(appID, releaseID string) error {
	return c.put(fmt.Sprintf("/apps/%s/release", appID), &ct.Release{ID: releaseID}, nil)
}

func (c *Client) GetAppRelease(appID string) (*ct.Release, error) {
	release := &ct.Release{}
	return release, c.get(fmt.Sprintf("/apps/%s/release", appID), release)
}

func (c *Client) CreateRoute(appID string, route *strowger.Route) error {
	return c.post(fmt.Sprintf("/apps/%s/routes", appID), route, route)
}
<|endoftext|>


<commit_before>package client

import (
	"github.com/jmcvetta/napping"
	"net/http"
	"net/url"
	"path"
)

type Client struct {
	*napping.Session
	*ClientOptions
}

type ClientOptions struct {
	// Hostname of gitbookio endpoint
	Host string

	// Auth info
	Username string
	Password string
}

func NewClient(opts ClientOptions) *Client {
	// Default hostname
	if opts.Host == "" {
		opts.Host = "https://www.gitbook.io"
	}

	// Setup session
	// for authentication and custom headers
	session := &napping.Session{
		Userinfo: url.UserPassword(opts.Username, opts.Password),
		Header:   &http.Header{},
		Client:   &http.Client{},

		// Authorize use of client to HTTP endpoints
		UnsafeBasicAuth: true,
	}

	// We want JSON responses (for errors especially)
	session.Header.Set("Accept", "application/json")

	return &Client{
		Session:       session,
		ClientOptions: &opts,
	}
}

func (c *Client) Delete(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Delete(c.Url(url), result, errMsg)
	})
}

func (c *Client) Get(url string, p *napping.Params, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Get(c.Url(url), p, result, errMsg)
	})
}

func (c *Client) Head(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Head(c.Url(url), result, errMsg)
	})
}

func (c *Client) Options(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Options(c.Url(url), result, errMsg)
	})
}

func (c *Client) Patch(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Patch(c.Url(url), payload, result, errMsg)
	})
}

func (c *Client) Post(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Post(c.Url(url), payload, result, errMsg)
	})
}

func (c *Client) Put(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Put(c.Url(url), payload, result, errMsg)
	})
}

// Url returns the full http url including host
func (c *Client) Url(urlpath string) string {
	// Ignore errors for now
	parsed, _ := url.Parse(c.Host)

	// Rewrite path
	parsed.Path = path.Join(parsed.Path, urlpath)

	// Return string URL
	return parsed.String()
}

// This is so we include API errors as well as protocol errors here
func errorPatch(f func(err *Error) (*napping.Response, error)) (*napping.Response, error) {
	errMsg := &Error{}
	resp, err := f(errMsg)
	// API error
	if err == nil && errMsg.Code != 0 {
		return resp, errMsg
	}
	// Normal or protcol error
	return resp, err
}
<commit_msg>Update for latest version of napping - setting UnsafeBasicAuth is no longer required.<commit_after>package client

import (
	"github.com/jmcvetta/napping"
	"net/http"
	"net/url"
	"path"
)

type Client struct {
	*napping.Session
	*ClientOptions
}

type ClientOptions struct {
	// Hostname of gitbookio endpoint
	Host string

	// Auth info
	Username string
	Password string
}

func NewClient(opts ClientOptions) *Client {
	// Default hostname
	if opts.Host == "" {
		opts.Host = "https://www.gitbook.io"
	}

	// Setup session
	// for authentication and custom headers
	session := &napping.Session{
		Userinfo: url.UserPassword(opts.Username, opts.Password),
		Header:   &http.Header{},
		Client:   &http.Client{},
	}

	// We want JSON responses (for errors especially)
	session.Header.Set("Accept", "application/json")

	return &Client{
		Session:       session,
		ClientOptions: &opts,
	}
}

func (c *Client) Delete(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Delete(c.Url(url), result, errMsg)
	})
}

func (c *Client) Get(url string, p *napping.Params, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Get(c.Url(url), p, result, errMsg)
	})
}

func (c *Client) Head(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Head(c.Url(url), result, errMsg)
	})
}

func (c *Client) Options(url string, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Options(c.Url(url), result, errMsg)
	})
}

func (c *Client) Patch(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Patch(c.Url(url), payload, result, errMsg)
	})
}

func (c *Client) Post(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Post(c.Url(url), payload, result, errMsg)
	})
}

func (c *Client) Put(url string, payload, result interface{}) (*napping.Response, error) {
	return errorPatch(func(errMsg *Error) (*napping.Response, error) {
		return c.Session.Put(c.Url(url), payload, result, errMsg)
	})
}

// Url returns the full http url including host
func (c *Client) Url(urlpath string) string {
	// Ignore errors for now
	parsed, _ := url.Parse(c.Host)

	// Rewrite path
	parsed.Path = path.Join(parsed.Path, urlpath)

	// Return string URL
	return parsed.String()
}

// This is so we include API errors as well as protocol errors here
func errorPatch(f func(err *Error) (*napping.Response, error)) (*napping.Response, error) {
	errMsg := &Error{}
	resp, err := f(errMsg)
	// API error
	if err == nil && errMsg.Code != 0 {
		return resp, errMsg
	}
	// Normal or protcol error
	return resp, err
}
<|endoftext|>


<commit_before>package wipro

import (
	"errors"
	"io"
)

var (
	ErrPrefix        = "proto: "
	ErrUnexpectedEOF = errors.New(ErrPrefix + "unexpected EOF")
	ErrConn          = errors.New(ErrPrefix + "network connection error")
)

type M interface {
	Marshal(*Writer)
	Unmarshal(*Reader)
}

func Send(m M, conn io.Writer) error {
	var w Writer
	m.Marshal(&w)
	if _, err := conn.Write(w.B); err != nil {
		return ErrConn
	}
	return nil
}

func Receive(conn io.Reader, m M) error {
	r := Reader{B: make([]byte, 4)}
	if _, err := conn.Read(r.B); err != nil {
		return ErrConn
	}
	size := int(r.ReadInt32())
	r.Grow(size)
	if _, err := io.ReadAtLeast(conn, r.B[4:], size); err != nil {
		return ErrConn
	}
	r.Reset()
	m.Unmarshal(&r)
	return r.Err
}

type Writer struct {
	B []byte
}

type Reader struct {
	B      []byte
	Offset int
	Err    error
}

func (w *Writer) WriteInt8(i int8) {
	w.B = append(w.B, byte(i))
}

func (r *Reader) ReadInt8() int8 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+1 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset++
	return int8(r.B[i])
}

func (w *Writer) WriteInt16(i int16) {
	w.B = append(w.B, byte(i>>8), byte(i))
}

func (r *Reader) ReadInt16() int16 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+2 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 2
	return int16(r.B[i])<<8 | int16(r.B[i+1])
}

func (w *Writer) WriteInt32(i int32) {
	w.B = append(w.B, byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (w *Writer) WriteUint32(i uint32) {
	w.B = append(w.B, byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (r *Reader) ReadInt32() int32 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+4 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 4
	return int32(r.B[i])<<24 | int32(r.B[i+1])<<16 | int32(r.B[i+2])<<8 | int32(r.B[i+3])
}

func (r *Reader) ReadUint32() uint32 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+4 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 4
	return uint32(r.B[i])<<24 | uint32(r.B[i+1])<<16 | uint32(r.B[i+2])<<8 | uint32(r.B[i+3])
}

func (w *Writer) WriteInt64(i int64) {
	w.B = append(w.B, byte(i>>56), byte(i>>48), byte(i>>40), byte(i>>32), byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (r *Reader) ReadInt64() int64 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+8 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 8
	return int64(r.B[i])<<56 | int64(r.B[i+1])<<48 | int64(r.B[i+2])<<40 | int64(r.B[i+3])<<32 |
		int64(r.B[i+4])<<24 | int64(r.B[i+5])<<16 | int64(r.B[i+6])<<8 | int64(r.B[i+7])
}

func (w *Writer) WriteString(s string) {
	w.WriteInt16(int16(len(s)))
	w.B = append(w.B, s...)
}

func (r *Reader) ReadString() string {
	l := int(r.ReadInt16())
	if r.Err != nil {
		return ""
	}
	i := r.Offset
	if i+l > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return ""
	}
	r.Offset += l
	return string(r.B[i : i+l])
}

func (w *Writer) WriteBytes(bs []byte) {
	w.WriteInt32(int32(len(bs)))
	w.B = append(w.B, bs...)
}

func (r *Reader) ReadBytes() []byte {
	l := int(r.ReadInt32())
	if r.Err != nil {
		return nil
	}
	i := r.Offset
	if i+l > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return nil
	}
	r.Offset += l
	return r.B[i : i+l]
}

func (w *Writer) SetInt32(offset int, i int32) {
	w.B[offset] = byte(i >> 24)
	w.B[offset+1] = byte(i >> 16)
	w.B[offset+2] = byte(i >> 8)
	w.B[offset+3] = byte(i)
}

func (w *Writer) SetUint32(offset int, i uint32) {
	w.B[offset] = byte(i >> 24)
	w.B[offset+1] = byte(i >> 16)
	w.B[offset+2] = byte(i >> 8)
	w.B[offset+3] = byte(i)
}

func (r *Reader) Grow(n int) {
	b := make([]byte, len(r.B)+n)
	copy(b, r.B)
	r.B = b
}

func (r *Reader) Reset() {
	r.Offset = 0
}
<commit_msg>return EOF if size < 0<commit_after>package wipro

import (
	"errors"
	"io"
)

var (
	ErrPrefix        = "proto: "
	ErrUnexpectedEOF = errors.New(ErrPrefix + "unexpected EOF")
	ErrConn          = errors.New(ErrPrefix + "network connection error")
)

type M interface {
	Marshal(*Writer)
	Unmarshal(*Reader)
}

func Send(m M, conn io.Writer) error {
	var w Writer
	m.Marshal(&w)
	if _, err := conn.Write(w.B); err != nil {
		return ErrConn
	}
	return nil
}

func Receive(conn io.Reader, m M) error {
	r := Reader{B: make([]byte, 4)}
	if _, err := conn.Read(r.B); err != nil {
		return ErrConn
	}
	size := int(r.ReadInt32())
	r.Grow(size)
	if _, err := io.ReadAtLeast(conn, r.B[4:], size); err != nil {
		return ErrConn
	}
	r.Reset()
	m.Unmarshal(&r)
	return r.Err
}

type Writer struct {
	B []byte
}

type Reader struct {
	B      []byte
	Offset int
	Err    error
}

func (w *Writer) WriteInt8(i int8) {
	w.B = append(w.B, byte(i))
}

func (r *Reader) ReadInt8() int8 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+1 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset++
	return int8(r.B[i])
}

func (w *Writer) WriteInt16(i int16) {
	w.B = append(w.B, byte(i>>8), byte(i))
}

func (r *Reader) ReadInt16() int16 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+2 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 2
	return int16(r.B[i])<<8 | int16(r.B[i+1])
}

func (w *Writer) WriteInt32(i int32) {
	w.B = append(w.B, byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (w *Writer) WriteUint32(i uint32) {
	w.B = append(w.B, byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (r *Reader) ReadInt32() int32 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+4 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 4
	return int32(r.B[i])<<24 | int32(r.B[i+1])<<16 | int32(r.B[i+2])<<8 | int32(r.B[i+3])
}

func (r *Reader) ReadUint32() uint32 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+4 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 4
	return uint32(r.B[i])<<24 | uint32(r.B[i+1])<<16 | uint32(r.B[i+2])<<8 | uint32(r.B[i+3])
}

func (w *Writer) WriteInt64(i int64) {
	w.B = append(w.B, byte(i>>56), byte(i>>48), byte(i>>40), byte(i>>32), byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}

func (r *Reader) ReadInt64() int64 {
	if r.Err != nil {
		return 0
	}
	i := r.Offset
	if i+8 > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return 0
	}
	r.Offset += 8
	return int64(r.B[i])<<56 | int64(r.B[i+1])<<48 | int64(r.B[i+2])<<40 | int64(r.B[i+3])<<32 |
		int64(r.B[i+4])<<24 | int64(r.B[i+5])<<16 | int64(r.B[i+6])<<8 | int64(r.B[i+7])
}

func (w *Writer) WriteString(s string) {
	w.WriteInt16(int16(len(s)))
	w.B = append(w.B, s...)
}

func (r *Reader) ReadString() string {
	l := int(r.ReadInt16())
	if r.Err != nil {
		return ""
	}
	i := r.Offset
	if i+l > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return ""
	}
	r.Offset += l
	return string(r.B[i : i+l])
}

func (w *Writer) WriteBytes(bs []byte) {
	w.WriteInt32(int32(len(bs)))
	w.B = append(w.B, bs...)
}

func (r *Reader) ReadBytes() []byte {
	l := int(r.ReadInt32())
	if l < 0 {
		r.Err = ErrUnexpectedEOF
		return nil
	}
	if r.Err != nil {
		return nil
	}
	i := r.Offset
	if i+l > len(r.B) {
		r.Err = ErrUnexpectedEOF
		return nil
	}
	r.Offset += l
	return r.B[i : i+l]
}

func (w *Writer) SetInt32(offset int, i int32) {
	w.B[offset] = byte(i >> 24)
	w.B[offset+1] = byte(i >> 16)
	w.B[offset+2] = byte(i >> 8)
	w.B[offset+3] = byte(i)
}

func (w *Writer) SetUint32(offset int, i uint32) {
	w.B[offset] = byte(i >> 24)
	w.B[offset+1] = byte(i >> 16)
	w.B[offset+2] = byte(i >> 8)
	w.B[offset+3] = byte(i)
}

func (r *Reader) Grow(n int) {
	b := make([]byte, len(r.B)+n)
	copy(b, r.B)
	r.B = b
}

func (r *Reader) Reset() {
	r.Offset = 0
}
<|endoftext|>


<commit_before>// Copyright 2018 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package testutil

import (
	"bytes"
	"fmt"
	"io"
	"reflect"

	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/internal"
)

// ToFloat64 collects all Metrics from the provided Collector. It expects that
// this results in exactly one Metric being collected, which must be a Gauge,
// Counter, or Untyped. In all other cases, ToFloat64 panics. ToFloat64 returns
// the value of the collected Metric.
//
// The Collector provided is typically a simple instance of Gauge or Counter, or
// – less commonly – a GaugeVec or CounterVec with exactly one element. But any
// Collector fulfilling the prerequisites described above will do.
//
// Use this function with caution. It is computationally very expensive and thus
// not suited at all to read values from Metrics in regular code. This is really
// only for testing purposes, and even for testing, other approaches are often
// more appropriate (see this package's documentation).
//
// A clear anti-pattern would be to use a metric type from the prometheus
// package to track values that are also needed for something else than the
// exposition of Prometheus metrics. For example, you would like to track the
// number of items in a queue because your code should reject queuing further
// items if a certain limit is reached. It is tempting to track the number of
// items in a prometheus.Gauge, as it is then easily available as a metric for
// exposition, too. However, then you would need to call ToFloat64 in your
// regular code, potentially quite often. The recommended way is to track the
// number of items conventionally (in the way you would have done it without
// considering Prometheus metrics) and then expose the number with a
// prometheus.GaugeFunc.
func ToFloat64(c prometheus.Collector) float64 {
	var (
		m      prometheus.Metric
		mCount int
		mChan  = make(chan prometheus.Metric)
		done   = make(chan struct{})
	)

	go func() {
		for m = range mChan {
			mCount++
		}
		close(done)
	}()

	c.Collect(mChan)
	close(mChan)
	<-done

	if mCount != 1 {
		panic(fmt.Errorf("collected %d metrics instead of exactly 1", mCount))
	}

	pb := &dto.Metric{}
	m.Write(pb)
	if pb.Gauge != nil {
		return pb.Gauge.GetValue()
	}
	if pb.Counter != nil {
		return pb.Counter.GetValue()
	}
	if pb.Untyped != nil {
		return pb.Untyped.GetValue()
	}
	panic(fmt.Errorf("collected a non-gauge/counter/untyped metric: %s", pb))
}

// CollectAndCompare registers the provided Collector with a newly created
// pedantic Registry. It then does the same as GatherAndCompare, gathering the
// metrics from the pedantic Registry.
func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames ...string) error {
	reg := prometheus.NewPedanticRegistry()
	if err := reg.Register(c); err != nil {
		return fmt.Errorf("registering collector failed: %s", err)
	}
	return GatherAndCompare(reg, expected, metricNames...)
}

// GatherAndCompare gathers all metrics from the provided Gatherer and compares
// it to an expected output read from the provided Reader in the Prometheus text
// exposition format. If any metricNames are provided, only metrics with those
// names are compared.
func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...string) error {
	metrics, err := g.Gather()
	if err != nil {
		return fmt.Errorf("gathering metrics failed: %s", err)
	}
	if metricNames != nil {
		metrics = filterMetrics(metrics, metricNames)
	}
	var tp expfmt.TextParser
	expectedMetrics, err := tp.TextToMetricFamilies(expected)
	if err != nil {
		return fmt.Errorf("parsing expected metrics failed: %s", err)
	}

	if !reflect.DeepEqual(metrics, internal.NormalizeMetricFamilies(expectedMetrics)) {
		// Encode the gathered output to the readable text format for comparison.
		var buf1 bytes.Buffer
		enc := expfmt.NewEncoder(&buf1, expfmt.FmtText)
		for _, mf := range metrics {
			if err := enc.Encode(mf); err != nil {
				return fmt.Errorf("encoding result failed: %s", err)
			}
		}
		// Encode normalized expected metrics again to generate them in the same ordering
		// the registry does to spot differences more easily.
		var buf2 bytes.Buffer
		enc = expfmt.NewEncoder(&buf2, expfmt.FmtText)
		for _, mf := range internal.NormalizeMetricFamilies(expectedMetrics) {
			if err := enc.Encode(mf); err != nil {
				return fmt.Errorf("encoding result failed: %s", err)
			}
		}

		return fmt.Errorf(`
metric output does not match expectation; want:

%s

got:

%s
`, buf2.String(), buf1.String())
	}
	return nil
}

func filterMetrics(metrics []*dto.MetricFamily, names []string) []*dto.MetricFamily {
	var filtered []*dto.MetricFamily
	for _, m := range metrics {
		for _, name := range names {
			if m.GetName() == name {
				filtered = append(filtered, m)
				break
			}
		}
	}
	return filtered
}
<commit_msg>Add package documentation<commit_after>// Copyright 2018 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package testutil provides helpers to test code using the prometheus package
// of client_golang.
//
// While writing unit tests to verify correct instrumentation of your code, it's
// a common mistake to mostly test the instrumentation library instead of your
// own code. Rather than verifying that a prometheus.Counter's value has changed
// as expected or that it shows up in the exposition after registration, it is
// in general more robust and more faithful to the concept of unit tests to use
// mock implementations of the prometheus.Counter and prometheus.Registerer
// interfaces that simply assert that the Add or Register methods have been
// called with the expected arguments. However, this might be overkill in simple
// scenarios. The ToFloat64 function is provided for simple inspection of a
// single-value metric, but it has to be used with caution.
//
// End-to-end tests to verify all or larger parts of the metrics exposition can
// be implemented with the CollectAndCompare or GatherAndCompare functions. The
// most appropriate use is not so much testing instrumentation of your code, but
// testing custom prometheus.Collector implementations and in particular whole
// exporters, i.e. programs that retrieve telemetry data from a 3rd party source
// and convert it into Prometheus metrics.
package testutil

import (
	"bytes"
	"fmt"
	"io"
	"reflect"

	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/internal"
)

// ToFloat64 collects all Metrics from the provided Collector. It expects that
// this results in exactly one Metric being collected, which must be a Gauge,
// Counter, or Untyped. In all other cases, ToFloat64 panics. ToFloat64 returns
// the value of the collected Metric.
//
// The Collector provided is typically a simple instance of Gauge or Counter, or
// – less commonly – a GaugeVec or CounterVec with exactly one element. But any
// Collector fulfilling the prerequisites described above will do.
//
// Use this function with caution. It is computationally very expensive and thus
// not suited at all to read values from Metrics in regular code. This is really
// only for testing purposes, and even for testing, other approaches are often
// more appropriate (see this package's documentation).
//
// A clear anti-pattern would be to use a metric type from the prometheus
// package to track values that are also needed for something else than the
// exposition of Prometheus metrics. For example, you would like to track the
// number of items in a queue because your code should reject queuing further
// items if a certain limit is reached. It is tempting to track the number of
// items in a prometheus.Gauge, as it is then easily available as a metric for
// exposition, too. However, then you would need to call ToFloat64 in your
// regular code, potentially quite often. The recommended way is to track the
// number of items conventionally (in the way you would have done it without
// considering Prometheus metrics) and then expose the number with a
// prometheus.GaugeFunc.
func ToFloat64(c prometheus.Collector) float64 {
	var (
		m      prometheus.Metric
		mCount int
		mChan  = make(chan prometheus.Metric)
		done   = make(chan struct{})
	)

	go func() {
		for m = range mChan {
			mCount++
		}
		close(done)
	}()

	c.Collect(mChan)
	close(mChan)
	<-done

	if mCount != 1 {
		panic(fmt.Errorf("collected %d metrics instead of exactly 1", mCount))
	}

	pb := &dto.Metric{}
	m.Write(pb)
	if pb.Gauge != nil {
		return pb.Gauge.GetValue()
	}
	if pb.Counter != nil {
		return pb.Counter.GetValue()
	}
	if pb.Untyped != nil {
		return pb.Untyped.GetValue()
	}
	panic(fmt.Errorf("collected a non-gauge/counter/untyped metric: %s", pb))
}

// CollectAndCompare registers the provided Collector with a newly created
// pedantic Registry. It then does the same as GatherAndCompare, gathering the
// metrics from the pedantic Registry.
func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames ...string) error {
	reg := prometheus.NewPedanticRegistry()
	if err := reg.Register(c); err != nil {
		return fmt.Errorf("registering collector failed: %s", err)
	}
	return GatherAndCompare(reg, expected, metricNames...)
}

// GatherAndCompare gathers all metrics from the provided Gatherer and compares
// it to an expected output read from the provided Reader in the Prometheus text
// exposition format. If any metricNames are provided, only metrics with those
// names are compared.
func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...string) error {
	metrics, err := g.Gather()
	if err != nil {
		return fmt.Errorf("gathering metrics failed: %s", err)
	}
	if metricNames != nil {
		metrics = filterMetrics(metrics, metricNames)
	}
	var tp expfmt.TextParser
	expectedMetrics, err := tp.TextToMetricFamilies(expected)
	if err != nil {
		return fmt.Errorf("parsing expected metrics failed: %s", err)
	}

	if !reflect.DeepEqual(metrics, internal.NormalizeMetricFamilies(expectedMetrics)) {
		// Encode the gathered output to the readable text format for comparison.
		var buf1 bytes.Buffer
		enc := expfmt.NewEncoder(&buf1, expfmt.FmtText)
		for _, mf := range metrics {
			if err := enc.Encode(mf); err != nil {
				return fmt.Errorf("encoding result failed: %s", err)
			}
		}
		// Encode normalized expected metrics again to generate them in the same ordering
		// the registry does to spot differences more easily.
		var buf2 bytes.Buffer
		enc = expfmt.NewEncoder(&buf2, expfmt.FmtText)
		for _, mf := range internal.NormalizeMetricFamilies(expectedMetrics) {
			if err := enc.Encode(mf); err != nil {
				return fmt.Errorf("encoding result failed: %s", err)
			}
		}

		return fmt.Errorf(`
metric output does not match expectation; want:

%s

got:

%s
`, buf2.String(), buf1.String())
	}
	return nil
}

func filterMetrics(metrics []*dto.MetricFamily, names []string) []*dto.MetricFamily {
	var filtered []*dto.MetricFamily
	for _, m := range metrics {
		for _, name := range names {
			if m.GetName() == name {
				filtered = append(filtered, m)
				break
			}
		}
	}
	return filtered
}
<|endoftext|>


<commit_before>package swarm

import (
	"context"
	"sync"

	peer "github.com/libp2p/go-libp2p-peer"
)

type DialFunc func(context.Context, peer.ID) (*Conn, error)

func NewDialSync(dfn DialFunc) *DialSync {
	return &DialSync{
		dials:    make(map[peer.ID]*activeDial),
		dialFunc: dfn,
	}
}

type DialSync struct {
	dials    map[peer.ID]*activeDial
	dialsLk  sync.Mutex
	dialFunc DialFunc
}

type activeDial struct {
	id       peer.ID
	refCnt   int
	refCntLk sync.Mutex
	cancel   func()

	err    error
	conn   *Conn
	waitch chan struct{}

	ds *DialSync
}

func (dr *activeDial) wait(ctx context.Context) (*Conn, error) {
	dr.incref()
	defer dr.decref()
	select {
	case <-dr.waitch:
		return dr.conn, dr.err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (ad *activeDial) incref() {
	ad.refCntLk.Lock()
	defer ad.refCntLk.Unlock()
	ad.refCnt++
}

func (ad *activeDial) decref() {
	ad.refCntLk.Lock()
	ad.refCnt--
	maybeZero := (ad.refCnt <= 0)
	ad.refCntLk.Unlock()

	// make sure to always take locks in correct order.
	if maybeZero {
		ad.ds.dialsLk.Lock()
		ad.refCntLk.Lock()
		// check again after lock swap drop to make sure nobody else called incref
		// in between locks
		if ad.refCnt <= 0 {
			ad.cancel()
			delete(ad.ds.dials, ad.id)
		}
		ad.refCntLk.Unlock()
		ad.ds.dialsLk.Unlock()
	}
}

func (ad *activeDial) start(ctx context.Context) {
	ad.conn, ad.err = ad.ds.dialFunc(ctx, ad.id)
	close(ad.waitch)
	ad.cancel()
}

func (ds *DialSync) getActiveDial(p peer.ID) *activeDial {
	ds.dialsLk.Lock()
	defer ds.dialsLk.Unlock()

	actd, ok := ds.dials[p]
	if !ok {
		adctx, cancel := context.WithCancel(context.Background())
		actd = &activeDial{
			id:     p,
			cancel: cancel,
			waitch: make(chan struct{}),
			ds:     ds,
		}
		ds.dials[p] = actd

		go actd.start(adctx)
	}

	return actd
}

func (ds *DialSync) DialLock(ctx context.Context, p peer.ID) (*Conn, error) {
	return ds.getActiveDial(p).wait(ctx)
}
<commit_msg>increase refcount before dropping dialsLk initially<commit_after>package swarm

import (
	"context"
	"sync"

	peer "github.com/libp2p/go-libp2p-peer"
)

type DialFunc func(context.Context, peer.ID) (*Conn, error)

func NewDialSync(dfn DialFunc) *DialSync {
	return &DialSync{
		dials:    make(map[peer.ID]*activeDial),
		dialFunc: dfn,
	}
}

type DialSync struct {
	dials    map[peer.ID]*activeDial
	dialsLk  sync.Mutex
	dialFunc DialFunc
}

type activeDial struct {
	id       peer.ID
	refCnt   int
	refCntLk sync.Mutex
	cancel   func()

	err    error
	conn   *Conn
	waitch chan struct{}

	ds *DialSync
}

func (dr *activeDial) wait(ctx context.Context) (*Conn, error) {
	defer dr.decref()
	select {
	case <-dr.waitch:
		return dr.conn, dr.err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (ad *activeDial) incref() {
	ad.refCntLk.Lock()
	defer ad.refCntLk.Unlock()
	ad.refCnt++
}

func (ad *activeDial) decref() {
	ad.refCntLk.Lock()
	ad.refCnt--
	maybeZero := (ad.refCnt <= 0)
	ad.refCntLk.Unlock()

	// make sure to always take locks in correct order.
	if maybeZero {
		ad.ds.dialsLk.Lock()
		ad.refCntLk.Lock()
		// check again after lock swap drop to make sure nobody else called incref
		// in between locks
		if ad.refCnt <= 0 {
			ad.cancel()
			delete(ad.ds.dials, ad.id)
		}
		ad.refCntLk.Unlock()
		ad.ds.dialsLk.Unlock()
	}
}

func (ad *activeDial) start(ctx context.Context) {
	ad.conn, ad.err = ad.ds.dialFunc(ctx, ad.id)
	close(ad.waitch)
	ad.cancel()
}

func (ds *DialSync) getActiveDial(p peer.ID) *activeDial {
	ds.dialsLk.Lock()
	defer ds.dialsLk.Unlock()

	actd, ok := ds.dials[p]
	if !ok {
		adctx, cancel := context.WithCancel(context.Background())
		actd = &activeDial{
			id:     p,
			cancel: cancel,
			waitch: make(chan struct{}),
			ds:     ds,
		}
		ds.dials[p] = actd

		go actd.start(adctx)
	}

	// increase ref count before dropping dialsLk
	actd.incref()

	return actd
}

func (ds *DialSync) DialLock(ctx context.Context, p peer.ID) (*Conn, error) {
	return ds.getActiveDial(p).wait(ctx)
}
<|endoftext|>