<commit_before>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/fatih/color"
	"github.com/kballard/go-shellquote"
	"golang.org/x/sys/unix"
)

var validationMessages []string

// Debugf is a helper function for debug logging if global variable debug is set to true
func Debugf(s string) {
	if debug != false {
		pc, _, _, _ := runtime.Caller(1)
		callingFunctionName := strings.Split(runtime.FuncForPC(pc).Name(), ".")[len(strings.Split(runtime.FuncForPC(pc).Name(), "."))-1]
		if strings.HasPrefix(callingFunctionName, "func") {
			// check for anonymous function names
			log.Print("DEBUG " + fmt.Sprint(s))
		} else {
			log.Print("DEBUG " + callingFunctionName + "(): " + fmt.Sprint(s))
		}
	}
}

// Verbosef is a helper function for verbose logging if global variable verbose is set to true
func Verbosef(s string) {
	if debug != false || verbose != false {
		log.Print(fmt.Sprint(s))
	}
}

// Infof is a helper function for info logging if global variable info is set to true
func Infof(s string) {
	if debug != false || verbose != false || info != false {
		color.Green(s)
	}
}

// Validatef is a helper function for validation logging if global variable validate is set to true
func Validatef() {
	if len(validationMessages) > 0 {
		for _, message := range validationMessages {
			color.New(color.FgRed).Fprintln(os.Stdout, message)
		}
		os.Exit(1)
	} else {
		color.New(color.FgGreen).Fprintln(os.Stdout, "Configuration successfully parsed.")
		os.Exit(0)
	}
}

// Warnf is a helper function for warning logging
func Warnf(s string) {
	color.Set(color.FgYellow)
	fmt.Println(s)
	color.Unset()
}

// Fatalf is a helper function for fatal logging
func Fatalf(s string) {
	if validate {
		validationMessages = append(validationMessages, s)
	} else {
		color.New(color.FgRed).Fprintln(os.Stderr, s)
		os.Exit(1)
	}
}

// fileExists checks if the given file exists and returns a bool
func fileExists(file string) bool {
	//Debugf("checking for file existence " + file)
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

// isDir checks if the given dir exists and returns a bool
func isDir(dir string) bool {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return false
	}
	if fi.Mode().IsDir() {
		return true
	}
	return false
}

// normalizeDir removes from the given directory path multiple redundant slashes and adds a trailing slash
func normalizeDir(dir string) string {
	if strings.Count(dir, "//") > 0 {
		dir = normalizeDir(strings.Replace(dir, "//", "/", -1))
	} else {
		if !strings.HasSuffix(dir, "/") {
			dir = dir + "/"
		}
	}
	return dir
}

// checkDirAndCreate tests if the given directory exists and tries to create it
func checkDirAndCreate(dir string, name string) string {
	if !dryRun {
		if len(dir) != 0 {
			if !fileExists(dir) {
				//log.Printf("checkDirAndCreate(): trying to create dir '%s' as %s", dir, name){
				if err := os.MkdirAll(dir, 0777); err != nil {
					Fatalf("checkDirAndCreate(): Error: failed to create directory: " + dir)
				}
			} else {
				if !isDir(dir) {
					Fatalf("checkDirAndCreate(): Error: " + dir + " exists, but is not a directory! Exiting!")
				} else {
					if unix.Access(dir, unix.W_OK) != nil {
						Fatalf("checkDirAndCreate(): Error: " + dir + " exists, but is not writable! Exiting!")
					}
				}
			}
		} else {
			// TODO make dir optional
			Fatalf("checkDirAndCreate(): Error: dir setting '" + name + "' missing! Exiting!")
		}
	}
	dir = normalizeDir(dir)
	Debugf("Using as " + name + ": " + dir)
	return dir
}

func createOrPurgeDir(dir string, callingFunction string) {
	if !dryRun {
		if !fileExists(dir) {
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.MkdirAll(dir, 0777)
		} else {
			Debugf("Trying to remove: " + dir + " called from " + callingFunction)
			if err := os.RemoveAll(dir); err != nil {
				log.Print("createOrPurgeDir(): error: removing dir failed", err)
			}
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.MkdirAll(dir, 0777)
		}
	}
}

func purgeDir(dir string, callingFunction string) {
	if !fileExists(dir) {
		Debugf("Unnecessary to remove dir: " + dir + " it does not exist. Called from " + callingFunction)
	} else {
		Debugf("Trying to remove: " + dir + " called from " + callingFunction)
		if err := os.RemoveAll(dir); err != nil {
			log.Print("purgeDir(): os.RemoveAll() error: removing dir failed: ", err)
			if err = syscall.Unlink(dir); err != nil {
				log.Print("purgeDir(): syscall.Unlink() error: removing link failed: ", err)
			}
		}
	}
}

func executeCommand(command string, timeout int, allowFail bool) ExecResult {
	Debugf("Executing " + command)
	parts := strings.SplitN(command, " ", 2)
	cmd := parts[0]
	cmdArgs := []string{}
	if len(parts) > 1 {
		args, err := shellquote.Split(parts[1])
		if err != nil {
			Debugf("err: " + fmt.Sprint(err))
		} else {
			cmdArgs = args
		}
	}

	before := time.Now()
	out, err := exec.Command(cmd, cmdArgs...).CombinedOutput()
	duration := time.Since(before).Seconds()
	er := ExecResult{0, string(out)}
	if msg, ok := err.(*exec.ExitError); ok { // there is error code
		er.returnCode = msg.Sys().(syscall.WaitStatus).ExitStatus()
	}
	if (allowFail || config.UseCacheFallback) && err != nil {
		Debugf("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	} else {
		Verbosef("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	}
	if err != nil {
		if !allowFail && !config.UseCacheFallback && !config.RetryGitCommands {
			if cmd == "git" {
				Fatalf("executeCommand(): git command failed: " + command + " " + err.Error() + "\nOutput: " + string(out) +
					"\nIf you are using GitLab please ensure that you've added your deploy key to your repository")
			} else {
				Fatalf("executeCommand(): command failed: " + command + " " + err.Error() + "\nOutput: " + string(out))
			}
		} else {
			er.returnCode = 1
			er.output = fmt.Sprint(err)
		}
	}
	return er
}

// funcName return the function name as a string
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	completeFuncname := runtime.FuncForPC(pc).Name()
	return strings.Split(completeFuncname, ".")[len(strings.Split(completeFuncname, "."))-1]
}

func timeTrack(start time.Time, name string) {
	duration := time.Since(start).Seconds()
	if name == "resolveForgeModules" {
		syncForgeTime = duration
	} else if name == "resolveGitRepositories" {
		syncGitTime = duration
	}
	Debugf(name + "() took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
}

// checkForAndExecutePostrunCommand check if a `postrun` command was specified in the g10k config and executes it
func checkForAndExecutePostrunCommand() {
	if len(config.PostRunCommand) > 0 {
		postrunCommandString := strings.Join(config.PostRunCommand, " ")
		postrunCommandString = strings.Replace(postrunCommandString, "$modifieddirs", strings.Join(needSyncDirs, " "), -1)

		needSyncEnvText := ""
		for needSyncEnv := range needSyncEnvs {
			needSyncEnvText += needSyncEnv + " "
		}
		postrunCommandString = strings.Replace(postrunCommandString, "$modifiedenvs", needSyncEnvText, -1)
		postrunCommandString = strings.Replace(postrunCommandString, "$branchparam", branchParam, -1)

		er := executeCommand(postrunCommandString, config.Timeout, false)
		Debugf("postrun command '" + postrunCommandString + "' terminated with exit code " + strconv.Itoa(er.returnCode))
	}
}

// getSha256sumFile return the SHA256 hash sum of the given file
func getSha256sumFile(file string) string {
	// https://golang.org/pkg/crypto/sha256/#New
	f, err := os.Open(file)
	if err != nil {
		Fatalf("failed to open file " + file + " to calculate SHA256 sum. Error: " + err.Error())
	}
	defer f.Close()

	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		Fatalf("failed to calculate SHA256 sum of file " + file + " Error: " + err.Error())
	}

	return hex.EncodeToString(h.Sum(nil))
}

// moveFile uses io.Copy to create a copy of the given file https://stackoverflow.com/a/50741908/682847
func moveFile(sourcePath, destPath string, deleteSourceFileToggle bool) error {
	inputFile, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("Couldn't open source file: %s", err)
	}
	outputFile, err := os.Create(destPath)
	if err != nil {
		inputFile.Close()
		return fmt.Errorf("Couldn't open dest file: %s", err)
	}
	defer outputFile.Close()
	_, err = io.Copy(outputFile, inputFile)
	inputFile.Close()
	if err != nil {
		return fmt.Errorf("Writing to output file failed: %s", err)
	}
	if deleteSourceFileToggle {
		// The copy was successful, so now delete the original file
		err = os.Remove(sourcePath)
		if err != nil {
			return fmt.Errorf("Failed removing original file: %s", err)
		}
	}
	return nil
}

func stringSliceContains(slice []string, element string) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}
	return false
}

func writeStructJSONFile(file string, v interface{}) {
	content, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		Warnf("Could not encode JSON file " + file + " " + err.Error())
	}

	err = ioutil.WriteFile(file, content, 0644)
	if err != nil {
		Warnf("Could not write JSON file " + file + " " + err.Error())
	}

}

func readDeployResultFile(file string) DeployResult {
	// Open our jsonFile
	jsonFile, err := os.Open(file)
	// if we os.Open returns an error then handle it
	if err != nil {
		Warnf("Could not open JSON file " + file + " " + err.Error())
	}
	defer jsonFile.Close()

	byteValue, err := ioutil.ReadAll(jsonFile)
	if err != nil {
		Warnf("Could not read JSON file " + file + " " + err.Error())
	}

	var dr DeployResult
	json.Unmarshal([]byte(byteValue), &dr)

	return dr

}
<commit_msg>big rework file/dir paths<commit_after>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/fatih/color"
	"github.com/kballard/go-shellquote"
	"golang.org/x/sys/unix"
)

var validationMessages []string

// Debugf is a helper function for debug logging if global variable debug is set to true
func Debugf(s string) {
	if debug != false {
		pc, _, _, _ := runtime.Caller(1)
		callingFunctionName := strings.Split(runtime.FuncForPC(pc).Name(), ".")[len(strings.Split(runtime.FuncForPC(pc).Name(), "."))-1]
		if strings.HasPrefix(callingFunctionName, "func") {
			// check for anonymous function names
			log.Print("DEBUG " + fmt.Sprint(s))
		} else {
			log.Print("DEBUG " + callingFunctionName + "(): " + fmt.Sprint(s))
		}
	}
}

// Verbosef is a helper function for verbose logging if global variable verbose is set to true
func Verbosef(s string) {
	if debug != false || verbose != false {
		log.Print(fmt.Sprint(s))
	}
}

// Infof is a helper function for info logging if global variable info is set to true
func Infof(s string) {
	if debug != false || verbose != false || info != false {
		color.Green(s)
	}
}

// Validatef is a helper function for validation logging if global variable validate is set to true
func Validatef() {
	if len(validationMessages) > 0 {
		for _, message := range validationMessages {
			color.New(color.FgRed).Fprintln(os.Stdout, message)
		}
		os.Exit(1)
	} else {
		color.New(color.FgGreen).Fprintln(os.Stdout, "Configuration successfully parsed.")
		os.Exit(0)
	}
}

// Warnf is a helper function for warning logging
func Warnf(s string) {
	color.Set(color.FgYellow)
	fmt.Println(s)
	color.Unset()
}

// Fatalf is a helper function for fatal logging
func Fatalf(s string) {
	if validate {
		validationMessages = append(validationMessages, s)
	} else {
		color.New(color.FgRed).Fprintln(os.Stderr, s)
		os.Exit(1)
	}
}

// fileExists checks if the given file exists and returns a bool
func fileExists(file string) bool {
	//Debugf("checking for file existence " + file)
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

// isDir checks if the given dir exists and returns a bool
func isDir(dir string) bool {
	fi, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return false
	}
	if fi.Mode().IsDir() {
		return true
	}
	return false
}

// normalizeDir removes from the given directory path multiple redundant slashes and removes a trailing slash
func normalizeDir(dir string) string {
	if strings.Count(dir, "//") > 0 {
		dir = normalizeDir(strings.Replace(dir, "//", "/", -1))
	}
	dir = strings.TrimSuffix(dir, "/")
	return dir
}

// checkDirAndCreate tests if the given directory exists and tries to create it
func checkDirAndCreate(dir string, name string) string {
	if !dryRun {
		if len(dir) != 0 {
			if !fileExists(dir) {
				//log.Printf("checkDirAndCreate(): trying to create dir '%s' as %s", dir, name){
				if err := os.MkdirAll(dir, 0777); err != nil {
					Fatalf("checkDirAndCreate(): Error: failed to create directory: " + dir)
				}
			} else {
				if !isDir(dir) {
					Fatalf("checkDirAndCreate(): Error: " + dir + " exists, but is not a directory! Exiting!")
				} else {
					if unix.Access(dir, unix.W_OK) != nil {
						Fatalf("checkDirAndCreate(): Error: " + dir + " exists, but is not writable! Exiting!")
					}
				}
			}
		} else {
			// TODO make dir optional
			Fatalf("checkDirAndCreate(): Error: dir setting '" + name + "' missing! Exiting!")
		}
	}
	dir = normalizeDir(dir)
	Debugf("Using as " + name + ": " + dir)
	return dir
}

func createOrPurgeDir(dir string, callingFunction string) {
	if !dryRun {
		if !fileExists(dir) {
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.MkdirAll(dir, 0777)
		} else {
			Debugf("Trying to remove: " + dir + " called from " + callingFunction)
			if err := os.RemoveAll(dir); err != nil {
				log.Print("createOrPurgeDir(): error: removing dir failed", err)
			}
			Debugf("Trying to create dir: " + dir + " called from " + callingFunction)
			os.MkdirAll(dir, 0777)
		}
	}
}

func purgeDir(dir string, callingFunction string) {
	if !fileExists(dir) {
		Debugf("Unnecessary to remove dir: " + dir + " it does not exist. Called from " + callingFunction)
	} else {
		Debugf("Trying to remove: " + dir + " called from " + callingFunction)
		if err := os.RemoveAll(dir); err != nil {
			log.Print("purgeDir(): os.RemoveAll() error: removing dir failed: ", err)
			if err = syscall.Unlink(dir); err != nil {
				log.Print("purgeDir(): syscall.Unlink() error: removing link failed: ", err)
			}
		}
	}
}

func executeCommand(command string, timeout int, allowFail bool) ExecResult {
	Debugf("Executing " + command)
	parts := strings.SplitN(command, " ", 2)
	cmd := parts[0]
	cmdArgs := []string{}
	if len(parts) > 1 {
		args, err := shellquote.Split(parts[1])
		if err != nil {
			Debugf("err: " + fmt.Sprint(err))
		} else {
			cmdArgs = args
		}
	}

	before := time.Now()
	out, err := exec.Command(cmd, cmdArgs...).CombinedOutput()
	duration := time.Since(before).Seconds()
	er := ExecResult{0, string(out)}
	if msg, ok := err.(*exec.ExitError); ok { // there is error code
		er.returnCode = msg.Sys().(syscall.WaitStatus).ExitStatus()
	}
	if (allowFail || config.UseCacheFallback) && err != nil {
		Debugf("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	} else {
		Verbosef("Executing " + command + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
	}
	if err != nil {
		if !allowFail && !config.UseCacheFallback && !config.RetryGitCommands {
			if cmd == "git" {
				Fatalf("executeCommand(): git command failed: " + command + " " + err.Error() + "\nOutput: " + string(out) +
					"\nIf you are using GitLab please ensure that you've added your deploy key to your repository")
			} else {
				Fatalf("executeCommand(): command failed: " + command + " " + err.Error() + "\nOutput: " + string(out))
			}
		} else {
			er.returnCode = 1
			er.output = fmt.Sprint(err)
		}
	}
	return er
}

// funcName return the function name as a string
func funcName() string {
	pc, _, _, _ := runtime.Caller(1)
	completeFuncname := runtime.FuncForPC(pc).Name()
	return strings.Split(completeFuncname, ".")[len(strings.Split(completeFuncname, "."))-1]
}

func timeTrack(start time.Time, name string) {
	duration := time.Since(start).Seconds()
	if name == "resolveForgeModules" {
		syncForgeTime = duration
	} else if name == "resolveGitRepositories" {
		syncGitTime = duration
	}
	Debugf(name + "() took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")
}

// checkForAndExecutePostrunCommand check if a `postrun` command was specified in the g10k config and executes it
func checkForAndExecutePostrunCommand() {
	if len(config.PostRunCommand) > 0 {
		postrunCommandString := strings.Join(config.PostRunCommand, " ")
		postrunCommandString = strings.Replace(postrunCommandString, "$modifieddirs", strings.Join(needSyncDirs, " "), -1)

		needSyncEnvText := ""
		for needSyncEnv := range needSyncEnvs {
			needSyncEnvText += needSyncEnv + " "
		}
		postrunCommandString = strings.Replace(postrunCommandString, "$modifiedenvs", needSyncEnvText, -1)
		postrunCommandString = strings.Replace(postrunCommandString, "$branchparam", branchParam, -1)

		er := executeCommand(postrunCommandString, config.Timeout, false)
		Debugf("postrun command '" + postrunCommandString + "' terminated with exit code " + strconv.Itoa(er.returnCode))
	}
}

// getSha256sumFile return the SHA256 hash sum of the given file
func getSha256sumFile(file string) string {
	// https://golang.org/pkg/crypto/sha256/#New
	f, err := os.Open(file)
	if err != nil {
		Fatalf("failed to open file " + file + " to calculate SHA256 sum. Error: " + err.Error())
	}
	defer f.Close()

	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		Fatalf("failed to calculate SHA256 sum of file " + file + " Error: " + err.Error())
	}

	return hex.EncodeToString(h.Sum(nil))
}

// moveFile uses io.Copy to create a copy of the given file https://stackoverflow.com/a/50741908/682847
func moveFile(sourcePath, destPath string, deleteSourceFileToggle bool) error {
	inputFile, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("Couldn't open source file: %s", err)
	}
	outputFile, err := os.Create(destPath)
	if err != nil {
		inputFile.Close()
		return fmt.Errorf("Couldn't open dest file: %s", err)
	}
	defer outputFile.Close()
	_, err = io.Copy(outputFile, inputFile)
	inputFile.Close()
	if err != nil {
		return fmt.Errorf("Writing to output file failed: %s", err)
	}
	if deleteSourceFileToggle {
		// The copy was successful, so now delete the original file
		err = os.Remove(sourcePath)
		if err != nil {
			return fmt.Errorf("Failed removing original file: %s", err)
		}
	}
	return nil
}

func stringSliceContains(slice []string, element string) bool {
	for _, e := range slice {
		if e == element {
			return true
		}
	}
	return false
}

func writeStructJSONFile(file string, v interface{}) {
	content, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		Warnf("Could not encode JSON file " + file + " " + err.Error())
	}

	err = ioutil.WriteFile(file, content, 0644)
	if err != nil {
		Warnf("Could not write JSON file " + file + " " + err.Error())
	}

}

func readDeployResultFile(file string) DeployResult {
	// Open our jsonFile
	jsonFile, err := os.Open(file)
	// if we os.Open returns an error then handle it
	if err != nil {
		Warnf("Could not open JSON file " + file + " " + err.Error())
	}
	defer jsonFile.Close()

	byteValue, err := ioutil.ReadAll(jsonFile)
	if err != nil {
		Warnf("Could not read JSON file " + file + " " + err.Error())
	}

	var dr DeployResult
	json.Unmarshal([]byte(byteValue), &dr)

	return dr

}
<|endoftext|>


<commit_before><commit_msg>PasswordHash obtained from Firebase Auth backend needs to be base64URL decode before import (#480)<commit_after><|endoftext|>


<commit_before>// Copyright 2013 Prometheus Team
// 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 retrieval

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/prometheus/client_golang/extraction"

	clientmodel "github.com/prometheus/client_golang/model"
)

const (
	InstanceLabel clientmodel.LabelName = "instance"
	// The metric name for the synthetic health variable.
	ScrapeHealthMetricName clientmodel.LabelValue = "up"
)

var localhostRepresentations = []string{"http://127.0.0.1", "http://localhost"}

// The state of the given Target.
type TargetState int

func (t TargetState) String() string {
	switch t {
	case UNKNOWN:
		return "UNKNOWN"
	case ALIVE:
		return "ALIVE"
	case UNREACHABLE:
		return "UNREACHABLE"
	}

	panic("unknown state")
}

const (
	// The Target has not been seen; we know nothing about it, except that it is
	// on our docket for examination.
	UNKNOWN TargetState = iota
	// The Target has been found and successfully queried.
	ALIVE
	// The Target was either historically found or not found and then determined
	// to be unhealthy by either not responding or disappearing.
	UNREACHABLE
)

// A healthReporter is a type that can provide insight into its health state.
//
// It mainly exists for testability reasons to decouple the scheduler behaviors
// from fully-fledged Target and other types.
type healthReporter interface {
	// Report the last-known health state for this target.
	State() TargetState
}

// A Target represents an endpoint that should be interrogated for metrics.
//
// The protocol described by this type will likely change in future iterations,
// as it offers no good support for aggregated targets and fan out.  Thusly,
// it is likely that the current Target and target uses will be
// wrapped with some resolver type.
//
// For the future, the Target protocol will abstract away the exact means that
// metrics are retrieved and deserialized from the given instance to which it
// refers.
type Target interface {
	// Retrieve values from this target.
	//
	// earliest refers to the soonest available opportunity to reschedule the
	// target for a future retrieval.  It is up to the underlying scheduler type,
	// alluded to in the scheduledFor function, to use this as it wants to.  The
	// current use case is to create a common batching time for scraping multiple
	// Targets in the future through the TargetPool.
	Scrape(earliest time.Time, results chan<- *extraction.Result) error
	// Fulfill the healthReporter interface.
	State() TargetState
	// Report the soonest time at which this Target may be scheduled for
	// retrieval.  This value needn't convey that the operation occurs at this
	// time, but it should occur no sooner than it.
	//
	// Right now, this is used as the sorting key in TargetPool.
	scheduledFor() time.Time
	// Return the last encountered scrape error, if any.
	LastError() error
	// The address to which the Target corresponds.  Out of all of the available
	// points in this interface, this one is the best candidate to change given
	// the ways to express the endpoint.
	Address() string
	// The address as seen from other hosts. References to localhost are resolved
	// to the address of the prometheus server.
	GlobalAddress() string
	// Return the target's base labels.
	BaseLabels() clientmodel.LabelSet
	// Merge a new externally supplied target definition (e.g. with changed base
	// labels) into an old target definition for the same endpoint. Preserve
	// remaining information - like health state - from the old target.
	Merge(newTarget Target)
}

// target is a Target that refers to a singular HTTP or HTTPS endpoint.
type target struct {
	// scheduler provides the scheduling strategy that is used to formulate what
	// is returned in Target.scheduledFor.
	scheduler scheduler
	// The current health state of the target.
	state TargetState
	// The last encountered scrape error, if any.
	lastError error

	address string
	// What is the deadline for the HTTP or HTTPS against this endpoint.
	Deadline time.Duration
	// Any base labels that are added to this target and its metrics.
	baseLabels clientmodel.LabelSet
	// The HTTP client used to scrape the target's endpoint.
	client http.Client
}

// Furnish a reasonably configured target for querying.
func NewTarget(address string, deadline time.Duration, baseLabels clientmodel.LabelSet) Target {
	target := &target{
		address:    address,
		Deadline:   deadline,
		baseLabels: baseLabels,
		client:     NewDeadlineClient(deadline),
	}

	scheduler := &healthScheduler{
		target: target,
	}
	target.scheduler = scheduler

	return target
}

func (t *target) recordScrapeHealth(results chan<- *extraction.Result, timestamp time.Time, healthy bool) {
	metric := clientmodel.Metric{}
	for label, value := range t.baseLabels {
		metric[label] = value
	}
	metric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(ScrapeHealthMetricName)
	metric[InstanceLabel] = clientmodel.LabelValue(t.Address())

	healthValue := clientmodel.SampleValue(0)
	if healthy {
		healthValue = clientmodel.SampleValue(1)
	}

	sample := &clientmodel.Sample{
		Metric:    metric,
		Timestamp: timestamp,
		Value:     healthValue,
	}

	results <- &extraction.Result{
		Err:     nil,
		Samples: clientmodel.Samples{sample},
	}
}

func (t *target) Scrape(earliest time.Time, results chan<- *extraction.Result) (err error) {
	now := time.Now()
	futureState := t.state

	if err = t.scrape(now, results); err != nil {
		t.recordScrapeHealth(results, now, false)
		futureState = UNREACHABLE
	} else {
		t.recordScrapeHealth(results, now, true)
		futureState = ALIVE
	}

	t.scheduler.Reschedule(earliest, futureState)
	t.state = futureState
	t.lastError = err

	return err
}

const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,application/json;schema=prometheus/telemetry;version=0.0.2;q=0.2,*/*;q=0.1`

func (t *target) scrape(timestamp time.Time, results chan<- *extraction.Result) (err error) {
	defer func(start time.Time) {
		ms := float64(time.Since(start)) / float64(time.Millisecond)
		labels := map[string]string{address: t.Address(), outcome: success}
		if err != nil {
			labels[outcome] = failure
		}

		targetOperationLatencies.Add(labels, ms)
		targetOperations.Increment(labels)
	}(time.Now())

	req, err := http.NewRequest("GET", t.Address(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Add("Accept", acceptHeader)

	resp, err := t.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	processor, err := extraction.ProcessorForRequestHeader(resp.Header)
	if err != nil {
		return err
	}

	// XXX: This is a wart; we need to handle this more gracefully down the
	//      road, especially once we have service discovery support.
	baseLabels := clientmodel.LabelSet{InstanceLabel: clientmodel.LabelValue(t.Address())}
	for baseLabel, baseValue := range t.baseLabels {
		baseLabels[baseLabel] = baseValue
	}

	processOptions := &extraction.ProcessOptions{
		Timestamp:  timestamp,
		BaseLabels: baseLabels,
	}

	return processor.ProcessSingle(resp.Body, results, processOptions)
}

func (t target) State() TargetState {
	return t.state
}

func (t target) scheduledFor() time.Time {
	return t.scheduler.ScheduledFor()
}

func (t target) LastError() error {
	return t.lastError
}

func (t target) Address() string {
	return t.address
}

func (t target) GlobalAddress() string {
	address := t.address
	hostname, err := os.Hostname()
	if err != nil {
		log.Printf("Couldn't get hostname: %s, returning target.Address()", err)
		return address
	}
	for _, localhostRepresentation := range localhostRepresentations {
		address = strings.Replace(address, localhostRepresentation, fmt.Sprintf("http://%s", hostname), -1)
	}
	return address
}

func (t target) BaseLabels() clientmodel.LabelSet {
	return t.baseLabels
}

// Merge a new externally supplied target definition (e.g. with changed base
// labels) into an old target definition for the same endpoint. Preserve
// remaining information - like health state - from the old target.
func (t *target) Merge(newTarget Target) {
	if t.Address() != newTarget.Address() {
		panic("targets don't refer to the same endpoint")
	}
	t.baseLabels = newTarget.BaseLabels()
}

type targets []Target

func (t targets) Len() int {
	return len(t)
}

func (t targets) Less(i, j int) bool {
	return t[i].scheduledFor().Before(t[j].scheduledFor())
}

func (t targets) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<commit_msg>Completely extract response payload for decoding.<commit_after>// Copyright 2013 Prometheus Team
// 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 retrieval

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/prometheus/client_golang/extraction"

	clientmodel "github.com/prometheus/client_golang/model"
)

const (
	InstanceLabel clientmodel.LabelName = "instance"
	// The metric name for the synthetic health variable.
	ScrapeHealthMetricName clientmodel.LabelValue = "up"
)

var localhostRepresentations = []string{"http://127.0.0.1", "http://localhost"}

// The state of the given Target.
type TargetState int

func (t TargetState) String() string {
	switch t {
	case UNKNOWN:
		return "UNKNOWN"
	case ALIVE:
		return "ALIVE"
	case UNREACHABLE:
		return "UNREACHABLE"
	}

	panic("unknown state")
}

const (
	// The Target has not been seen; we know nothing about it, except that it is
	// on our docket for examination.
	UNKNOWN TargetState = iota
	// The Target has been found and successfully queried.
	ALIVE
	// The Target was either historically found or not found and then determined
	// to be unhealthy by either not responding or disappearing.
	UNREACHABLE
)

// A healthReporter is a type that can provide insight into its health state.
//
// It mainly exists for testability reasons to decouple the scheduler behaviors
// from fully-fledged Target and other types.
type healthReporter interface {
	// Report the last-known health state for this target.
	State() TargetState
}

// A Target represents an endpoint that should be interrogated for metrics.
//
// The protocol described by this type will likely change in future iterations,
// as it offers no good support for aggregated targets and fan out.  Thusly,
// it is likely that the current Target and target uses will be
// wrapped with some resolver type.
//
// For the future, the Target protocol will abstract away the exact means that
// metrics are retrieved and deserialized from the given instance to which it
// refers.
type Target interface {
	// Retrieve values from this target.
	//
	// earliest refers to the soonest available opportunity to reschedule the
	// target for a future retrieval.  It is up to the underlying scheduler type,
	// alluded to in the scheduledFor function, to use this as it wants to.  The
	// current use case is to create a common batching time for scraping multiple
	// Targets in the future through the TargetPool.
	Scrape(earliest time.Time, results chan<- *extraction.Result) error
	// Fulfill the healthReporter interface.
	State() TargetState
	// Report the soonest time at which this Target may be scheduled for
	// retrieval.  This value needn't convey that the operation occurs at this
	// time, but it should occur no sooner than it.
	//
	// Right now, this is used as the sorting key in TargetPool.
	scheduledFor() time.Time
	// Return the last encountered scrape error, if any.
	LastError() error
	// The address to which the Target corresponds.  Out of all of the available
	// points in this interface, this one is the best candidate to change given
	// the ways to express the endpoint.
	Address() string
	// The address as seen from other hosts. References to localhost are resolved
	// to the address of the prometheus server.
	GlobalAddress() string
	// Return the target's base labels.
	BaseLabels() clientmodel.LabelSet
	// Merge a new externally supplied target definition (e.g. with changed base
	// labels) into an old target definition for the same endpoint. Preserve
	// remaining information - like health state - from the old target.
	Merge(newTarget Target)
}

// target is a Target that refers to a singular HTTP or HTTPS endpoint.
type target struct {
	// scheduler provides the scheduling strategy that is used to formulate what
	// is returned in Target.scheduledFor.
	scheduler scheduler
	// The current health state of the target.
	state TargetState
	// The last encountered scrape error, if any.
	lastError error

	address string
	// What is the deadline for the HTTP or HTTPS against this endpoint.
	Deadline time.Duration
	// Any base labels that are added to this target and its metrics.
	baseLabels clientmodel.LabelSet
	// The HTTP client used to scrape the target's endpoint.
	client http.Client
}

// Furnish a reasonably configured target for querying.
func NewTarget(address string, deadline time.Duration, baseLabels clientmodel.LabelSet) Target {
	target := &target{
		address:    address,
		Deadline:   deadline,
		baseLabels: baseLabels,
		client:     NewDeadlineClient(deadline),
	}

	scheduler := &healthScheduler{
		target: target,
	}
	target.scheduler = scheduler

	return target
}

func (t *target) recordScrapeHealth(results chan<- *extraction.Result, timestamp time.Time, healthy bool) {
	metric := clientmodel.Metric{}
	for label, value := range t.baseLabels {
		metric[label] = value
	}
	metric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(ScrapeHealthMetricName)
	metric[InstanceLabel] = clientmodel.LabelValue(t.Address())

	healthValue := clientmodel.SampleValue(0)
	if healthy {
		healthValue = clientmodel.SampleValue(1)
	}

	sample := &clientmodel.Sample{
		Metric:    metric,
		Timestamp: timestamp,
		Value:     healthValue,
	}

	results <- &extraction.Result{
		Err:     nil,
		Samples: clientmodel.Samples{sample},
	}
}

func (t *target) Scrape(earliest time.Time, results chan<- *extraction.Result) (err error) {
	now := time.Now()
	futureState := t.state

	if err = t.scrape(now, results); err != nil {
		t.recordScrapeHealth(results, now, false)
		futureState = UNREACHABLE
	} else {
		t.recordScrapeHealth(results, now, true)
		futureState = ALIVE
	}

	t.scheduler.Reschedule(earliest, futureState)
	t.state = futureState
	t.lastError = err

	return err
}

const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,application/json;schema=prometheus/telemetry;version=0.0.2;q=0.2,*/*;q=0.1`

func (t *target) scrape(timestamp time.Time, results chan<- *extraction.Result) (err error) {
	defer func(start time.Time) {
		ms := float64(time.Since(start)) / float64(time.Millisecond)
		labels := map[string]string{address: t.Address(), outcome: success}
		if err != nil {
			labels[outcome] = failure
		}

		targetOperationLatencies.Add(labels, ms)
		targetOperations.Increment(labels)
	}(time.Now())

	req, err := http.NewRequest("GET", t.Address(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Add("Accept", acceptHeader)

	resp, err := t.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	processor, err := extraction.ProcessorForRequestHeader(resp.Header)
	if err != nil {
		return err
	}

	// XXX: This is a wart; we need to handle this more gracefully down the
	//      road, especially once we have service discovery support.
	baseLabels := clientmodel.LabelSet{InstanceLabel: clientmodel.LabelValue(t.Address())}
	for baseLabel, baseValue := range t.baseLabels {
		baseLabels[baseLabel] = baseValue
	}

	processOptions := &extraction.ProcessOptions{
		Timestamp:  timestamp,
		BaseLabels: baseLabels,
	}

	// N.B. - It is explicitly required to extract the entire payload before
	//        attempting to deserialize, as the underlying reader expects will
	//        interpret pending data as a truncated message.
	buf := new(bytes.Buffer)
	if _, err := buf.ReadFrom(resp.Body); err != nil {
		return err
	}

	return processor.ProcessSingle(buf, results, processOptions)
}

func (t target) State() TargetState {
	return t.state
}

func (t target) scheduledFor() time.Time {
	return t.scheduler.ScheduledFor()
}

func (t target) LastError() error {
	return t.lastError
}

func (t target) Address() string {
	return t.address
}

func (t target) GlobalAddress() string {
	address := t.address
	hostname, err := os.Hostname()
	if err != nil {
		log.Printf("Couldn't get hostname: %s, returning target.Address()", err)
		return address
	}
	for _, localhostRepresentation := range localhostRepresentations {
		address = strings.Replace(address, localhostRepresentation, fmt.Sprintf("http://%s", hostname), -1)
	}
	return address
}

func (t target) BaseLabels() clientmodel.LabelSet {
	return t.baseLabels
}

// Merge a new externally supplied target definition (e.g. with changed base
// labels) into an old target definition for the same endpoint. Preserve
// remaining information - like health state - from the old target.
func (t *target) Merge(newTarget Target) {
	if t.Address() != newTarget.Address() {
		panic("targets don't refer to the same endpoint")
	}
	t.baseLabels = newTarget.BaseLabels()
}

type targets []Target

func (t targets) Len() int {
	return len(t)
}

func (t targets) Less(i, j int) bool {
	return t[i].scheduledFor().Before(t[j].scheduledFor())
}

func (t targets) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<|endoftext|>


<commit_before>package queue

import (
	"context"
	"github.com/ipfs-search/ipfs-search/instr"
	"github.com/ipfs-search/ipfs-search/worker"
	"github.com/streadway/amqp"
	"go.opentelemetry.io/otel/api/trace"
	"go.opentelemetry.io/otel/codes"
	"go.opentelemetry.io/otel/label"
)

// MessageWorkerFactory instantiates a worker for a single AMQP message
type MessageWorkerFactory func(msg *amqp.Delivery) worker.Worker

// newMessageWorker implements MessageWorkerFactory and wraps a factory with
// a messageWorker, such that messages will be properly acked/rejected and
// errors/panics handled
func newMessageWorker(factory MessageWorkerFactory, i *instr.Instrumentation) MessageWorkerFactory {
	return func(msg *amqp.Delivery) worker.Worker {
		return &messageWorker{
			Factory:         factory,
			Delivery:        msg,
			Instrumentation: i,
		}
	}
}

// messageWorker instantiates and wraps a single worker for every message for
// error handling and ack/rejection
type messageWorker struct {
	Factory MessageWorkerFactory
	*amqp.Delivery
	*instr.Instrumentation
}

// Work initiates the contained worker for a single message, acking if no error and rejecting otherwise
func (m *messageWorker) Work(ctx context.Context) (err error) {
	ctx, span := m.Tracer.Start(ctx, "queue.messageWorker.Work",
		trace.WithAttributes(
			label.String("message", string(m.Body)),
			label.Uint("priority", uint(m.Priority)),
		),
	)
	defer span.End()

	// Create new worker for the actual work and perform it
	worker := m.Factory(m.Delivery)
	err = worker.Work(ctx)

	if err != nil {
		span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))

		// Don't retry
		if err := m.Reject(false); err != nil {
			span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))
		}

		return
	}

	span.RecordError(ctx, err, trace.WithErrorStatus(codes.Ok))

	// Everything went fine, ack the message
	if err := m.Ack(false); err != nil {
		span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))
	}

	return
}
<commit_msg>messageWorker has its own parent<commit_after>package queue

import (
	"context"
	"github.com/ipfs-search/ipfs-search/instr"
	"github.com/ipfs-search/ipfs-search/worker"
	"github.com/streadway/amqp"
	"go.opentelemetry.io/otel/api/trace"
	"go.opentelemetry.io/otel/codes"
	"go.opentelemetry.io/otel/label"
)

// MessageWorkerFactory instantiates a worker for a single AMQP message
type MessageWorkerFactory func(msg *amqp.Delivery) worker.Worker

// newMessageWorker implements MessageWorkerFactory and wraps a factory with
// a messageWorker, such that messages will be properly acked/rejected and
// errors/panics handled
func newMessageWorker(factory MessageWorkerFactory, i *instr.Instrumentation) MessageWorkerFactory {
	return func(msg *amqp.Delivery) worker.Worker {
		return &messageWorker{
			Factory:         factory,
			Delivery:        msg,
			Instrumentation: i,
		}
	}
}

// messageWorker instantiates and wraps a single worker for every message for
// error handling and ack/rejection
type messageWorker struct {
	Factory MessageWorkerFactory
	*amqp.Delivery
	*instr.Instrumentation
}

// Work initiates the contained worker for a single message, acking if no error and rejecting otherwise
func (m *messageWorker) Work(ctx context.Context) (err error) {
	ctx, span := m.Tracer.Start(ctx, "queue.messageWorker.Work",
		trace.WithAttributes(
			label.String("message", string(m.Body)),
			label.Uint("priority", uint(m.Priority)),
		),
		trace.WithNewRoot(),
	)
	defer span.End()

	// Create new worker for the actual work and perform it
	worker := m.Factory(m.Delivery)
	err = worker.Work(ctx)

	if err != nil {
		span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))

		// Don't retry
		if err := m.Reject(false); err != nil {
			span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))
		}

		return
	}

	span.RecordError(ctx, err, trace.WithErrorStatus(codes.Ok))

	// Everything went fine, ack the message
	if err := m.Ack(false); err != nil {
		span.RecordError(ctx, err, trace.WithErrorStatus(codes.Error))
	}

	return
}
<|endoftext|>


<commit_before>package hbuild

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"net/url"
)

type herokuClient struct {
	httpClient *http.Client
	token      string
	url        *url.URL
	version    string
	userAgent  string
}

type herokuRequest struct {
	method            string
	path              string
	body              interface{}
	additionalHeaders http.Header
}

func newHerokuClient(token string) herokuClient {
	herokuUrl, _ := url.Parse("https://api.heroku.com")
	return herokuClient{
		httpClient: http.DefaultClient,
		token:      token,
		url:        herokuUrl,
		version:    "application/vnd.heroku+json; version=edge",
		userAgent:  "hbuild/1",
	}
}

func (hc herokuClient) request(hrequest herokuRequest, v interface{}) (err error) {
	var requestBody io.Reader

	url := hc.url.String() + hrequest.path

	if hrequest.body != nil {
		requestJson, err := json.Marshal(hrequest.body)
		if err != nil {
			return err
		}
		requestBody = bytes.NewReader(requestJson)
	}

	request, err := http.NewRequest(hrequest.method, url, requestBody)
	if err != nil {
		return
	}

	request.SetBasicAuth("", hc.token)
	request.Header.Set("Accept", hc.version)
	request.Header.Set("User-Agent", hc.userAgent)
	if hrequest.body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	for k, v := range hrequest.additionalHeaders {
		request.Header[k] = v
	}

	response, err := hc.httpClient.Do(request)
	if err != nil {
		return
	}

	if response.StatusCode/100 != 2 {
		var herr HerokuJsonError
		err = json.NewDecoder(response.Body).Decode(&herr)
		if err != nil {
			return err
		}
		return HerokuError{errors.New(herr.Message), herr.Id, herr.URL}
	}

	err = json.NewDecoder(response.Body).Decode(&v)
	return
}

type HerokuJsonError struct {
	Message string
	Id      string
	URL     string `json:"url"`
}

type HerokuError struct {
	error
	Id  string
	URL string
}
<commit_msg>Add unverified tls transport.<commit_after>package hbuild

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

// unverifiedSSLTransport is a transport that mirrors http.DefaultTransport
// and skips SSL verification.
var unverifiedSSLTransport http.RoundTripper = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
	TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
}

type herokuClient struct {
	httpClient *http.Client
	token      string
	url        *url.URL
	version    string
	userAgent  string
}

type herokuRequest struct {
	method            string
	path              string
	body              interface{}
	additionalHeaders http.Header
}

func newHerokuClient(token string) herokuClient {
	herokuUrl, _ := url.Parse("https://api.heroku.com")
	return herokuClient{
		httpClient: http.DefaultClient,
		token:      token,
		url:        herokuUrl,
		version:    "application/vnd.heroku+json; version=edge",
		userAgent:  "hbuild/1",
	}
}

func (hc herokuClient) request(hrequest herokuRequest, v interface{}) (err error) {
	var requestBody io.Reader

	url := hc.url.String() + hrequest.path

	if hrequest.body != nil {
		requestJson, err := json.Marshal(hrequest.body)
		if err != nil {
			return err
		}
		requestBody = bytes.NewReader(requestJson)
	}

	request, err := http.NewRequest(hrequest.method, url, requestBody)
	if err != nil {
		return
	}

	request.SetBasicAuth("", hc.token)
	request.Header.Set("Accept", hc.version)
	request.Header.Set("User-Agent", hc.userAgent)
	if hrequest.body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	for k, v := range hrequest.additionalHeaders {
		request.Header[k] = v
	}

	response, err := hc.httpClient.Do(request)
	if err != nil {
		return
	}

	if response.StatusCode/100 != 2 {
		var herr HerokuJsonError
		err = json.NewDecoder(response.Body).Decode(&herr)
		if err != nil {
			return err
		}
		return HerokuError{errors.New(herr.Message), herr.Id, herr.URL}
	}

	err = json.NewDecoder(response.Body).Decode(&v)
	return
}

type HerokuJsonError struct {
	Message string
	Id      string
	URL     string `json:"url"`
}

type HerokuError struct {
	error
	Id  string
	URL string
}
<|endoftext|>


<commit_before>package gcplogs

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/docker/docker/daemon/logger"

	"cloud.google.com/go/compute/metadata"
	"cloud.google.com/go/logging"
	"github.com/Sirupsen/logrus"
	"golang.org/x/net/context"
	mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
)

const (
	name = "gcplogs"

	projectOptKey  = "gcp-project"
	logLabelsKey   = "labels"
	logEnvKey      = "env"
	logEnvRegexKey = "env-regex"
	logCmdKey      = "gcp-log-cmd"
	logZoneKey     = "gcp-meta-zone"
	logNameKey     = "gcp-meta-name"
	logIDKey       = "gcp-meta-id"
)

var (
	// The number of logs the gcplogs driver has dropped.
	droppedLogs uint64

	onGCE bool

	// instance metadata populated from the metadata server if available
	projectID    string
	zone         string
	instanceName string
	instanceID   string
)

func init() {

	if err := logger.RegisterLogDriver(name, New); err != nil {
		logrus.Fatal(err)
	}

	if err := logger.RegisterLogOptValidator(name, ValidateLogOpts); err != nil {
		logrus.Fatal(err)
	}
}

type gcplogs struct {
	logger    *logging.Logger
	instance  *instanceInfo
	container *containerInfo
}

type dockerLogEntry struct {
	Instance  *instanceInfo  `json:"instance,omitempty"`
	Container *containerInfo `json:"container,omitempty"`
	Data      string         `json:"data,omitempty"`
}

type instanceInfo struct {
	Zone string `json:"zone,omitempty"`
	Name string `json:"name,omitempty"`
	ID   string `json:"id,omitempty"`
}

type containerInfo struct {
	Name      string            `json:"name,omitempty"`
	ID        string            `json:"id,omitempty"`
	ImageName string            `json:"imageName,omitempty"`
	ImageID   string            `json:"imageId,omitempty"`
	Created   time.Time         `json:"created,omitempty"`
	Command   string            `json:"command,omitempty"`
	Metadata  map[string]string `json:"metadata,omitempty"`
}

var initGCPOnce sync.Once

func initGCP() {
	initGCPOnce.Do(func() {
		onGCE = metadata.OnGCE()
		if onGCE {
			// These will fail on instances if the metadata service is
			// down or the client is compiled with an API version that
			// has been removed. Since these are not vital, let's ignore
			// them and make their fields in the dockerLogEntry ,omitempty
			projectID, _ = metadata.ProjectID()
			zone, _ = metadata.Zone()
			instanceName, _ = metadata.InstanceName()
			instanceID, _ = metadata.InstanceID()
		}
	})
}

// New creates a new logger that logs to Google Cloud Logging using the application
// default credentials.
//
// See https://developers.google.com/identity/protocols/application-default-credentials
func New(info logger.Info) (logger.Logger, error) {
	initGCP()

	var project string
	if projectID != "" {
		project = projectID
	}
	if projectID, found := info.Config[projectOptKey]; found {
		project = projectID
	}
	if project == "" {
		return nil, fmt.Errorf("No project was specified and couldn't read project from the metadata server. Please specify a project")
	}

	// Issue #29344: gcplogs segfaults (static binary)
	// If HOME is not set, logging.NewClient() will call os/user.Current() via oauth2/google.
	// However, in static binary, os/user.Current() leads to segfault due to a glibc issue that won't be fixed
	// in a short term. (golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341)
	// So we forcibly set HOME so as to avoid call to os/user/Current()
	if err := ensureHomeIfIAmStatic(); err != nil {
		return nil, err
	}

	c, err := logging.NewClient(context.Background(), project)
	if err != nil {
		return nil, err
	}
	var instanceResource *instanceInfo
	if onGCE {
		instanceResource = &instanceInfo{
			Zone: zone,
			Name: instanceName,
			ID:   instanceID,
		}
	} else if info.Config[logZoneKey] != "" || info.Config[logNameKey] != "" || info.Config[logIDKey] != "" {
		instanceResource = &instanceInfo{
			Zone: info.Config[logZoneKey],
			Name: info.Config[logNameKey],
			ID:   info.Config[logIDKey],
		}
	}

	options := []logging.LoggerOption{}
	if instanceResource != nil {
		vmMrpb := logging.CommonResource(
			&mrpb.MonitoredResource{
				Type: "gce_instance",
				Labels: map[string]string{
					"instance_id": instanceResource.ID,
					"zone":        instanceResource.Zone,
				},
			},
		)
		options = []logging.LoggerOption{vmMrpb}
	}
	lg := c.Logger("gcplogs-docker-driver", options...)

	if err := c.Ping(context.Background()); err != nil {
		return nil, fmt.Errorf("unable to connect or authenticate with Google Cloud Logging: %v", err)
	}

	extraAttributes, err := info.ExtraAttributes(nil)
	if err != nil {
		return nil, err
	}

	l := &gcplogs{
		logger: lg,
		container: &containerInfo{
			Name:      info.ContainerName,
			ID:        info.ContainerID,
			ImageName: info.ContainerImageName,
			ImageID:   info.ContainerImageID,
			Created:   info.ContainerCreated,
			Metadata:  extraAttributes,
		},
	}

	if info.Config[logCmdKey] == "true" {
		l.container.Command = info.Command()
	}

	if instanceResource != nil {
		l.instance = instanceResource
	}

	// The logger "overflows" at a rate of 10,000 logs per second and this
	// overflow func is called. We want to surface the error to the user
	// without overly spamming /var/log/docker.log so we log the first time
	// we overflow and every 1000th time after.
	c.OnError = func(err error) {
		if err == logging.ErrOverflow {
			if i := atomic.AddUint64(&droppedLogs, 1); i%1000 == 1 {
				logrus.Errorf("gcplogs driver has dropped %v logs", i)
			}
		} else {
			logrus.Error(err)
		}
	}

	return l, nil
}

// ValidateLogOpts validates the opts passed to the gcplogs driver. Currently, the gcplogs
// driver doesn't take any arguments.
func ValidateLogOpts(cfg map[string]string) error {
	for k := range cfg {
		switch k {
		case projectOptKey, logLabelsKey, logEnvKey, logEnvRegexKey, logCmdKey, logZoneKey, logNameKey, logIDKey:
		default:
			return fmt.Errorf("%q is not a valid option for the gcplogs driver", k)
		}
	}
	return nil
}

func (l *gcplogs) Log(m *logger.Message) error {
	data := string(m.Line)
	ts := m.Timestamp
	logger.PutMessage(m)

	l.logger.Log(logging.Entry{
		Timestamp: ts,
		Payload: &dockerLogEntry{
			Instance:  l.instance,
			Container: l.container,
			Data:      data,
		},
	})
	return nil
}

func (l *gcplogs) Close() error {
	l.logger.Flush()
	return nil
}

func (l *gcplogs) Name() string {
	return name
}
<commit_msg>GCP Logs: send log line as jsonPayload.message instead of jsonPayload.data<commit_after>package gcplogs

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/docker/docker/daemon/logger"

	"cloud.google.com/go/compute/metadata"
	"cloud.google.com/go/logging"
	"github.com/Sirupsen/logrus"
	"golang.org/x/net/context"
	mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
)

const (
	name = "gcplogs"

	projectOptKey  = "gcp-project"
	logLabelsKey   = "labels"
	logEnvKey      = "env"
	logEnvRegexKey = "env-regex"
	logCmdKey      = "gcp-log-cmd"
	logZoneKey     = "gcp-meta-zone"
	logNameKey     = "gcp-meta-name"
	logIDKey       = "gcp-meta-id"
)

var (
	// The number of logs the gcplogs driver has dropped.
	droppedLogs uint64

	onGCE bool

	// instance metadata populated from the metadata server if available
	projectID    string
	zone         string
	instanceName string
	instanceID   string
)

func init() {

	if err := logger.RegisterLogDriver(name, New); err != nil {
		logrus.Fatal(err)
	}

	if err := logger.RegisterLogOptValidator(name, ValidateLogOpts); err != nil {
		logrus.Fatal(err)
	}
}

type gcplogs struct {
	logger    *logging.Logger
	instance  *instanceInfo
	container *containerInfo
}

type dockerLogEntry struct {
	Instance  *instanceInfo  `json:"instance,omitempty"`
	Container *containerInfo `json:"container,omitempty"`
	Message   string         `json:"message,omitempty"`
}

type instanceInfo struct {
	Zone string `json:"zone,omitempty"`
	Name string `json:"name,omitempty"`
	ID   string `json:"id,omitempty"`
}

type containerInfo struct {
	Name      string            `json:"name,omitempty"`
	ID        string            `json:"id,omitempty"`
	ImageName string            `json:"imageName,omitempty"`
	ImageID   string            `json:"imageId,omitempty"`
	Created   time.Time         `json:"created,omitempty"`
	Command   string            `json:"command,omitempty"`
	Metadata  map[string]string `json:"metadata,omitempty"`
}

var initGCPOnce sync.Once

func initGCP() {
	initGCPOnce.Do(func() {
		onGCE = metadata.OnGCE()
		if onGCE {
			// These will fail on instances if the metadata service is
			// down or the client is compiled with an API version that
			// has been removed. Since these are not vital, let's ignore
			// them and make their fields in the dockerLogEntry ,omitempty
			projectID, _ = metadata.ProjectID()
			zone, _ = metadata.Zone()
			instanceName, _ = metadata.InstanceName()
			instanceID, _ = metadata.InstanceID()
		}
	})
}

// New creates a new logger that logs to Google Cloud Logging using the application
// default credentials.
//
// See https://developers.google.com/identity/protocols/application-default-credentials
func New(info logger.Info) (logger.Logger, error) {
	initGCP()

	var project string
	if projectID != "" {
		project = projectID
	}
	if projectID, found := info.Config[projectOptKey]; found {
		project = projectID
	}
	if project == "" {
		return nil, fmt.Errorf("No project was specified and couldn't read project from the metadata server. Please specify a project")
	}

	// Issue #29344: gcplogs segfaults (static binary)
	// If HOME is not set, logging.NewClient() will call os/user.Current() via oauth2/google.
	// However, in static binary, os/user.Current() leads to segfault due to a glibc issue that won't be fixed
	// in a short term. (golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341)
	// So we forcibly set HOME so as to avoid call to os/user/Current()
	if err := ensureHomeIfIAmStatic(); err != nil {
		return nil, err
	}

	c, err := logging.NewClient(context.Background(), project)
	if err != nil {
		return nil, err
	}
	var instanceResource *instanceInfo
	if onGCE {
		instanceResource = &instanceInfo{
			Zone: zone,
			Name: instanceName,
			ID:   instanceID,
		}
	} else if info.Config[logZoneKey] != "" || info.Config[logNameKey] != "" || info.Config[logIDKey] != "" {
		instanceResource = &instanceInfo{
			Zone: info.Config[logZoneKey],
			Name: info.Config[logNameKey],
			ID:   info.Config[logIDKey],
		}
	}

	options := []logging.LoggerOption{}
	if instanceResource != nil {
		vmMrpb := logging.CommonResource(
			&mrpb.MonitoredResource{
				Type: "gce_instance",
				Labels: map[string]string{
					"instance_id": instanceResource.ID,
					"zone":        instanceResource.Zone,
				},
			},
		)
		options = []logging.LoggerOption{vmMrpb}
	}
	lg := c.Logger("gcplogs-docker-driver", options...)

	if err := c.Ping(context.Background()); err != nil {
		return nil, fmt.Errorf("unable to connect or authenticate with Google Cloud Logging: %v", err)
	}

	extraAttributes, err := info.ExtraAttributes(nil)
	if err != nil {
		return nil, err
	}

	l := &gcplogs{
		logger: lg,
		container: &containerInfo{
			Name:      info.ContainerName,
			ID:        info.ContainerID,
			ImageName: info.ContainerImageName,
			ImageID:   info.ContainerImageID,
			Created:   info.ContainerCreated,
			Metadata:  extraAttributes,
		},
	}

	if info.Config[logCmdKey] == "true" {
		l.container.Command = info.Command()
	}

	if instanceResource != nil {
		l.instance = instanceResource
	}

	// The logger "overflows" at a rate of 10,000 logs per second and this
	// overflow func is called. We want to surface the error to the user
	// without overly spamming /var/log/docker.log so we log the first time
	// we overflow and every 1000th time after.
	c.OnError = func(err error) {
		if err == logging.ErrOverflow {
			if i := atomic.AddUint64(&droppedLogs, 1); i%1000 == 1 {
				logrus.Errorf("gcplogs driver has dropped %v logs", i)
			}
		} else {
			logrus.Error(err)
		}
	}

	return l, nil
}

// ValidateLogOpts validates the opts passed to the gcplogs driver. Currently, the gcplogs
// driver doesn't take any arguments.
func ValidateLogOpts(cfg map[string]string) error {
	for k := range cfg {
		switch k {
		case projectOptKey, logLabelsKey, logEnvKey, logEnvRegexKey, logCmdKey, logZoneKey, logNameKey, logIDKey:
		default:
			return fmt.Errorf("%q is not a valid option for the gcplogs driver", k)
		}
	}
	return nil
}

func (l *gcplogs) Log(m *logger.Message) error {
	message := string(m.Line)
	ts := m.Timestamp
	logger.PutMessage(m)

	l.logger.Log(logging.Entry{
		Timestamp: ts,
		Payload: &dockerLogEntry{
			Instance:  l.instance,
			Container: l.container,
			Message:   message,
		},
	})
	return nil
}

func (l *gcplogs) Close() error {
	l.logger.Flush()
	return nil
}

func (l *gcplogs) Name() string {
	return name
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All rights reserved.
//
// 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 s2

import (
	"encoding/binary"
	"io"
)

const (
	// encodingVersion is the current version of the encoding
	// format that is compatible with C++ and other S2 libraries.
	encodingVersion = int8(1)

	// encodingCompressedVersion is the current version of the
	// compressed format.
	encodingCompressedVersion = int8(4)
)

// encoder handles the specifics of encoding for S2 types.
type encoder struct {
	w   io.Writer // the real writer passed to Encode
	err error
}

func (e *encoder) writeUvarint(x uint64) {
	if e.err != nil {
		return
	}
	var buf [binary.MaxVarintLen64]byte
	n := binary.PutUvarint(buf[:], x)
	_, e.err = e.w.Write(buf[:n])
}

func (e *encoder) writeBool(x bool) {
	if e.err != nil {
		return
	}
	var val int8
	if x {
		val = 1
	}
	e.err = binary.Write(e.w, binary.LittleEndian, val)
}

func (e *encoder) writeInt8(x int8) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt16(x int16) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt32(x int32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt64(x int64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeUint8(x uint8) {
	if e.err != nil {
		return
	}
	_, e.err = e.w.Write([]byte{x})
}

func (e *encoder) writeUint32(x uint32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeUint64(x uint64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeFloat32(x float32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeFloat64(x float64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

type byteReader interface {
	io.Reader
	io.ByteReader
}

// byteReaderAdapter embellishes an io.Reader with a ReadByte method,
// so that it implements the io.ByteReader interface.
type byteReaderAdapter struct {
	io.Reader
}

func (b byteReaderAdapter) ReadByte() (byte, error) {
	buf := []byte{0}
	_, err := io.ReadFull(b, buf)
	return buf[0], err
}

func asByteReader(r io.Reader) byteReader {
	if br, ok := r.(byteReader); ok {
		return br
	}
	return byteReaderAdapter{r}
}

type decoder struct {
	r   byteReader // the real reader passed to Decode
	err error
}

func (d *decoder) readBool() (x bool) {
	if d.err != nil {
		return
	}
	var val int8
	d.err = binary.Read(d.r, binary.LittleEndian, &val)
	return val == 1
}

func (d *decoder) readInt8() (x int8) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readInt16() (x int16) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readInt32() (x int32) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readInt64() (x int64) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readUint8() (x uint8) {
	if d.err != nil {
		return
	}
	x, d.err = d.r.ReadByte()
	return
}

func (d *decoder) readUint32() (x uint32) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readUint64() (x uint64) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readFloat32() (x float32) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readFloat64() (x float64) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readUvarint() (x uint64) {
	if d.err != nil {
		return
	}
	x, d.err = binary.ReadUvarint(d.r)
	return
}
<commit_msg>s2: Optimise readFloat64 to speed up Rect decoding.<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// 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 s2

import (
	"encoding/binary"
	"io"
	"math"
)

const (
	// encodingVersion is the current version of the encoding
	// format that is compatible with C++ and other S2 libraries.
	encodingVersion = int8(1)

	// encodingCompressedVersion is the current version of the
	// compressed format.
	encodingCompressedVersion = int8(4)
)

// encoder handles the specifics of encoding for S2 types.
type encoder struct {
	w   io.Writer // the real writer passed to Encode
	err error
}

func (e *encoder) writeUvarint(x uint64) {
	if e.err != nil {
		return
	}
	var buf [binary.MaxVarintLen64]byte
	n := binary.PutUvarint(buf[:], x)
	_, e.err = e.w.Write(buf[:n])
}

func (e *encoder) writeBool(x bool) {
	if e.err != nil {
		return
	}
	var val int8
	if x {
		val = 1
	}
	e.err = binary.Write(e.w, binary.LittleEndian, val)
}

func (e *encoder) writeInt8(x int8) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt16(x int16) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt32(x int32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeInt64(x int64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeUint8(x uint8) {
	if e.err != nil {
		return
	}
	_, e.err = e.w.Write([]byte{x})
}

func (e *encoder) writeUint32(x uint32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeUint64(x uint64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeFloat32(x float32) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

func (e *encoder) writeFloat64(x float64) {
	if e.err != nil {
		return
	}
	e.err = binary.Write(e.w, binary.LittleEndian, x)
}

type byteReader interface {
	io.Reader
	io.ByteReader
}

// byteReaderAdapter embellishes an io.Reader with a ReadByte method,
// so that it implements the io.ByteReader interface.
type byteReaderAdapter struct {
	io.Reader
}

func (b byteReaderAdapter) ReadByte() (byte, error) {
	buf := []byte{0}
	_, err := io.ReadFull(b, buf)
	return buf[0], err
}

func asByteReader(r io.Reader) byteReader {
	if br, ok := r.(byteReader); ok {
		return br
	}
	return byteReaderAdapter{r}
}

type decoder struct {
	r   byteReader // the real reader passed to Decode
	err error
	buf []byte
}

// Get a buffer of size 8, to avoid allocating over and over.
func (d *decoder) buffer() []byte {
	if d.buf == nil {
		d.buf = make([]byte, 8)
	}
	return d.buf
}

func (d *decoder) readBool() (x bool) {
	if d.err != nil {
		return
	}
	var val int8
	d.err = binary.Read(d.r, binary.LittleEndian, &val)
	return val == 1
}

func (d *decoder) readInt8() (x int8) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readInt64() (x int64) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readUint8() (x uint8) {
	if d.err != nil {
		return
	}
	x, d.err = d.r.ReadByte()
	return
}

func (d *decoder) readUint32() (x uint32) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readUint64() (x uint64) {
	if d.err != nil {
		return
	}
	d.err = binary.Read(d.r, binary.LittleEndian, &x)
	return
}

func (d *decoder) readFloat64() float64 {
	if d.err != nil {
		return 0
	}
	buf := d.buffer()
	_, d.err = io.ReadFull(d.r, buf)
	return math.Float64frombits(binary.LittleEndian.Uint64(buf))
}

func (d *decoder) readUvarint() (x uint64) {
	if d.err != nil {
		return
	}
	x, d.err = binary.ReadUvarint(d.r)
	return
}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gredis_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/database/gredis"
	"github.com/gogf/gf/test/gtest"
	redis2 "github.com/gomodule/redigo/redis"
)

var (
	config = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
	}
)

func Test_NewClose(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		gtest.AssertNE(redis, nil)
		err := redis.Close()
		gtest.Assert(err, nil)
	})
}

func Test_Do(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		_, err := redis.Do("SET", "k", "v")
		gtest.Assert(err, nil)

		r, err := redis.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, []byte("v"))

		_, err = redis.Do("DEL", "k")
		gtest.Assert(err, nil)
		r, err = redis.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Stats(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		redis.SetMaxIdle(2)
		redis.SetMaxActive(100)
		redis.SetIdleTimeout(500 * time.Millisecond)
		redis.SetMaxConnLifetime(500 * time.Millisecond)

		array := make([]*gredis.Conn, 0)
		for i := 0; i < 10; i++ {
			array = append(array, redis.Conn())
		}
		stats := redis.Stats()
		gtest.Assert(stats.ActiveCount, 10)
		gtest.Assert(stats.IdleCount, 0)
		for i := 0; i < 10; i++ {
			array[i].Close()
		}
		stats = redis.Stats()
		gtest.Assert(stats.ActiveCount, 2)
		gtest.Assert(stats.IdleCount, 2)
		//time.Sleep(3000*time.Millisecond)
		//stats  = redis.Stats()
		//fmt.Println(stats)
		//gtest.Assert(stats.ActiveCount,  0)
		//gtest.Assert(stats.IdleCount,    0)
	})
}

func Test_Conn(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		conn := redis.Conn()
		defer conn.Close()

		r, err := conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, []byte("v"))

		_, err = conn.Do("DEL", "k")
		gtest.Assert(err, nil)
		r, err = conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Instance(t *testing.T) {
	gtest.Case(t, func() {
		group := "my-test"
		gredis.SetConfig(config, group)
		defer gredis.RemoveConfig(group)
		redis := gredis.Instance(group)
		defer redis.Close()

		conn := redis.Conn()
		defer conn.Close()

		_, err := conn.Do("SET", "k", "v")
		gtest.Assert(err, nil)

		r, err := conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, []byte("v"))

		_, err = conn.Do("DEL", "k")
		gtest.Assert(err, nil)
		r, err = conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Basic(t *testing.T) {
	gtest.Case(t, func() {
		config1 := gredis.Config{
			Host: "127.0.0.2",
			Port: 6379,
			Db:   1,
		}
		redis := gredis.New(config1)
		_, err := redis.Do("info")
		gtest.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   1,
			Pass: "666666",
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		gtest.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   100,
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		gtest.AssertNE(err, nil)

		redis = gredis.Instance("gf")
		gtest.Assert(redis == nil, true)
		gredis.ClearConfig()

		redis = gredis.New(config)
		defer redis.Close()
		_, err = redis.DoVar("SET", "k", "v")
		gtest.Assert(err, nil)

		v, err := redis.DoVar("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), "v")

		conn := redis.GetConn()
		_, err = conn.DoVar("SET", "k", "v")
		gtest.Assert(err, nil)

		//v, err = conn.ReceiveVar()
		//gtest.Assert(err, nil)
		//gtest.Assert(v.String(), "v")

		psc := redis2.PubSubConn{Conn: conn}
		psc.Subscribe("gf")
		redis.DoVar("PUBLISH", "gf", "gf test")
		go func() {
			for {
				v, _ := conn.ReceiveVar()
				switch obj := v.Val().(type) {
				case redis2.Message:
					gtest.Assert(string(obj.Data), "gf test")
				case redis2.Subscription:

				}
			}
		}()

		time.Sleep(time.Second)
	})
}

func Test_Bool(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		_, err := redis.Do("SET", "key-true", true)
		gtest.Assert(err, nil)

		_, err = redis.Do("SET", "key-false", false)
		gtest.Assert(err, nil)

		r, err := redis.DoVar("GET", "key-true")
		gtest.Assert(err, nil)
		gtest.Assert(r.Bool(), true)

		r, err = redis.DoVar("GET", "key-false")
		gtest.Assert(err, nil)
		gtest.Assert(r.Bool(), false)
	})
}
<commit_msg>fix issue in gredis unit test cases<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gredis_test

import (
	"testing"
	"time"

	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/database/gredis"
	"github.com/gogf/gf/test/gtest"
	redis2 "github.com/gomodule/redigo/redis"
)

var (
	config = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
	}
)

func Test_NewClose(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		gtest.AssertNE(redis, nil)
		err := redis.Close()
		gtest.Assert(err, nil)
	})
}

func Test_Do(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		_, err := redis.Do("SET", "k", "v")
		gtest.Assert(err, nil)

		r, err := redis.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, []byte("v"))

		_, err = redis.Do("DEL", "k")
		gtest.Assert(err, nil)
		r, err = redis.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Stats(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		redis.SetMaxIdle(2)
		redis.SetMaxActive(100)
		redis.SetIdleTimeout(500 * time.Millisecond)
		redis.SetMaxConnLifetime(500 * time.Millisecond)

		array := make([]*gredis.Conn, 0)
		for i := 0; i < 10; i++ {
			array = append(array, redis.Conn())
		}
		stats := redis.Stats()
		gtest.Assert(stats.ActiveCount, 10)
		gtest.Assert(stats.IdleCount, 0)
		for i := 0; i < 10; i++ {
			array[i].Close()
		}
		stats = redis.Stats()
		gtest.Assert(stats.ActiveCount, 2)
		gtest.Assert(stats.IdleCount, 2)
		//time.Sleep(3000*time.Millisecond)
		//stats  = redis.Stats()
		//fmt.Println(stats)
		//gtest.Assert(stats.ActiveCount,  0)
		//gtest.Assert(stats.IdleCount,    0)
	})
}

func Test_Conn(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		defer redis.Close()
		conn := redis.Conn()
		defer conn.Close()

		key := gconv.String(gtime.Nanosecond())
		value := []byte("v")
		r, err := conn.Do("SET", key, value)
		gtest.Assert(err, nil)

		r, err = conn.Do("GET", key)
		gtest.Assert(err, nil)
		gtest.Assert(r, value)

		_, err = conn.Do("DEL", key)
		gtest.Assert(err, nil)
		r, err = conn.Do("GET", key)
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Instance(t *testing.T) {
	gtest.Case(t, func() {
		group := "my-test"
		gredis.SetConfig(config, group)
		defer gredis.RemoveConfig(group)
		redis := gredis.Instance(group)
		defer redis.Close()

		conn := redis.Conn()
		defer conn.Close()

		_, err := conn.Do("SET", "k", "v")
		gtest.Assert(err, nil)

		r, err := conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, []byte("v"))

		_, err = conn.Do("DEL", "k")
		gtest.Assert(err, nil)
		r, err = conn.Do("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(r, nil)
	})
}

func Test_Basic(t *testing.T) {
	gtest.Case(t, func() {
		config1 := gredis.Config{
			Host: "127.0.0.2",
			Port: 6379,
			Db:   1,
		}
		redis := gredis.New(config1)
		_, err := redis.Do("info")
		gtest.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   1,
			Pass: "666666",
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		gtest.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   100,
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		gtest.AssertNE(err, nil)

		redis = gredis.Instance("gf")
		gtest.Assert(redis == nil, true)
		gredis.ClearConfig()

		redis = gredis.New(config)
		defer redis.Close()
		_, err = redis.DoVar("SET", "k", "v")
		gtest.Assert(err, nil)

		v, err := redis.DoVar("GET", "k")
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), "v")

		conn := redis.GetConn()
		_, err = conn.DoVar("SET", "k", "v")
		gtest.Assert(err, nil)

		//v, err = conn.ReceiveVar()
		//gtest.Assert(err, nil)
		//gtest.Assert(v.String(), "v")

		psc := redis2.PubSubConn{Conn: conn}
		psc.Subscribe("gf")
		redis.DoVar("PUBLISH", "gf", "gf test")
		go func() {
			for {
				v, _ := conn.ReceiveVar()
				switch obj := v.Val().(type) {
				case redis2.Message:
					gtest.Assert(string(obj.Data), "gf test")
				case redis2.Subscription:

				}
			}
		}()

		time.Sleep(time.Second)
	})
}

func Test_Bool(t *testing.T) {
	gtest.Case(t, func() {
		redis := gredis.New(config)
		_, err := redis.Do("SET", "key-true", true)
		gtest.Assert(err, nil)

		_, err = redis.Do("SET", "key-false", false)
		gtest.Assert(err, nil)

		r, err := redis.DoVar("GET", "key-true")
		gtest.Assert(err, nil)
		gtest.Assert(r.Bool(), true)

		r, err = redis.DoVar("GET", "key-false")
		gtest.Assert(err, nil)
		gtest.Assert(r.Bool(), false)
	})
}
<|endoftext|>


<commit_before>package accounts

import (
	"net/url"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/globals"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/logger"
)

// Account holds configuration information for an account
type Account struct {
	DocID       string                 `json:"_id,omitempty"`
	DocRev      string                 `json:"_rev,omitempty"`
	Name        string                 `json:"name"`
	AccountType string                 `json:"account_type"`
	Basic       *BasicInfo             `json:"auth,omitempty"`
	Oauth       *OauthInfo             `json:"oauth,omitempty"`
	Extras      map[string]interface{} `json:"oauth_callback_results,omitempty"`
}

// OauthInfo holds configuration information for an oauth account
type OauthInfo struct {
	AccessToken  string      `json:"access_token,omitempty"`
	TokenType    string      `json:"token_type,omitempty"`
	ExpiresAt    time.Time   `json:"expires_at,omitempty"`
	RefreshToken string      `json:"refresh_token,omitempty"`
	ClientID     string      `json:"client_id,omitempty"`
	ClientSecret string      `json:"client_secret,omitempty"`
	Query        *url.Values `json:"query,omitempty"`
}

// BasicInfo holds configuration information for an user/pass account
type BasicInfo struct {
	Login    string `json:"login,omitempty"`
	Password string `json:"password,omitempty"`
}

// ID is used to implement the couchdb.Doc interface
func (ac *Account) ID() string { return ac.DocID }

// Rev is used to implement the couchdb.Doc interface
func (ac *Account) Rev() string { return ac.DocRev }

// SetID is used to implement the couchdb.Doc interface
func (ac *Account) SetID(id string) { ac.DocID = id }

// SetRev is used to implement the couchdb.Doc interface
func (ac *Account) SetRev(rev string) { ac.DocRev = rev }

// DocType implements couchdb.Doc
func (ac *Account) DocType() string { return consts.Accounts }

// Clone implements couchdb.Doc
func (ac *Account) Clone() couchdb.Doc {
	cloned := *ac
	if ac.Oauth != nil {
		tmp := *ac.Oauth
		cloned.Oauth = &tmp
	}
	if ac.Basic != nil {
		tmp := *ac.Basic
		cloned.Basic = &tmp
	}
	cloned.Extras = make(map[string]interface{})
	for k, v := range ac.Extras {
		cloned.Extras[k] = v
	}
	return &cloned
}

// Valid implements permissions.Validable
func (ac *Account) Valid(field, expected string) bool {
	return field == "account_type" && expected == ac.AccountType
}

func init() {
	couchdb.AddHook(consts.Accounts, couchdb.EventDelete,
		func(domain string, doc couchdb.Doc, old couchdb.Doc) error {
			sched := globals.GetScheduler()
			broker := globals.GetBroker()

			trigs, err := sched.GetAll(domain)
			if err != nil {
				logger.WithDomain(domain).Error(
					"Failed to fetch triggers after account deletion: ", err)
				return err
			}
			for _, t := range trigs {
				toDelete := false

				if t.Infos().WorkerType != "konnector" {
					continue
				}

				var msg struct {
					Account string `json:"account"`
				}
				err := t.Infos().Message.Unmarshal(&msg)
				if err != nil {
					toDelete = true
				} else if msg.Account == doc.ID() {
					toDelete = true
				}
				if toDelete {
					if err := sched.Delete(domain, t.ID()); err != nil {
						logger.WithDomain(domain).Errorln("failed to delete orphan trigger", err)
					}
				}
			}

			// When an account is deleted, we need to push a new job in order to
			// delete possible data associated with this account. This is done via
			// this hook.
			//
			// This may require additionnal specifications to allow konnectors to
			// define more explicitly when and how they want to be called in order to
			// cleanup or update their associated content. For now we make this
			// process really specific to the deletion of an account, which is our
			// only detailed usecase.
			if old != nil {
				var konnector string
				switch v := old.(type) {
				case *Account:
					konnector = v.AccountType
				case *couchdb.JSONDoc:
					konnector, _ = v.M["account_type"].(string)
				}
				if konnector == "" {
					return nil
				}

				msg, err := jobs.NewMessage(struct {
					Account        string `json:"account"`
					AccountRev     string `json:"account_rev"`
					Konnector      string `json:"konnector"`
					AccountDeleted bool   `json:"account_deleted"`
				}{
					Account:        old.ID(),
					AccountRev:     old.Rev(),
					Konnector:      konnector,
					AccountDeleted: true,
				})
				if err != nil {
					return err
				}
				if _, err = broker.PushJob(&jobs.JobRequest{
					Domain:     domain,
					WorkerType: "konnector",
					Message:    msg,
				}); err != nil {
					return err
				}
			}

			return nil
		})
}
<commit_msg>Account deletion: add couchdb.JSONDoc value case<commit_after>package accounts

import (
	"net/url"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/globals"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/logger"
)

// Account holds configuration information for an account
type Account struct {
	DocID       string                 `json:"_id,omitempty"`
	DocRev      string                 `json:"_rev,omitempty"`
	Name        string                 `json:"name"`
	AccountType string                 `json:"account_type"`
	Basic       *BasicInfo             `json:"auth,omitempty"`
	Oauth       *OauthInfo             `json:"oauth,omitempty"`
	Extras      map[string]interface{} `json:"oauth_callback_results,omitempty"`
}

// OauthInfo holds configuration information for an oauth account
type OauthInfo struct {
	AccessToken  string      `json:"access_token,omitempty"`
	TokenType    string      `json:"token_type,omitempty"`
	ExpiresAt    time.Time   `json:"expires_at,omitempty"`
	RefreshToken string      `json:"refresh_token,omitempty"`
	ClientID     string      `json:"client_id,omitempty"`
	ClientSecret string      `json:"client_secret,omitempty"`
	Query        *url.Values `json:"query,omitempty"`
}

// BasicInfo holds configuration information for an user/pass account
type BasicInfo struct {
	Login    string `json:"login,omitempty"`
	Password string `json:"password,omitempty"`
}

// ID is used to implement the couchdb.Doc interface
func (ac *Account) ID() string { return ac.DocID }

// Rev is used to implement the couchdb.Doc interface
func (ac *Account) Rev() string { return ac.DocRev }

// SetID is used to implement the couchdb.Doc interface
func (ac *Account) SetID(id string) { ac.DocID = id }

// SetRev is used to implement the couchdb.Doc interface
func (ac *Account) SetRev(rev string) { ac.DocRev = rev }

// DocType implements couchdb.Doc
func (ac *Account) DocType() string { return consts.Accounts }

// Clone implements couchdb.Doc
func (ac *Account) Clone() couchdb.Doc {
	cloned := *ac
	if ac.Oauth != nil {
		tmp := *ac.Oauth
		cloned.Oauth = &tmp
	}
	if ac.Basic != nil {
		tmp := *ac.Basic
		cloned.Basic = &tmp
	}
	cloned.Extras = make(map[string]interface{})
	for k, v := range ac.Extras {
		cloned.Extras[k] = v
	}
	return &cloned
}

// Valid implements permissions.Validable
func (ac *Account) Valid(field, expected string) bool {
	return field == "account_type" && expected == ac.AccountType
}

func init() {
	couchdb.AddHook(consts.Accounts, couchdb.EventDelete,
		func(domain string, doc couchdb.Doc, old couchdb.Doc) error {
			sched := globals.GetScheduler()
			broker := globals.GetBroker()

			trigs, err := sched.GetAll(domain)
			if err != nil {
				logger.WithDomain(domain).Error(
					"Failed to fetch triggers after account deletion: ", err)
				return err
			}
			for _, t := range trigs {
				toDelete := false

				if t.Infos().WorkerType != "konnector" {
					continue
				}

				var msg struct {
					Account string `json:"account"`
				}
				err := t.Infos().Message.Unmarshal(&msg)
				if err != nil {
					toDelete = true
				} else if msg.Account == doc.ID() {
					toDelete = true
				}
				if toDelete {
					if err := sched.Delete(domain, t.ID()); err != nil {
						logger.WithDomain(domain).Errorln("failed to delete orphan trigger", err)
					}
				}
			}

			// When an account is deleted, we need to push a new job in order to
			// delete possible data associated with this account. This is done via
			// this hook.
			//
			// This may require additionnal specifications to allow konnectors to
			// define more explicitly when and how they want to be called in order to
			// cleanup or update their associated content. For now we make this
			// process really specific to the deletion of an account, which is our
			// only detailed usecase.
			if old != nil {
				log := logger.WithDomain(domain).
					WithField("account_id", old.ID()).
					WithField("account_rev", old.Rev())

				var konnector string
				switch v := old.(type) {
				case *Account:
					konnector = v.AccountType
				case *couchdb.JSONDoc:
					konnector, _ = v.M["account_type"].(string)
				case couchdb.JSONDoc:
					konnector, _ = v.M["account_type"].(string)
				}
				if konnector == "" {
					log.Info("No associated konnector for account: " +
						"cannot create on_delete job")
					return nil
				}

				logger.
					WithField("konnector", konnector).
					Info("Pusing job for konnector on_delete")

				msg, err := jobs.NewMessage(struct {
					Account        string `json:"account"`
					AccountRev     string `json:"account_rev"`
					Konnector      string `json:"konnector"`
					AccountDeleted bool   `json:"account_deleted"`
				}{
					Account:        old.ID(),
					AccountRev:     old.Rev(),
					Konnector:      konnector,
					AccountDeleted: true,
				})
				if err != nil {
					return err
				}
				if _, err = broker.PushJob(&jobs.JobRequest{
					Domain:     domain,
					WorkerType: "konnector",
					Message:    msg,
				}); err != nil {
					return err
				}
			}

			return nil
		})
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

	"github.com/pelletier/go-toml"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/require"
)

func TestStoreGet(t *testing.T) {
	treeMap := map[string]interface{}{
		"core": map[string]interface{}{
			"dcos_url":   "https://dcos.example.com",
			"ssl_verify": "false",
			"timeout":    15,
			"reporting":  true,
		},
		"cluster": map[string]interface{}{
			"name": "mr-cluster",
		},
	}

	tree, err := toml.TreeFromMap(treeMap)
	require.NoError(t, err)

	store := NewStore(StoreOpts{
		Tree: tree,
		EnvLookup: func(key string) (string, bool) {
			switch key {
			case "DCOS_URL":
				return "https://dcos-env.example.com", true
			case "DCOS_SSL_VERIFY":
				return "true", true
			case "DCOS_CLUSTER_NAME":
				return "dummy", true
			}
			return "", false
		},
	})

	val := store.Get(keyURL)
	require.Equal(t, "https://dcos-env.example.com", val)

	val = store.Get(keyTLS)
	require.Equal(t, "true", val)

	val = store.Get(keyReporting)
	require.Equal(t, true, val)

	val = store.Get(keyTimeout)
	require.EqualValues(t, 15, val)

	val = store.Get(keyClusterName)
	require.Equal(t, "mr-cluster", val)
}

func TestStoreSetAndUnset(t *testing.T) {
	store := NewStore(StoreOpts{})

	store.Set(keyURL, "https://dcos.example.com")
	store.Set(keyTimeout, "30")
	store.Set(keyReporting, "1")

	require.Equal(t, "https://dcos.example.com", store.Get(keyURL))
	require.Equal(t, true, store.Get(keyReporting))
	require.EqualValues(t, 30, store.Get(keyTimeout))

	store.Unset(keyURL)
	require.Nil(t, store.Get(keyURL))
}

func TestStoreKeys(t *testing.T) {
	treeMap := map[string]interface{}{
		"core": map[string]interface{}{
			"dcos_url": "https://dcos.example.com",
			"timeout":  15,
		},
		"cluster": map[string]interface{}{
			"name": "mr-cluster",
		},
		"marathon": map[string]interface{}{
			"url": "https://marathon.example.com",
		},
	}

	tree, err := toml.TreeFromMap(treeMap)
	require.NoError(t, err)

	store := NewStore(StoreOpts{
		Tree: tree,
		EnvLookup: func(key string) (string, bool) {
			switch key {
			case "DCOS_URL":
				return "https://dcos-env.example.com", true
			case "DCOS_SSL_VERIFY":
				return "true", true
			}
			return "", false
		},
	})

	keys := store.Keys()
	expectedKeys := []string{
		"cluster.name",
		"core.dcos_url",
		"core.ssl_verify",
		"core.timeout",
		"marathon.url",
	}
	require.Equal(t, expectedKeys, keys)
}

func TestPersistWithoutPath(t *testing.T) {
	store := NewStore(StoreOpts{})
	require.Error(t, store.Persist())
}

func TestPersist(t *testing.T) {
	store := NewStore(StoreOpts{})

	f, _ := afero.TempFile(fs, "/", "config")
	store.SetPath(f.Name())

	require.NoError(t, store.Persist())
}
<commit_msg>Compare against the exact error in TestPersistWithoutPath<commit_after>package config

import (
	"testing"

	"github.com/pelletier/go-toml"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/require"
)

func TestStoreGet(t *testing.T) {
	treeMap := map[string]interface{}{
		"core": map[string]interface{}{
			"dcos_url":   "https://dcos.example.com",
			"ssl_verify": "false",
			"timeout":    15,
			"reporting":  true,
		},
		"cluster": map[string]interface{}{
			"name": "mr-cluster",
		},
	}

	tree, err := toml.TreeFromMap(treeMap)
	require.NoError(t, err)

	store := NewStore(StoreOpts{
		Tree: tree,
		EnvLookup: func(key string) (string, bool) {
			switch key {
			case "DCOS_URL":
				return "https://dcos-env.example.com", true
			case "DCOS_SSL_VERIFY":
				return "true", true
			case "DCOS_CLUSTER_NAME":
				return "dummy", true
			}
			return "", false
		},
	})

	val := store.Get(keyURL)
	require.Equal(t, "https://dcos-env.example.com", val)

	val = store.Get(keyTLS)
	require.Equal(t, "true", val)

	val = store.Get(keyReporting)
	require.Equal(t, true, val)

	val = store.Get(keyTimeout)
	require.EqualValues(t, 15, val)

	val = store.Get(keyClusterName)
	require.Equal(t, "mr-cluster", val)
}

func TestStoreSetAndUnset(t *testing.T) {
	store := NewStore(StoreOpts{})

	store.Set(keyURL, "https://dcos.example.com")
	store.Set(keyTimeout, "30")
	store.Set(keyReporting, "1")

	require.Equal(t, "https://dcos.example.com", store.Get(keyURL))
	require.Equal(t, true, store.Get(keyReporting))
	require.EqualValues(t, 30, store.Get(keyTimeout))

	store.Unset(keyURL)
	require.Nil(t, store.Get(keyURL))
}

func TestStoreKeys(t *testing.T) {
	treeMap := map[string]interface{}{
		"core": map[string]interface{}{
			"dcos_url": "https://dcos.example.com",
			"timeout":  15,
		},
		"cluster": map[string]interface{}{
			"name": "mr-cluster",
		},
		"marathon": map[string]interface{}{
			"url": "https://marathon.example.com",
		},
	}

	tree, err := toml.TreeFromMap(treeMap)
	require.NoError(t, err)

	store := NewStore(StoreOpts{
		Tree: tree,
		EnvLookup: func(key string) (string, bool) {
			switch key {
			case "DCOS_URL":
				return "https://dcos-env.example.com", true
			case "DCOS_SSL_VERIFY":
				return "true", true
			}
			return "", false
		},
	})

	keys := store.Keys()
	expectedKeys := []string{
		"cluster.name",
		"core.dcos_url",
		"core.ssl_verify",
		"core.timeout",
		"marathon.url",
	}
	require.Equal(t, expectedKeys, keys)
}

func TestPersistWithoutPath(t *testing.T) {
	store := NewStore(StoreOpts{})
	require.Equal(t, ErrNoStorePath, store.Persist())
}

func TestPersist(t *testing.T) {
	store := NewStore(StoreOpts{})

	f, _ := afero.TempFile(fs, "/", "config")
	store.SetPath(f.Name())

	require.NoError(t, store.Persist())
}
<|endoftext|>