<commit_before>// Copyright 2017 Yahoo Holdings, Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package zpu

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/ardielle/ardielle-go/rdl"
	"github.com/yahoo/athenz/clients/go/zts"
	"github.com/yahoo/athenz/libs/go/zmssvctoken"
	"github.com/yahoo/athenz/utils/zpe-updater/util"
)

func PolicyUpdater(config *ZpuConfiguration) error {
	if config == nil {
		return errors.New("Nil configuration")
	}
	if config.DomainList == "" {
		return errors.New("No domain list to process from configuration")
	}
	if config.Zts == "" {
		return errors.New("Empty Zts url in configuration")
	}
	success := true
	domains := strings.Split(config.DomainList, ",")
	ztsURL := formatURL(config.Zts, "zts/v1")
	ztsClient := zts.NewClient(ztsURL, nil)
	policyFileDir := config.PolicyFileDir
	failedDomains := ""
	for _, domain := range domains {
		err := GetPolicies(config, ztsClient, policyFileDir, domain)
		if err != nil {
			if success {
				success = false
			}
			failedDomains += `"`
			failedDomains += domain
			failedDomains += `" `
			log.Printf("Failed to get policies for domain: %v, Error:%v", domain, err)
		}
	}
	metricFilesPath := config.MetricsDir
	if metricFilesPath != "" {
		err := PostAllDomainMetric(ztsClient, metricFilesPath)
		if err != nil {
			log.Printf("Posting of metrics to Zts failed, Error:%v", err)
		}
	}
	if !success {
		return fmt.Errorf("Failed to get policies for domains: %v", failedDomains)
	}
	return nil
}

func GetPolicies(config *ZpuConfiguration, ztsClient zts.ZTSClient, policyFileDir, domain string) error {
	log.Printf("Getting policies for domain: %v", domain)
	etag := GetEtagForExistingPolicy(config, ztsClient, domain, policyFileDir)
	data, _, err := ztsClient.GetDomainSignedPolicyData(zts.DomainName(domain), etag)
	if err != nil {
		return fmt.Errorf("Failed to get domain signed policy data for domain: %v, Error:%v", domain, err)
	}

	if data == nil {
		if etag != "" {
			log.Printf("Policies not updated since last fetch for domain: %v", domain)
			return nil
		}
		return fmt.Errorf("Empty policies data returned for domain: %v", domain)
	}
	// validate data using zts public key and signature
	err = ValidateSignedPolicies(config, ztsClient, data)
	if err != nil {
		return fmt.Errorf("Failed to validate policy data for domain: %v, Error: %v", domain, err)
	}
	err = WritePolicies(config, data, domain, policyFileDir)
	if err != nil {
		return fmt.Errorf("Unable to write Policies for domain:\"%v\" to file, Error:%v", domain, err)
	}
	log.Printf("Policies for domain: %v successfully written", domain)
	return nil
}

func GetEtagForExistingPolicy(config *ZpuConfiguration, ztsClient zts.ZTSClient, domain, policyFileDir string) string {
	var etag string
	var domainSignedPolicyData *zts.DomainSignedPolicyData

	policyFile := fmt.Sprintf("%s/%s.pol", policyFileDir, domain)

	// If Policies file is not found, return empty etag the first time.
	// Otherwise load the file contents, if data has expired return empty etag,
	// else construct etag from modified field in JSON.
	exists := util.Exists(policyFile)
	if !exists {
		return ""
	}

	readFile, err := os.OpenFile(policyFile, os.O_RDONLY, 0444)
	defer readFile.Close()
	if err != nil {
		return ""
	}
	err = json.NewDecoder(readFile).Decode(&domainSignedPolicyData)
	if err != nil {
		return ""
	}
	err = ValidateSignedPolicies(config, ztsClient, domainSignedPolicyData)
	if err != nil {
		return ""
	}
	expires := domainSignedPolicyData.SignedPolicyData.Expires
	// We are going to see if we should consider the policy expired
	// and retrieve the latest policy. We're going to take the current
	// expiry timestamp from the policy file, subtract the expected
	// expiry check time (default 2 days) and then see if the date we
	// get should be considered as expired.
	if expired(expires, config.ExpiryCheck) {
		return ""
	}
	modified := domainSignedPolicyData.SignedPolicyData.Modified
	if !modified.IsZero() {
		etag = "\"" + string(modified.String()) + "\""
	}
	return etag
}

func ValidateSignedPolicies(config *ZpuConfiguration, ztsClient zts.ZTSClient, data *zts.DomainSignedPolicyData) error {
	expires := data.SignedPolicyData.Expires
	if expired(expires, 0) {
		return fmt.Errorf("The policy data is expired on %v", expires)
	}
	signedPolicyData := data.SignedPolicyData
	ztsSignature := data.Signature
	ztsKeyID := data.KeyId

	ztsPublicKey := config.GetZtsPublicKey(ztsKeyID)
	if ztsPublicKey == "" {
		key, err := ztsClient.GetPublicKeyEntry("sys.auth", "zts", ztsKeyID)
		if err != nil {
			return fmt.Errorf("Unable to get the Zts public key with id:\"%v\" to verify data", ztsKeyID)
		}
		decodedKey, err := new(zmssvctoken.YBase64).DecodeString(key.Key)
		if err != nil {
			return fmt.Errorf("Unable to decode the Zts public key with id:\"%v\" to verify data", ztsKeyID)
		}
		ztsPublicKey = string(decodedKey)
	}
	input, err := util.ToCanonicalString(signedPolicyData)
	if err != nil {
		return err
	}
	err = verify(input, ztsSignature, ztsPublicKey)
	if err != nil {
		return fmt.Errorf("Verification of data with zts key having id:\"%v\" failed, Error :%v", ztsKeyID, err)
	}
	zmsSignature := data.SignedPolicyData.ZmsSignature
	zmsKeyID := data.SignedPolicyData.ZmsKeyId
	zmsPublicKey := config.GetZmsPublicKey(zmsKeyID)
	if zmsPublicKey == "" {
		key, err := ztsClient.GetPublicKeyEntry("sys.auth", "zms", zmsKeyID)
		if err != nil {
			return fmt.Errorf("Unable to get the Zms public key with id:\"%v\" to verify data", zmsKeyID)
		}
		decodedKey, err := new(zmssvctoken.YBase64).DecodeString(key.Key)
		if err != nil {
			return fmt.Errorf("Unable to decode the Zms public key with id:\"%v\" to verify data", zmsKeyID)
		}
		zmsPublicKey = string(decodedKey)
	}
	policyData := data.SignedPolicyData.PolicyData
	input, err = util.ToCanonicalString(policyData)
	if err != nil {
		return err
	}
	err = verify(input, zmsSignature, zmsPublicKey)
	if err != nil {
		return fmt.Errorf("Verification of data with zms key with id:\"%v\" failed, Error :%v", zmsKeyID, err)
	}
	return nil
}

func verify(input, signature, publicKey string) error {
	verifier, err := zmssvctoken.NewVerifier([]byte(publicKey))
	if err != nil {
		return err
	}
	err = verifier.Verify(input, signature)
	return err
}

func expired(expires rdl.Timestamp, offset int) bool {
	expiryCheck := rdl.NewTimestamp(expires.Time.Add(-1 * time.Duration(int64(offset)) * time.Second))
	return rdl.TimestampNow().Millis() > expiryCheck.Millis()
}

// If domain policy file is not found, create the policy file and write policies in it.
// Else delete the existing file and write the modified policies to new file.
func WritePolicies(config *ZpuConfiguration, data *zts.DomainSignedPolicyData, domain, policyFileDir string) error {
	tempPolicyFileDir := config.TempPolicyFileDir
	if tempPolicyFileDir == "" || data == nil {
		return errors.New("Empty parameters are not valid arguments")
	}
	policyFile := fmt.Sprintf("%s/%s.pol", policyFileDir, domain)
	tempPolicyFile := fmt.Sprintf("%s/%s.tmp", tempPolicyFileDir, domain)
	if util.Exists(tempPolicyFile) {
		err := os.Remove(tempPolicyFile)
		if err != nil {
			return err
		}
	}

	bytes, err := json.Marshal(&data)
	if err != nil {
		return err
	}
	err = verifyTmpDirSetup(tempPolicyFileDir)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(tempPolicyFile, bytes, 0755)
	if err != nil {
		return err
	}
	os.Rename(tempPolicyFile, policyFile)
	return err
}

func verifyTmpDirSetup(TempPolicyFileDir string) error {
	if util.Exists(TempPolicyFileDir) {
		return nil
	}
	err := os.MkdirAll(TempPolicyFileDir, 0755)
	return err
}

func PostAllDomainMetric(ztsClient zts.ZTSClient, metricFilePath string) error {
	m, err := aggregateAllDomainMetrics(metricFilePath)
	if err != nil {
		return err
	}
	if m != nil {
		for key, value := range m {

			data, err := buildDomainMetrics(key, value)
			if err != nil {
				return err
			}
			log.Printf("Posting Domain metric for domain %v to Zts", key)
			data, err = ztsClient.PostDomainMetrics(zts.DomainName(key), data)
			if err != nil {
				log.Printf("Failed to post metrics for domain %v to Zts", key)
				return err
			}
			deleteDomainMetricFiles(metricFilePath, key)
		}
	}
	return nil
}

func aggregateAllDomainMetrics(metricFilePath string) (map[string]map[string]int, error) {
	var m = make(map[string]map[string]int)
	var fileMap = make(map[string]int)

	files, err := ioutil.ReadDir(metricFilePath)
	if err != nil {
		return nil, err
	}
	if len(files) == 0 {
		return nil, nil
	}
	for _, f := range files {
		data, err := ioutil.ReadFile(metricFilePath + "/" + f.Name())
		if err != nil {
			return nil, fmt.Errorf("Failed to read metric  file : %v, Error:%v", f.Name(), err)
		}
		fileMap = map[string]int{}
		err = json.Unmarshal(data, &fileMap)
		if err != nil {
			return nil, fmt.Errorf("Unmarshalling Error:%v for file : %v", err, f.Name())
		}
		domain := strings.Split(f.Name(), "_")
		if _, exists := m[domain[0]]; exists {
			domainMap := m[domain[0]]
			for key, value := range fileMap {
				if _, exists := domainMap[key]; exists {
					val := domainMap[key]
					val += value
					domainMap[key] = val
				} else {
					domainMap[key] = value
				}

			}
		} else {
			m[domain[0]] = fileMap
		}

	}
	return m, nil
}

func buildDomainMetrics(key string, value map[string]int) (*zts.DomainMetrics, error) {
	var data *zts.DomainMetrics
	counter := 1
	metricJSON := `{"domainName":"` + key + `","metricList":[`
	valuekeys := []string{}
	for k := range value {
		valuekeys = append(valuekeys, k)
	}
	sort.Strings(valuekeys)
	for _, innerKey := range valuekeys {
		size := len(value)
		if counter == size {
			metricJSON += `{"metricType":"` + innerKey + `","metricVal":` + strconv.Itoa(value[innerKey]) + `}`
		} else {
			metricJSON += `{"metricType":"` + innerKey + `","metricVal":` + strconv.Itoa(value[innerKey]) + `},`
		}
		counter++
	}
	metricJSON += `]}`
	err := json.Unmarshal([]byte(metricJSON), &data)
	if err != nil {
		return nil, err
	}
	return data, err
}

func deleteDomainMetricFiles(path, domainName string) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		log.Printf("Failed to get metric files at path for deletion: %v", path)
		return
	}
	for _, f := range files {
		domain := strings.Split(f.Name(), "_")
		if domain[0] == domainName {
			err := os.Remove(path + "/" + f.Name())
			if err != nil {
				log.Printf("Failed to delete file : % v for domain : %v", f.Name(), domainName)
			}
		}
	}
}

func formatURL(url, suffix string) string {
	if !strings.HasSuffix(url, suffix) {
		if strings.LastIndex(url, "/") != len(url)-1 {
			url += "/"
		}
		url += suffix
	}
	return url
}
<commit_msg>Write Policies method returns err if rename fails (#409)<commit_after>// Copyright 2017 Yahoo Holdings, Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package zpu

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/ardielle/ardielle-go/rdl"
	"github.com/yahoo/athenz/clients/go/zts"
	"github.com/yahoo/athenz/libs/go/zmssvctoken"
	"github.com/yahoo/athenz/utils/zpe-updater/util"
)

func PolicyUpdater(config *ZpuConfiguration) error {
	if config == nil {
		return errors.New("Nil configuration")
	}
	if config.DomainList == "" {
		return errors.New("No domain list to process from configuration")
	}
	if config.Zts == "" {
		return errors.New("Empty Zts url in configuration")
	}
	success := true
	domains := strings.Split(config.DomainList, ",")
	ztsURL := formatURL(config.Zts, "zts/v1")
	ztsClient := zts.NewClient(ztsURL, nil)
	policyFileDir := config.PolicyFileDir
	failedDomains := ""
	for _, domain := range domains {
		err := GetPolicies(config, ztsClient, policyFileDir, domain)
		if err != nil {
			if success {
				success = false
			}
			failedDomains += `"`
			failedDomains += domain
			failedDomains += `" `
			log.Printf("Failed to get policies for domain: %v, Error:%v", domain, err)
		}
	}
	metricFilesPath := config.MetricsDir
	if metricFilesPath != "" {
		err := PostAllDomainMetric(ztsClient, metricFilesPath)
		if err != nil {
			log.Printf("Posting of metrics to Zts failed, Error:%v", err)
		}
	}
	if !success {
		return fmt.Errorf("Failed to get policies for domains: %v", failedDomains)
	}
	return nil
}

func GetPolicies(config *ZpuConfiguration, ztsClient zts.ZTSClient, policyFileDir, domain string) error {
	log.Printf("Getting policies for domain: %v", domain)
	etag := GetEtagForExistingPolicy(config, ztsClient, domain, policyFileDir)
	data, _, err := ztsClient.GetDomainSignedPolicyData(zts.DomainName(domain), etag)
	if err != nil {
		return fmt.Errorf("Failed to get domain signed policy data for domain: %v, Error:%v", domain, err)
	}

	if data == nil {
		if etag != "" {
			log.Printf("Policies not updated since last fetch for domain: %v", domain)
			return nil
		}
		return fmt.Errorf("Empty policies data returned for domain: %v", domain)
	}
	// validate data using zts public key and signature
	err = ValidateSignedPolicies(config, ztsClient, data)
	if err != nil {
		return fmt.Errorf("Failed to validate policy data for domain: %v, Error: %v", domain, err)
	}
	err = WritePolicies(config, data, domain, policyFileDir)
	if err != nil {
		return fmt.Errorf("Unable to write Policies for domain:\"%v\" to file, Error:%v", domain, err)
	}
	log.Printf("Policies for domain: %v successfully written", domain)
	return nil
}

func GetEtagForExistingPolicy(config *ZpuConfiguration, ztsClient zts.ZTSClient, domain, policyFileDir string) string {
	var etag string
	var domainSignedPolicyData *zts.DomainSignedPolicyData

	policyFile := fmt.Sprintf("%s/%s.pol", policyFileDir, domain)

	// If Policies file is not found, return empty etag the first time.
	// Otherwise load the file contents, if data has expired return empty etag,
	// else construct etag from modified field in JSON.
	exists := util.Exists(policyFile)
	if !exists {
		return ""
	}

	readFile, err := os.OpenFile(policyFile, os.O_RDONLY, 0444)
	defer readFile.Close()
	if err != nil {
		return ""
	}
	err = json.NewDecoder(readFile).Decode(&domainSignedPolicyData)
	if err != nil {
		return ""
	}
	err = ValidateSignedPolicies(config, ztsClient, domainSignedPolicyData)
	if err != nil {
		return ""
	}
	expires := domainSignedPolicyData.SignedPolicyData.Expires
	// We are going to see if we should consider the policy expired
	// and retrieve the latest policy. We're going to take the current
	// expiry timestamp from the policy file, subtract the expected
	// expiry check time (default 2 days) and then see if the date we
	// get should be considered as expired.
	if expired(expires, config.ExpiryCheck) {
		return ""
	}
	modified := domainSignedPolicyData.SignedPolicyData.Modified
	if !modified.IsZero() {
		etag = "\"" + string(modified.String()) + "\""
	}
	return etag
}

func ValidateSignedPolicies(config *ZpuConfiguration, ztsClient zts.ZTSClient, data *zts.DomainSignedPolicyData) error {
	expires := data.SignedPolicyData.Expires
	if expired(expires, 0) {
		return fmt.Errorf("The policy data is expired on %v", expires)
	}
	signedPolicyData := data.SignedPolicyData
	ztsSignature := data.Signature
	ztsKeyID := data.KeyId

	ztsPublicKey := config.GetZtsPublicKey(ztsKeyID)
	if ztsPublicKey == "" {
		key, err := ztsClient.GetPublicKeyEntry("sys.auth", "zts", ztsKeyID)
		if err != nil {
			return fmt.Errorf("Unable to get the Zts public key with id:\"%v\" to verify data", ztsKeyID)
		}
		decodedKey, err := new(zmssvctoken.YBase64).DecodeString(key.Key)
		if err != nil {
			return fmt.Errorf("Unable to decode the Zts public key with id:\"%v\" to verify data", ztsKeyID)
		}
		ztsPublicKey = string(decodedKey)
	}
	input, err := util.ToCanonicalString(signedPolicyData)
	if err != nil {
		return err
	}
	err = verify(input, ztsSignature, ztsPublicKey)
	if err != nil {
		return fmt.Errorf("Verification of data with zts key having id:\"%v\" failed, Error :%v", ztsKeyID, err)
	}
	zmsSignature := data.SignedPolicyData.ZmsSignature
	zmsKeyID := data.SignedPolicyData.ZmsKeyId
	zmsPublicKey := config.GetZmsPublicKey(zmsKeyID)
	if zmsPublicKey == "" {
		key, err := ztsClient.GetPublicKeyEntry("sys.auth", "zms", zmsKeyID)
		if err != nil {
			return fmt.Errorf("Unable to get the Zms public key with id:\"%v\" to verify data", zmsKeyID)
		}
		decodedKey, err := new(zmssvctoken.YBase64).DecodeString(key.Key)
		if err != nil {
			return fmt.Errorf("Unable to decode the Zms public key with id:\"%v\" to verify data", zmsKeyID)
		}
		zmsPublicKey = string(decodedKey)
	}
	policyData := data.SignedPolicyData.PolicyData
	input, err = util.ToCanonicalString(policyData)
	if err != nil {
		return err
	}
	err = verify(input, zmsSignature, zmsPublicKey)
	if err != nil {
		return fmt.Errorf("Verification of data with zms key with id:\"%v\" failed, Error :%v", zmsKeyID, err)
	}
	return nil
}

func verify(input, signature, publicKey string) error {
	verifier, err := zmssvctoken.NewVerifier([]byte(publicKey))
	if err != nil {
		return err
	}
	err = verifier.Verify(input, signature)
	return err
}

func expired(expires rdl.Timestamp, offset int) bool {
	expiryCheck := rdl.NewTimestamp(expires.Time.Add(-1 * time.Duration(int64(offset)) * time.Second))
	return rdl.TimestampNow().Millis() > expiryCheck.Millis()
}

// If domain policy file is not found, create the policy file and write policies in it.
// Else delete the existing file and write the modified policies to new file.
func WritePolicies(config *ZpuConfiguration, data *zts.DomainSignedPolicyData, domain, policyFileDir string) error {
	tempPolicyFileDir := config.TempPolicyFileDir
	if tempPolicyFileDir == "" || data == nil {
		return errors.New("Empty parameters are not valid arguments")
	}
	policyFile := fmt.Sprintf("%s/%s.pol", policyFileDir, domain)
	tempPolicyFile := fmt.Sprintf("%s/%s.tmp", tempPolicyFileDir, domain)
	if util.Exists(tempPolicyFile) {
		err := os.Remove(tempPolicyFile)
		if err != nil {
			return err
		}
	}

	bytes, err := json.Marshal(&data)
	if err != nil {
		return err
	}
	err = verifyTmpDirSetup(tempPolicyFileDir)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(tempPolicyFile, bytes, 0755)
	if err != nil {
		return err
	}
	err = os.Rename(tempPolicyFile, policyFile)
	return err
}

func verifyTmpDirSetup(TempPolicyFileDir string) error {
	if util.Exists(TempPolicyFileDir) {
		return nil
	}
	err := os.MkdirAll(TempPolicyFileDir, 0755)
	return err
}

func PostAllDomainMetric(ztsClient zts.ZTSClient, metricFilePath string) error {
	m, err := aggregateAllDomainMetrics(metricFilePath)
	if err != nil {
		return err
	}
	if m != nil {
		for key, value := range m {

			data, err := buildDomainMetrics(key, value)
			if err != nil {
				return err
			}
			log.Printf("Posting Domain metric for domain %v to Zts", key)
			data, err = ztsClient.PostDomainMetrics(zts.DomainName(key), data)
			if err != nil {
				log.Printf("Failed to post metrics for domain %v to Zts", key)
				return err
			}
			deleteDomainMetricFiles(metricFilePath, key)
		}
	}
	return nil
}

func aggregateAllDomainMetrics(metricFilePath string) (map[string]map[string]int, error) {
	var m = make(map[string]map[string]int)
	var fileMap = make(map[string]int)

	files, err := ioutil.ReadDir(metricFilePath)
	if err != nil {
		return nil, err
	}
	if len(files) == 0 {
		return nil, nil
	}
	for _, f := range files {
		data, err := ioutil.ReadFile(metricFilePath + "/" + f.Name())
		if err != nil {
			return nil, fmt.Errorf("Failed to read metric  file : %v, Error:%v", f.Name(), err)
		}
		fileMap = map[string]int{}
		err = json.Unmarshal(data, &fileMap)
		if err != nil {
			return nil, fmt.Errorf("Unmarshalling Error:%v for file : %v", err, f.Name())
		}
		domain := strings.Split(f.Name(), "_")
		if _, exists := m[domain[0]]; exists {
			domainMap := m[domain[0]]
			for key, value := range fileMap {
				if _, exists := domainMap[key]; exists {
					val := domainMap[key]
					val += value
					domainMap[key] = val
				} else {
					domainMap[key] = value
				}

			}
		} else {
			m[domain[0]] = fileMap
		}

	}
	return m, nil
}

func buildDomainMetrics(key string, value map[string]int) (*zts.DomainMetrics, error) {
	var data *zts.DomainMetrics
	counter := 1
	metricJSON := `{"domainName":"` + key + `","metricList":[`
	valuekeys := []string{}
	for k := range value {
		valuekeys = append(valuekeys, k)
	}
	sort.Strings(valuekeys)
	for _, innerKey := range valuekeys {
		size := len(value)
		if counter == size {
			metricJSON += `{"metricType":"` + innerKey + `","metricVal":` + strconv.Itoa(value[innerKey]) + `}`
		} else {
			metricJSON += `{"metricType":"` + innerKey + `","metricVal":` + strconv.Itoa(value[innerKey]) + `},`
		}
		counter++
	}
	metricJSON += `]}`
	err := json.Unmarshal([]byte(metricJSON), &data)
	if err != nil {
		return nil, err
	}
	return data, err
}

func deleteDomainMetricFiles(path, domainName string) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		log.Printf("Failed to get metric files at path for deletion: %v", path)
		return
	}
	for _, f := range files {
		domain := strings.Split(f.Name(), "_")
		if domain[0] == domainName {
			err := os.Remove(path + "/" + f.Name())
			if err != nil {
				log.Printf("Failed to delete file : % v for domain : %v", f.Name(), domainName)
			}
		}
	}
}

func formatURL(url, suffix string) string {
	if !strings.HasSuffix(url, suffix) {
		if strings.LastIndex(url, "/") != len(url)-1 {
			url += "/"
		}
		url += suffix
	}
	return url
}
<|endoftext|>


<commit_before>package grappos

import "testing"

func TestGetLocations(t *testing.T) {
	locations := [4]int{0, 1, 2, 3}
	invalidLocation := -1

	for _, location := range locations {
		_, err := GetLocations(location)

		if err != nil {
			t.Fatal(err)
		}
	}

	_, err := GetLocations(invalidLocation)
	if err == nil {
		t.Fatal("There isn't an error, which is bad")
	}
}

func TestSearchForLocation(t *testing.T) {
	goodLocation := "11238"
	badLocation := "11"

	_, err := SearchForLocation(goodLocation)

	if err != nil {
		t.Fatal(err)
	}

	_, err = SearchForLocation(badLocation)
	if err == nil {
		t.Fatal("This should not work")
	}
}

func TestSearchForLocationByCityName(t *testing.T) {
	goodLocation := "San Francisco"
	badLocation := "QWERTY-layout"

	_, err := SearchForLocation(goodLocation)

	if err != nil {
		t.Fatal(err)
	}

	_, err = SearchForLocation(badLocation)
	if err == nil {
		t.Fatal("This should not work")
	}
}
<commit_msg>Remove test case for invalid postcode (len < 5) as search for location is now based on partial string matching<commit_after>package grappos

import "testing"

func TestGetLocations(t *testing.T) {
	locations := [4]int{0, 1, 2, 3}
	invalidLocation := -1

	for _, location := range locations {
		_, err := GetLocations(location)

		if err != nil {
			t.Fatal(err)
		}
	}

	_, err := GetLocations(invalidLocation)
	if err == nil {
		t.Fatal("There isn't an error, which is bad")
	}
}

func TestSearchForLocation(t *testing.T) {
	postcode := "11238"
	_, err := SearchForLocation(postcode)

	if err != nil {
		t.Fatal(err)
	}
}

func TestSearchForLocationByCityName(t *testing.T) {
	goodLocation := "San Francisco"
	badLocation := "QWERTY-layout"

	_, err := SearchForLocation(goodLocation)

	if err != nil {
		t.Fatal(err)
	}

	_, err = SearchForLocation(badLocation)
	if err == nil {
		t.Fatal("This should not work")
	}
}
<|endoftext|>


<commit_before>package index

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/radar"
	"github.com/concourse/atc/web/routes"
)

type handler struct {
	logger lager.Logger

	// used for providing resource state
	radar *radar.Radar

	resources config.Resources
	jobs      config.Jobs
	db        db.DB
	template  *template.Template
}

func NewHandler(logger lager.Logger, radar *radar.Radar, resources config.Resources, jobs config.Jobs, db db.DB, template *template.Template) http.Handler {
	return &handler{
		logger: logger,

		radar: radar,

		resources: resources,
		jobs:      jobs,
		db:        db,
		template:  template,
	}
}

type TemplateData struct {
	Jobs  []JobStatus
	Nodes []DotNode
	Edges []DotEdge
}

type DotNode struct {
	ID    string            `json:"id"`
	Value map[string]string `json:"value,omitempty"`
}

type DotEdge struct {
	Source      string            `json:"u"`
	Destination string            `json:"v"`
	Value       map[string]string `json:"value,omitempty"`
}

type JobStatus struct {
	Job          config.Job
	CurrentBuild builds.Build
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	data := TemplateData{
		Nodes: []DotNode{},
		Edges: []DotEdge{},
	}

	log := handler.logger.Session("index")

	currentBuilds := map[string]builds.Build{}

	for _, job := range handler.jobs {
		currentBuild, err := handler.db.GetCurrentBuild(job.Name)
		if err != nil {
			currentBuild.Status = builds.StatusPending
		}

		currentBuilds[job.Name] = currentBuild

		data.Jobs = append(data.Jobs, JobStatus{
			Job:          job,
			CurrentBuild: currentBuild,
		})
	}

	for _, resource := range handler.resources {
		resourceID := resourceNode(resource.Name)

		resourceURI, _ := routes.Routes.CreatePathForRoute(routes.GetResource, rata.Params{
			"resource": resource.Name,
		})

		failing, checking := handler.radar.ResourceStatus(resource.Name)

		var status string
		if failing {
			status = "failing"
		} else {
			status = "ok"
		}

		if checking {
			status += " checking"
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: resourceID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="resource"><a href="%s">%s</a></h1>`, resourceURI, resource.Name),
				"type":   "resource",
				"status": status,
			},
		})
	}

	for _, job := range handler.jobs {
		jobID := jobNode(job.Name)
		currentBuild := currentBuilds[job.Name]

		var buildURI string
		var err error

		if currentBuild.Name != "" {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
				"job":   job.Name,
				"build": currentBuild.Name,
			})
		} else {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetJob, rata.Params{
				"job": job.Name,
			})
		}

		if err != nil {
			log.Error("failed-to-create-route", err)
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: jobID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="job"><a href="%s">%s</a>`, buildURI, job.Name),
				"status": string(currentBuild.Status),
				"type":   "job",
			},
		})

		edges := map[string]DotEdge{}

		for _, input := range job.Inputs {
			if len(input.Passed) > 0 {
				var nodeID string

				if len(input.Passed) > 1 {
					nodeID = jobID + "-input-" + input.Resource

					data.Nodes = append(data.Nodes, DotNode{
						ID: nodeID,
						Value: map[string]string{
							"useDef": "gateway",
						},
					})

					data.Edges = append(data.Edges, DotEdge{
						Source:      nodeID,
						Destination: jobID,
					})
				} else {
					nodeID = jobID
				}

				for _, passed := range input.Passed {
					currentBuild := currentBuilds[passed]

					passedJob, found := handler.jobs.Lookup(passed)
					if !found {
						panic("unknown job: " + passed)
					}

					existingEdge, found := edges[passed]
					if found {
						if len(passedJob.Inputs) > 1 {
							existingEdge.Value["label"] += "\n" + input.Resource
						}
					} else {
						value := map[string]string{
							"status": string(currentBuild.Status),
						}

						if len(passedJob.Inputs) > 1 {
							value["label"] = input.Resource
						}

						edges[passed] = DotEdge{
							Source:      jobNode(passed),
							Destination: nodeID,
							Value:       value,
						}
					}
				}
			} else {
				data.Edges = append(data.Edges, DotEdge{
					Source:      resourceNode(input.Resource),
					Destination: jobID,
				})
			}
		}

		for _, edge := range edges {
			data.Edges = append(data.Edges, edge)
		}

		for _, output := range job.Outputs {
			data.Edges = append(data.Edges, DotEdge{
				Source:      jobID,
				Destination: resourceNode(output.Resource),
				Value: map[string]string{
					"status": string(currentBuild.Status),
				},
			})
		}
	}

	err := handler.template.Execute(w, data)
	if err != nil {
		log.Fatal("failed-to-execute-template", err, lager.Data{
			"template-data": data,
		})
	}
}

func resourceNode(resource string) string {
	return "resource-" + resource
}

func jobNode(job string) string {
	return "job-" + job
}
<commit_msg>hide resources from pipeline with ?hide-resources<commit_after>package index

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/radar"
	"github.com/concourse/atc/web/routes"
)

type handler struct {
	logger lager.Logger

	// used for providing resource state
	radar *radar.Radar

	resources config.Resources
	jobs      config.Jobs
	db        db.DB
	template  *template.Template
}

func NewHandler(logger lager.Logger, radar *radar.Radar, resources config.Resources, jobs config.Jobs, db db.DB, template *template.Template) http.Handler {
	return &handler{
		logger: logger,

		radar: radar,

		resources: resources,
		jobs:      jobs,
		db:        db,
		template:  template,
	}
}

type TemplateData struct {
	Jobs  []JobStatus
	Nodes []DotNode
	Edges []DotEdge
}

type DotNode struct {
	ID    string            `json:"id"`
	Value map[string]string `json:"value,omitempty"`
}

type DotEdge struct {
	Source      string            `json:"u"`
	Destination string            `json:"v"`
	Value       map[string]string `json:"value,omitempty"`
}

type JobStatus struct {
	Job          config.Job
	CurrentBuild builds.Build
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, hideResources := r.URL.Query()["hide-resources"]
	showResources := !hideResources

	data := TemplateData{
		Nodes: []DotNode{},
		Edges: []DotEdge{},
	}

	log := handler.logger.Session("index")

	currentBuilds := map[string]builds.Build{}

	for _, job := range handler.jobs {
		currentBuild, err := handler.db.GetCurrentBuild(job.Name)
		if err != nil {
			currentBuild.Status = builds.StatusPending
		}

		currentBuilds[job.Name] = currentBuild

		data.Jobs = append(data.Jobs, JobStatus{
			Job:          job,
			CurrentBuild: currentBuild,
		})
	}

	if showResources {
		for _, resource := range handler.resources {
			resourceID := resourceNode(resource.Name)

			resourceURI, _ := routes.Routes.CreatePathForRoute(routes.GetResource, rata.Params{
				"resource": resource.Name,
			})

			failing, checking := handler.radar.ResourceStatus(resource.Name)

			var status string
			if failing {
				status = "failing"
			} else {
				status = "ok"
			}

			if checking {
				status += " checking"
			}

			data.Nodes = append(data.Nodes, DotNode{
				ID: resourceID,
				Value: map[string]string{
					"label":  fmt.Sprintf(`<h1 class="resource"><a href="%s">%s</a></h1>`, resourceURI, resource.Name),
					"type":   "resource",
					"status": status,
				},
			})
		}
	}

	for _, job := range handler.jobs {
		jobID := jobNode(job.Name)
		currentBuild := currentBuilds[job.Name]

		var buildURI string
		var err error

		if currentBuild.Name != "" {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
				"job":   job.Name,
				"build": currentBuild.Name,
			})
		} else {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetJob, rata.Params{
				"job": job.Name,
			})
		}

		if err != nil {
			log.Error("failed-to-create-route", err)
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: jobID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="job"><a href="%s">%s</a>`, buildURI, job.Name),
				"status": string(currentBuild.Status),
				"type":   "job",
			},
		})

		edges := map[string]DotEdge{}

		for _, input := range job.Inputs {
			if len(input.Passed) > 0 {
				var nodeID string

				if len(input.Passed) > 1 {
					nodeID = jobID + "-input-" + input.Resource

					data.Nodes = append(data.Nodes, DotNode{
						ID: nodeID,
						Value: map[string]string{
							"useDef": "gateway",
						},
					})

					data.Edges = append(data.Edges, DotEdge{
						Source:      nodeID,
						Destination: jobID,
					})
				} else {
					nodeID = jobID
				}

				for _, passed := range input.Passed {
					currentBuild := currentBuilds[passed]

					passedJob, found := handler.jobs.Lookup(passed)
					if !found {
						panic("unknown job: " + passed)
					}

					existingEdge, found := edges[passed]
					if found {
						if len(passedJob.Inputs) > 1 {
							existingEdge.Value["label"] += "\n" + input.Resource
						}
					} else {
						value := map[string]string{
							"status": string(currentBuild.Status),
						}

						if len(passedJob.Inputs) > 1 {
							value["label"] = input.Resource
						}

						edges[passed] = DotEdge{
							Source:      jobNode(passed),
							Destination: nodeID,
							Value:       value,
						}
					}
				}
			} else if showResources {
				data.Edges = append(data.Edges, DotEdge{
					Source:      resourceNode(input.Resource),
					Destination: jobID,
				})
			}
		}

		for _, edge := range edges {
			data.Edges = append(data.Edges, edge)
		}

		if showResources {
			for _, output := range job.Outputs {
				data.Edges = append(data.Edges, DotEdge{
					Source:      jobID,
					Destination: resourceNode(output.Resource),
					Value: map[string]string{
						"status": string(currentBuild.Status),
					},
				})
			}
		}
	}

	err := handler.template.Execute(w, data)
	if err != nil {
		log.Fatal("failed-to-execute-template", err, lager.Data{
			"template-data": data,
		})
	}
}

func resourceNode(resource string) string {
	return "resource-" + resource
}

func jobNode(job string) string {
	return "job-" + job
}
<|endoftext|>


<commit_before>package instance

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

type uploadCmdData struct {
	AccessKey       string
	BucketName      string
	BundleDirectory string
	ManifestPath    string
	Region          string
	SecretKey       string
}

type StepUploadBundle struct{}

func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
	comm := state.Get("communicator").(packer.Communicator)
	config := state.Get("config").(*Config)
	manifestName := state.Get("manifest_name").(string)
	manifestPath := state.Get("manifest_path").(string)
	ui := state.Get("ui").(packer.Ui)

	region, err := config.Region()
	if err != nil {
		err := fmt.Errorf("Error retrieving region: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	config.BundleUploadCommand, err = config.tpl.Process(config.BundleUploadCommand, uploadCmdData{
		AccessKey:       config.AccessKey,
		BucketName:      config.S3Bucket,
		BundleDirectory: config.BundleDestination,
		ManifestPath:    manifestPath,
		Region:          region.Name,
		SecretKey:       config.SecretKey,
	})
	if err != nil {
		err := fmt.Errorf("Error processing bundle upload command: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Uploading the bundle...")
	cmd := &packer.RemoteCmd{Command: config.BundleUploadCommand}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		state.Put("error", fmt.Errorf("Error uploading volume: %s", err))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	if cmd.ExitStatus != 0 {
		state.Put("error", fmt.Errorf(
			"Bundle upload failed. Please see the output above for more\n"+
				"details on what went wrong."))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	state.Put("remote_manifest_path", fmt.Sprintf(
		"%s/%s", config.S3Bucket, manifestName))

	return multistep.ActionContinue
}

func (s *StepUploadBundle) Cleanup(state multistep.StateBag) {}
<commit_msg>builder/amazon/instance: use US if region us-east-1 for upload [GH-729]<commit_after>package instance

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

type uploadCmdData struct {
	AccessKey       string
	BucketName      string
	BundleDirectory string
	ManifestPath    string
	Region          string
	SecretKey       string
}

type StepUploadBundle struct{}

func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
	comm := state.Get("communicator").(packer.Communicator)
	config := state.Get("config").(*Config)
	manifestName := state.Get("manifest_name").(string)
	manifestPath := state.Get("manifest_path").(string)
	ui := state.Get("ui").(packer.Ui)

	region, err := config.Region()
	if err != nil {
		err := fmt.Errorf("Error retrieving region: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// See GH-729 and http://goo.gl/rNZiCs
	regionName := region.Name
	if regionName == "us-east-1" {
		regionName = "US"
	}

	config.BundleUploadCommand, err = config.tpl.Process(config.BundleUploadCommand, uploadCmdData{
		AccessKey:       config.AccessKey,
		BucketName:      config.S3Bucket,
		BundleDirectory: config.BundleDestination,
		ManifestPath:    manifestPath,
		Region:          regionName,
		SecretKey:       config.SecretKey,
	})
	if err != nil {
		err := fmt.Errorf("Error processing bundle upload command: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Uploading the bundle...")
	cmd := &packer.RemoteCmd{Command: config.BundleUploadCommand}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		state.Put("error", fmt.Errorf("Error uploading volume: %s", err))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	if cmd.ExitStatus != 0 {
		state.Put("error", fmt.Errorf(
			"Bundle upload failed. Please see the output above for more\n"+
				"details on what went wrong."))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	state.Put("remote_manifest_path", fmt.Sprintf(
		"%s/%s", config.S3Bucket, manifestName))

	return multistep.ActionContinue
}

func (s *StepUploadBundle) Cleanup(state multistep.StateBag) {}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"time"

	"github.com/jurgen-kluft/go-home/config"
	logpkg "github.com/jurgen-kluft/go-home/logging"
	"github.com/jurgen-kluft/go-home/metrics"
	"github.com/jurgen-kluft/go-home/pubsub"
)

// Color Temperature
// URL: https://panasonic.net/es/solution-works/jiyugaoka/

func inTimeSpan(start, end, t time.Time) bool {
	sh, sm, sc := start.Clock()
	sx := float64(sh*60*60) + float64(sm*60) + float64(sc)
	eh, em, ec := end.Clock()
	ex := float64(eh*60*60) + float64(em*60) + float64(ec) + 1.0
	th, tm, tc := t.Clock()
	tx := float64(th*60*60) + float64(tm*60) + float64(tc)
	return tx >= sx && tx < ex
}

// Return the factor 0.0 - 1.0 that indicates where we are in between start - end
func computeTimeSpanX(start, end, t time.Time) float64 {
	sh, sm, sc := start.Clock()
	sx := float64(sh*60*60) + float64(sm*60) + float64(sc)
	eh, em, ec := end.Clock()
	ex := float64(eh*60*60) + float64(em*60) + float64(ec) + 1.0
	th, tm, tc := t.Clock()
	tx := float64(th*60*60) + float64(tm*60) + float64(tc)
	x := (tx - sx) / (ex - sx)
	return x
}

type MovingAverage struct {
	history []float64
	index   int
}

func NewFilter(sizeOfHistory int) *MovingAverage {
	filter := &MovingAverage{history: make([]float64, sizeOfHistory), index: -1}
	return filter
}

func (m *MovingAverage) Sample(sample float64) float64 {
	if m.index == -1 {
		for i := range m.history {
			m.history[i] = sample
		}
		m.index = 0
	}

	m.history[m.index] = sample
	m.index = (m.index + 1) % len(m.history)

	sum := 0.0
	for _, s := range m.history {
		sum += s
	}
	return sum / float64(len(m.history))
}

type context struct {
	name       string
	cfgch      string
	config     *config.FluxConfig
	metrics    *metrics.Metrics
	suncalc    *config.SensorState
	seasonName string
	season     *config.Season
	weather    *config.SensorState
	averageCT  *MovingAverage
	averageBRI *MovingAverage
}

func new() *context {
	c := &context{}
	c.name = c.name
	c.cfgch = "config/flux/"
	c.metrics, _ = metrics.New()
	c.metrics.Register("hue", map[string]string{"CT": "Color Temperature", "BRI": "Brightness"}, map[string]interface{}{"CT": 200.0, "BRI": 200.0})
	c.metrics.Register("yee", map[string]string{"CT": "Color Temperature", "BRI": "Brightness"}, map[string]interface{}{"CT": 200.0, "BRI": 200.0})
	c.seasonName = "spring"
	c.averageCT = NewFilter(30)
	c.averageBRI = NewFilter(30)
	return c
}

func (c *context) updateSeasonFromName(season string) {
	for _, e := range c.config.Seasons {
		if e.Name == season {
			c.season = &config.Season{}
			*c.season = e
		}
	}
}

// Process will update 'string'states and 'float'states
// States are both input and output, for example as input
// there are Season/Weather states like 'Season':'Winter'
// and 'Clouds':0.5
func (c *context) Process(client *pubsub.Context) {
	if c.config == nil || c.suncalc == nil {
		return
	}

	now := time.Now()

	// Update our season
	c.updateSeasonFromName(c.seasonName)

	// First build our sun moments map
	sunmoments := map[string]time.Time{}
	for _, tss := range c.suncalc.TimeWndAttrs {
		sunmoments[tss.Name+".begin"] = tss.Begin
		sunmoments[tss.Name+".end"] = tss.End
	}

	// Add our custom time-points to the sun moments map
	for _, at := range c.config.SuncalcMoments {
		moment, exists := sunmoments[at.Name]
		if exists {
			moment = moment.Add(time.Duration(at.Shift) * time.Minute)
			sunmoments[at.Name+at.Tag] = moment
		}
	}

	// Update our Lighttime start and end time from the sun moments map
	for i, lt := range c.config.Lighttime {
		start, exists := sunmoments[lt.TimeSlot.StartMoment]
		if exists {
			end, exists := sunmoments[lt.TimeSlot.EndMoment]
			if exists {
				c.config.Lighttime[i].TimeSlot.StartTime = start
				c.config.Lighttime[i].TimeSlot.EndTime = end
			}
		}
	}

	// Figure out in which light time moment we are now
	current := config.Lighttime{}
	for _, sm := range c.config.Lighttime {
		t0 := sm.TimeSlot.StartTime
		t1 := sm.TimeSlot.EndTime
		if inTimeSpan(t0, t1, now) {
			fmt.Println("Current light time from", sm.TimeSlot.StartMoment, "to", sm.TimeSlot.EndMoment)
			current = sm
			break
		}
	}

	// Time interpolation factor, where are we between startMoment - endMoment
	currentx := computeTimeSpanX(current.TimeSlot.StartTime, current.TimeSlot.EndTime, now)
	currentx = float64(int64(currentx*100.0)) / 100.0

	clouds := config.Weather{Clouds: config.MinMax{Min: 0.0, Max: 0.001}, CTPct: 0.0, BriPct: 0.0}
	cloudFac := float64(0.0)
	if c.weather != nil {
		cloudFac = c.weather.GetFloatAttr("clouds", 0.0)
	}
	for _, w := range c.config.Weather {
		if cloudFac >= w.Clouds.Min && cloudFac < w.Clouds.Max {
			clouds = w
			break
		}
	}

	// Full cloud cover will increase color-temperature by 10% of (Max - Current)
	// NOTE: Only during the day (twilight + light)
	// TODO: when the moon is shining in the night the amount
	//       of blue-light is also higher than normal.
	// CT = 0.0 -> Coldest (>6500K)
	// CT = 1.0 -> Warmest (2000K)
	CT := current.CT.LinearInterpolated(currentx)
	if current.Darkorlight != "dark" {
		if clouds.CTPct >= 0 {
			CT = CT + clouds.CTPct*(1.0-CT)
		} else {
			CT = CT + clouds.CTPct*CT
		}
	}
	CT = c.season.CT.LinearInterpolated(CT)
	CT = c.averageCT.Sample(CT)

	// Full cloud cover will increase brightness by 10% of (Max - Current)
	// BRI = 0 -> Very dim light
	// BRI = 1 -> Very bright light
	BRI := current.Bri.LinearInterpolated(currentx)
	BRI = BRI + cloudFac*0.1*(1.0-BRI)
	if current.Darkorlight != "dark" {
		// A bit brighter lights when there are clouds during the day.
		if clouds.BriPct >= 0 {
			BRI = BRI + clouds.BriPct*(1.0-BRI)
		} else {
			BRI = BRI + clouds.BriPct*BRI
		}
	}
	BRI = c.season.BRI.LinearInterpolated(BRI)
	BRI = c.averageBRI.Sample(BRI)

	// Publishing the following sensors:
	//  - Sensor.Light.HUE, Name = CT, Value = float64(100.0)
	//  - Sensor.Light.HUE, Name = BRI, Value = float64(100.0)
	//  - Sensor.Light.YEE, Name = CT, Value = float64(100.0)
	//  - Sensor.Light.YEE, Name = BRI, Value = float64(100.0)
	//  - Sensor.Light.DarkOrLight = string(Dark)

	for _, ltype := range c.config.Lighttype {
		sensor := config.NewSensorState("all")

		c.metrics.Begin(ltype.Name)

		lct := ltype.CT.LinearInterpolated(CT)
		sensor.AddFloatAttr("CT", math.Floor(lct))
		c.metrics.Set(ltype.Name, "CT", lct)

		lbri := ltype.BRI.LinearInterpolated(BRI)
		sensor.AddFloatAttr("BRI", math.Floor(lbri))
		c.metrics.Set(ltype.Name, "BRI", lbri)

		c.metrics.Send(ltype.Name)

		jsonstr, err := sensor.ToJSON()
		if err == nil {
			publishSensor(fmt.Sprintf("state/sensor/%s/", ltype.Name), jsonstr, client)
		}
	}

	sensorDOL, err := config.StringAttrAsJSON("darkorlight", "DarkOrLight", string(current.Darkorlight))
	if err == nil {
		publishSensor("state/sensor/darkorlight/", sensorDOL, client)
	}
}

func publishSensor(channel string, sensorjson string, client *pubsub.Context) {
	fmt.Println("Publish at", channel, "JSON [", sensorjson, "]")
	client.Publish(channel, sensorjson)
}

func main() {
	c := new()

	logger := logpkg.New(c.name)
	logger.AddEntry("emitter")
	logger.AddEntry(c.name)

	for {
		client := pubsub.New(config.PubSubCfg)
		register := []string{"config/flux/", "state/sensor/weather/", "state/sensor/sun/", "state/sensor/season/", "state/light/hue/", "state/light/yee/"}
		subscribe := []string{"config/flux/", "state/sensor/weather/", "state/sensor/sun/", "state/sensor/season/", "config/request/"}
		err := client.Connect(c.name, register, subscribe)
		if err == nil {
			logger.LogInfo("emitter", "connected")

			connected := true
			for connected {
				select {
				case msg := <-client.InMsgs:
					topic := msg.Topic()
					if topic == c.cfgch {
						c.config, err = config.FluxConfigFromJSON(msg.Payload())
						if err == nil {
							logger.LogInfo(c.name, "received configuration")
						} else {
							logger.LogError(c.name, err.Error())
						}
					} else if topic == "state/sensor/weather/" {
						c.weather, err = config.SensorStateFromJSON(msg.Payload())
						if err == nil {
							logger.LogInfo(c.name, "received weather state")
						} else {
							logger.LogError(c.name, err.Error())
						}
					} else if topic == "state/sensor/sun/" {
						c.suncalc, err = config.SensorStateFromJSON(msg.Payload())
						if err == nil {
							logger.LogInfo(c.name, "received sun state")
						} else {
							logger.LogError(c.name, err.Error())
						}
					} else if topic == "state/sensor/season/" {
						seasonSensorState, err := config.SensorStateFromJSON(msg.Payload())
						if err == nil {
							logger.LogInfo(c.name, "received season state")
							c.seasonName = seasonSensorState.GetValueAttr("season", "winter")
						} else {
							logger.LogError(c.name, err.Error())
						}
					} else if topic == "client/disconnected/" {
						logger.LogInfo("emitter", "disconnected")
						connected = false
					}
				case <-time.After(time.Second * 10):
					c.Process(client)

				case <-time.After(time.Minute * 1): // Try and request our configuration
					if c.config == nil {
						client.Publish("config/request/", "flux")
					}

				}

			}
		}

		if err != nil {
			logger.LogError(c.name, err.Error())
		}
		time.Sleep(5 * time.Second)
	}
}
<commit_msg>* flux using micro-service<commit_after>package main

import (
	"fmt"
	"math"
	"time"

	"github.com/jurgen-kluft/go-home/config"
	"github.com/jurgen-kluft/go-home/metrics"
	"github.com/jurgen-kluft/go-home/micro-service"
)

// Color Temperature
// URL: https://panasonic.net/es/solution-works/jiyugaoka/

func inTimeSpan(start, end, t time.Time) bool {
	sh, sm, sc := start.Clock()
	sx := float64(sh*60*60) + float64(sm*60) + float64(sc)
	eh, em, ec := end.Clock()
	ex := float64(eh*60*60) + float64(em*60) + float64(ec) + 1.0
	th, tm, tc := t.Clock()
	tx := float64(th*60*60) + float64(tm*60) + float64(tc)
	return tx >= sx && tx < ex
}

// Return the factor 0.0 - 1.0 that indicates where we are in between start - end
func computeTimeSpanX(start, end, t time.Time) float64 {
	sh, sm, sc := start.Clock()
	sx := float64(sh*60*60) + float64(sm*60) + float64(sc)
	eh, em, ec := end.Clock()
	ex := float64(eh*60*60) + float64(em*60) + float64(ec) + 1.0
	th, tm, tc := t.Clock()
	tx := float64(th*60*60) + float64(tm*60) + float64(tc)
	x := (tx - sx) / (ex - sx)
	return x
}

type MovingAverage struct {
	history []float64
	index   int
}

func NewFilter(sizeOfHistory int) *MovingAverage {
	filter := &MovingAverage{history: make([]float64, sizeOfHistory), index: -1}
	return filter
}

func (m *MovingAverage) Sample(sample float64) float64 {
	if m.index == -1 {
		for i := range m.history {
			m.history[i] = sample
		}
		m.index = 0
	}

	m.history[m.index] = sample
	m.index = (m.index + 1) % len(m.history)

	sum := 0.0
	for _, s := range m.history {
		sum += s
	}
	return sum / float64(len(m.history))
}

type context struct {
	name       string
	cfgch      string
	config     *config.FluxConfig
	metrics    *metrics.Metrics
	suncalc    *config.SensorState
	seasonName string
	season     *config.Season
	weather    *config.SensorState
	averageCT  *MovingAverage
	averageBRI *MovingAverage
	service    *microservice.Service
}

func new() *context {
	c := &context{}
	c.name = "flux"
	c.cfgch = "config/flux/"
	c.metrics, _ = metrics.New()
	c.metrics.Register("hue", map[string]string{"CT": "Color Temperature", "BRI": "Brightness"}, map[string]interface{}{"CT": 200.0, "BRI": 200.0})
	c.metrics.Register("yee", map[string]string{"CT": "Color Temperature", "BRI": "Brightness"}, map[string]interface{}{"CT": 200.0, "BRI": 200.0})
	c.seasonName = "spring"
	c.averageCT = NewFilter(30)
	c.averageBRI = NewFilter(30)
	return c
}

func (c *context) updateSeasonFromName(season string) {
	for _, e := range c.config.Seasons {
		if e.Name == season {
			c.season = &config.Season{}
			*c.season = e
		}
	}
}

// Process will update 'string'states and 'float'states
// States are both input and output, for example as input
// there are Season/Weather states like 'Season':'Winter'
// and 'Clouds':0.5
func (c *context) Process() {
	if c.config == nil || c.suncalc == nil {
		return
	}

	now := time.Now()

	// Update our season
	c.updateSeasonFromName(c.seasonName)

	// First build our sun moments map
	sunmoments := map[string]time.Time{}
	for _, tss := range c.suncalc.TimeWndAttrs {
		sunmoments[tss.Name+".begin"] = tss.Begin
		sunmoments[tss.Name+".end"] = tss.End
	}

	// Add our custom time-points to the sun moments map
	for _, at := range c.config.SuncalcMoments {
		moment, exists := sunmoments[at.Name]
		if exists {
			moment = moment.Add(time.Duration(at.Shift) * time.Minute)
			sunmoments[at.Name+at.Tag] = moment
		}
	}

	// Update our Lighttime start and end time from the sun moments map
	for i, lt := range c.config.Lighttime {
		start, exists := sunmoments[lt.TimeSlot.StartMoment]
		if exists {
			end, exists := sunmoments[lt.TimeSlot.EndMoment]
			if exists {
				c.config.Lighttime[i].TimeSlot.StartTime = start
				c.config.Lighttime[i].TimeSlot.EndTime = end
			}
		}
	}

	// Figure out in which light time moment we are now
	current := config.Lighttime{}
	for _, sm := range c.config.Lighttime {
		t0 := sm.TimeSlot.StartTime
		t1 := sm.TimeSlot.EndTime
		if inTimeSpan(t0, t1, now) {
			fmt.Println("Current light time from", sm.TimeSlot.StartMoment, "to", sm.TimeSlot.EndMoment)
			current = sm
			break
		}
	}

	// Time interpolation factor, where are we between startMoment - endMoment
	currentx := computeTimeSpanX(current.TimeSlot.StartTime, current.TimeSlot.EndTime, now)
	currentx = float64(int64(currentx*100.0)) / 100.0

	clouds := config.Weather{Clouds: config.MinMax{Min: 0.0, Max: 0.001}, CTPct: 0.0, BriPct: 0.0}
	cloudFac := float64(0.0)
	if c.weather != nil {
		cloudFac = c.weather.GetFloatAttr("clouds", 0.0)
	}
	for _, w := range c.config.Weather {
		if cloudFac >= w.Clouds.Min && cloudFac < w.Clouds.Max {
			clouds = w
			break
		}
	}

	// Full cloud cover will increase color-temperature by 10% of (Max - Current)
	// NOTE: Only during the day (twilight + light)
	// TODO: when the moon is shining in the night the amount
	//       of blue-light is also higher than normal.
	// CT = 0.0 -> Coldest (>6500K)
	// CT = 1.0 -> Warmest (2000K)
	CT := current.CT.LinearInterpolated(currentx)
	if current.Darkorlight != "dark" {
		if clouds.CTPct >= 0 {
			CT = CT + clouds.CTPct*(1.0-CT)
		} else {
			CT = CT + clouds.CTPct*CT
		}
	}
	CT = c.season.CT.LinearInterpolated(CT)
	CT = c.averageCT.Sample(CT)

	// Full cloud cover will increase brightness by 10% of (Max - Current)
	// BRI = 0 -> Very dim light
	// BRI = 1 -> Very bright light
	BRI := current.Bri.LinearInterpolated(currentx)
	BRI = BRI + cloudFac*0.1*(1.0-BRI)
	if current.Darkorlight != "dark" {
		// A bit brighter lights when there are clouds during the day.
		if clouds.BriPct >= 0 {
			BRI = BRI + clouds.BriPct*(1.0-BRI)
		} else {
			BRI = BRI + clouds.BriPct*BRI
		}
	}
	BRI = c.season.BRI.LinearInterpolated(BRI)
	BRI = c.averageBRI.Sample(BRI)

	// Publishing the following sensors:
	//  - Sensor.Light.HUE, Name = CT, Value = float64(100.0)
	//  - Sensor.Light.HUE, Name = BRI, Value = float64(100.0)
	//  - Sensor.Light.YEE, Name = CT, Value = float64(100.0)
	//  - Sensor.Light.YEE, Name = BRI, Value = float64(100.0)
	//  - Sensor.Light.DarkOrLight = string(Dark)

	for _, ltype := range c.config.Lighttype {
		sensor := config.NewSensorState("all")

		c.metrics.Begin(ltype.Name)

		lct := ltype.CT.LinearInterpolated(CT)
		sensor.AddFloatAttr("CT", math.Floor(lct))
		c.metrics.Set(ltype.Name, "CT", lct)

		lbri := ltype.BRI.LinearInterpolated(BRI)
		sensor.AddFloatAttr("BRI", math.Floor(lbri))
		c.metrics.Set(ltype.Name, "BRI", lbri)

		c.metrics.Send(ltype.Name)

		jsonstr, err := sensor.ToJSON()
		if err == nil {
			c.publishSensor(fmt.Sprintf("state/sensor/%s/", ltype.Name), jsonstr)
		}
	}

	sensorDOL, err := config.StringAttrAsJSON("darkorlight", "DarkOrLight", string(current.Darkorlight))
	if err == nil {
		c.publishSensor("state/sensor/darkorlight/", sensorDOL)
	}
}

func (c *context) publishSensor(channel string, sensorjson string) {
	c.service.Logger.LogInfo(c.service.Name, "Publish at '"+channel+"' JSON ["+sensorjson+"]")
	c.service.Pubsub.Publish(channel, sensorjson)
}

func main() {
	register := []string{"config/flux/", "state/sensor/weather/", "state/sensor/sun/", "state/sensor/season/", "state/light/hue/", "state/light/yee/"}
	subscribe := []string{"config/flux/", "state/sensor/weather/", "state/sensor/sun/", "state/sensor/season/", "config/request/"}

	m := microservice.New("flux")
	m.RegisterAndSubscribe(register, subscribe)

	c := new()
	c.service = m

	m.RegisterHandler("config/flux/", func(m *microservice.Service, topic string, msg []byte) bool {
		var err error
		c.config, err = config.FluxConfigFromJSON(msg)
		if err == nil {
			m.Logger.LogInfo(m.Name, "received configuration")
		} else {
			m.Logger.LogError(m.Name, err.Error())
		}
		return true
	})

	m.RegisterHandler("state/sensor/weather/", func(m *microservice.Service, topic string, msg []byte) bool {
		var err error
		c.weather, err = config.SensorStateFromJSON(msg)
		if err == nil {
			m.Logger.LogInfo(c.name, "received weather state")
		} else {
			m.Logger.LogError(c.name, err.Error())
		}
		return true
	})

	m.RegisterHandler("state/sensor/sun/", func(m *microservice.Service, topic string, msg []byte) bool {
		var err error
		c.suncalc, err = config.SensorStateFromJSON(msg)
		if err == nil {
			m.Logger.LogInfo(c.name, "received sun state")
		} else {
			m.Logger.LogError(c.name, err.Error())
		}
		return true
	})

	m.RegisterHandler("state/sensor/season/", func(m *microservice.Service, topic string, msg []byte) bool {
		seasonSensorState, err := config.SensorStateFromJSON(msg)
		if err == nil {
			m.Logger.LogInfo(c.name, "received season state")
			c.seasonName = seasonSensorState.GetValueAttr("season", "winter")
		} else {
			m.Logger.LogError(c.name, err.Error())
		}
		return true
	})

	tickCount := 0
	m.RegisterHandler("tick/", func(m *microservice.Service, topic string, msg []byte) bool {
		if (tickCount % 5) == 0 {
			c.Process()
		} else if (tickCount % 30) == 0 {
			if c.config == nil {
				m.Pubsub.Publish("config/request/", m.Name)
			}
		}
		tickCount++
		return true
	})

	m.Loop()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.64"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.31", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<commit_msg>fnlb: 0.0.65 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.65"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.32", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"log"
	"os"

	_ "github.com/mattn/go-sqlite3"
)

type Store struct {
	db *sql.DB
}

func (s *Store) Open(database string) {
	db, err := sql.Open("sqlite3", database)
	if err != nil {
		log.Fatal(err)
	}
	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			s.createDatabase()
		}
	}
}

func (s *Store) Close() {
	s.db.Close()
}

func (s *Store) createDatabase() {

	// TODO: create all tables in one variable when multiple statements will
	// be supported see https://github.com/mattn/go-sqlite3/issues/60
	sql_people_table := `CREATE TABLE people (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	name TEXT
);
	`
	sql_quotes_table := `CREATE TABLE quotes (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	person_id INTEGER NOT NULL,
	quote TEXT,
	FOREIGN KEY(person_id) REFERENCES people(id)
);
	`
	s.db.Exec(sql_people_table)
	s.db.Exec(sql_quotes_table)
}

func (s *Store) GetQuote(person string) (quote string) {
	var q string
	query := `SELECT quote FROM quotes WHERE person_id =
		(SELECT id FROM people WHERE name = ?) ORDER BY RANDOM() LIMIT 1;`
	s.db.QueryRow(query, person).Scan(&q)
	return q
}
<commit_msg>Added alias table<commit_after>package db

import (
	"database/sql"
	"log"
	"os"

	_ "github.com/mattn/go-sqlite3"
)

type Store struct {
	db *sql.DB
}

func (s *Store) Open(database string) {
	db, err := sql.Open("sqlite3", database)
	if err != nil {
		log.Fatal(err)
	}
	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			s.createDatabase()
		}
	}
}

func (s *Store) Close() {
	s.db.Close()
}

func (s *Store) createDatabase() {

	// TODO: create all tables in one variable when multiple statements will
	// be supported see https://github.com/mattn/go-sqlite3/issues/60
	sql_people_table := `CREATE TABLE people (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	name TEXT
);`

	sql_quotes_table := `CREATE TABLE quotes (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	person_id INTEGER NOT NULL,
	quote TEXT,
	FOREIGN KEY(person_id) REFERENCES people(id)
);`

	sql_alias_table := `CREATE TABLE alias (
	person_id INTEGER NOT NULL,
	alias TEXT,
	FOREIGN KEY(person_id) REFERENCES people(id)
);`

	s.db.Exec(sql_people_table)
	s.db.Exec(sql_quotes_table)
	s.db.Exec(sql_alias_table)
}

func (s *Store) getPerson(name string) (id int) {
	query_people := `SELECT id FROM people WHERE name = ?`
	query_alias := `SELECT person_id FROM alias WHERE alias = ?`

	s.db.QueryRow(query_people, name).Scan(&id)
	if id == 0 {
		s.db.QueryRow(query_alias, name).Scan(&id)
	}

	return
}

func (s *Store) GetQuote(person string) (quote string) {
	var q string
	query := `SELECT quote FROM quotes WHERE person_id = ?
		ORDER BY RANDOM() LIMIT 1;`
	s.db.QueryRow(query, s.getPerson(person)).Scan(&q)
	return q
}
<|endoftext|>


<commit_before>// Copyright 2011 The Snappy-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snappy

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

var (
	// ErrCorrupt reports that the input is invalid.
	ErrCorrupt = errors.New("snappy: corrupt input")
	// ErrUnsupported reports that the input isn't supported.
	ErrUnsupported = errors.New("snappy: unsupported input")
)

// DecodedLen returns the length of the decoded block.
func DecodedLen(src []byte) (int, error) {
	v, _, err := decodedLen(src)
	return v, err
}

// decodedLen returns the length of the decoded block and the number of bytes
// that the length header occupied.
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
	v, n := binary.Uvarint(src)
	if n <= 0 {
		return 0, 0, ErrCorrupt
	}
	if uint64(int(v)) != v {
		return 0, 0, errors.New("snappy: decoded block is too large")
	}
	return int(v), n, nil
}

// Decode returns the decoded form of src. The returned slice may be a sub-
// slice of dst if dst was large enough to hold the entire decoded block.
// Otherwise, a newly allocated slice will be returned.
// It is valid to pass a nil dst.
func Decode(dst, src []byte) ([]byte, error) {
	dLen, s, err := decodedLen(src)
	if err != nil {
		return nil, err
	}
	if len(dst) < dLen {
		dst = make([]byte, dLen)
	}

	var d, offset, length int
	for s < len(src) {
		switch src[s] & 0x03 {
		case tagLiteral:
			x := uint(src[s] >> 2)
			switch {
			case x < 60:
				s += 1
			case x == 60:
				s += 2
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-1])
			case x == 61:
				s += 3
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-2]) | uint(src[s-1])<<8
			case x == 62:
				s += 4
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
			case x == 63:
				s += 5
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
			}
			length = int(x + 1)
			if length <= 0 {
				return nil, errors.New("snappy: unsupported literal length")
			}
			if length > len(dst)-d || length > len(src)-s {
				return nil, ErrCorrupt
			}
			copy(dst[d:], src[s:s+length])
			d += length
			s += length
			continue

		case tagCopy1:
			s += 2
			if s > len(src) {
				return nil, ErrCorrupt
			}
			length = 4 + int(src[s-2])>>2&0x7
			offset = int(src[s-2])&0xe0<<3 | int(src[s-1])

		case tagCopy2:
			s += 3
			if s > len(src) {
				return nil, ErrCorrupt
			}
			length = 1 + int(src[s-3])>>2
			offset = int(src[s-2]) | int(src[s-1])<<8

		case tagCopy4:
			return nil, errors.New("snappy: unsupported COPY_4 tag")
		}

		end := d + length
		if offset > d || end > len(dst) {
			return nil, ErrCorrupt
		}
		for ; d < end; d++ {
			dst[d] = dst[d-offset]
		}
	}
	if d != dLen {
		return nil, ErrCorrupt
	}
	return dst[:d], nil
}

// NewReader returns a new Reader that decompresses from r, using the framing
// format described at
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
func NewReader(r io.Reader) *Reader {
	return &Reader{
		r:       r,
		decoded: make([]byte, maxUncompressedChunkLen),
		buf:     make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
	}
}

// Reader is an io.Reader than can read Snappy-compressed bytes.
type Reader struct {
	r       io.Reader
	err     error
	decoded []byte
	buf     []byte
	// decoded[i:j] contains decoded bytes that have not yet been passed on.
	i, j       int
	readHeader bool
}

// Reset discards any buffered data, resets all state, and switches the Snappy
// reader to read from r. This permits reusing a Reader rather than allocating
// a new one.
func (r *Reader) Reset(reader io.Reader) {
	r.r = reader
	r.err = nil
	r.i = 0
	r.j = 0
	r.readHeader = false
}

func (r *Reader) readFull(p []byte) (ok bool) {
	if _, r.err = io.ReadFull(r.r, p); r.err != nil {
		if r.err == io.ErrUnexpectedEOF {
			r.err = ErrCorrupt
		}
		return false
	}
	return true
}

// Read satisfies the io.Reader interface.
func (r *Reader) Read(p []byte) (int, error) {
	if r.err != nil {
		return 0, r.err
	}
	for {
		if r.i < r.j {
			n := copy(p, r.decoded[r.i:r.j])
			r.i += n
			return n, nil
		}
		if !r.readFull(r.buf[:4]) {
			return 0, r.err
		}
		chunkType := r.buf[0]
		if !r.readHeader {
			if chunkType != chunkTypeStreamIdentifier {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.readHeader = true
		}
		chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
		if chunkLen > len(r.buf) {
			r.err = ErrUnsupported
			return 0, r.err
		}

		// The chunk types are specified at
		// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
		switch chunkType {
		case chunkTypeCompressedData:
			// Section 4.2. Compressed data (chunk type 0x00).
			if chunkLen < checksumSize {
				r.err = ErrCorrupt
				return 0, r.err
			}
			buf := r.buf[:chunkLen]
			if !r.readFull(buf) {
				return 0, r.err
			}
			checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
			buf = buf[checksumSize:]

			n, err := DecodedLen(buf)
			if err != nil {
				r.err = err
				return 0, r.err
			}
			if n > len(r.decoded) {
				r.err = ErrCorrupt
				return 0, r.err
			}
			if _, err := Decode(r.decoded, buf); err != nil {
				r.err = err
				return 0, r.err
			}
			if crc(r.decoded[:n]) != checksum {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.i, r.j = 0, n
			continue

		case chunkTypeUncompressedData:
			// Section 4.3. Uncompressed data (chunk type 0x01).
			if chunkLen < checksumSize {
				r.err = ErrCorrupt
				return 0, r.err
			}
			buf := r.buf[:checksumSize]
			if !r.readFull(buf) {
				return 0, r.err
			}
			checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
			// Read directly into r.decoded instead of via r.buf.
			n := chunkLen - checksumSize
			if !r.readFull(r.decoded[:n]) {
				return 0, r.err
			}
			if crc(r.decoded[:n]) != checksum {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.i, r.j = 0, n
			continue

		case chunkTypeStreamIdentifier:
			// Section 4.1. Stream identifier (chunk type 0xff).
			if chunkLen != len(magicBody) {
				r.err = ErrCorrupt
				return 0, r.err
			}
			if !r.readFull(r.buf[:len(magicBody)]) {
				return 0, r.err
			}
			for i := 0; i < len(magicBody); i++ {
				if r.buf[i] != magicBody[i] {
					r.err = ErrCorrupt
					return 0, r.err
				}
			}
			continue
		}

		if chunkType <= 0x7f {
			// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
			r.err = ErrUnsupported
			return 0, r.err

		} else {
			// Section 4.4 Padding (chunk type 0xfe).
			// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
			if !r.readFull(r.buf[:chunkLen]) {
				return 0, r.err
			}
		}
	}
}
<commit_msg>Lint fixes.<commit_after>// Copyright 2011 The Snappy-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snappy

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

var (
	// ErrCorrupt reports that the input is invalid.
	ErrCorrupt = errors.New("snappy: corrupt input")
	// ErrUnsupported reports that the input isn't supported.
	ErrUnsupported = errors.New("snappy: unsupported input")
)

// DecodedLen returns the length of the decoded block.
func DecodedLen(src []byte) (int, error) {
	v, _, err := decodedLen(src)
	return v, err
}

// decodedLen returns the length of the decoded block and the number of bytes
// that the length header occupied.
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
	v, n := binary.Uvarint(src)
	if n <= 0 {
		return 0, 0, ErrCorrupt
	}
	if uint64(int(v)) != v {
		return 0, 0, errors.New("snappy: decoded block is too large")
	}
	return int(v), n, nil
}

// Decode returns the decoded form of src. The returned slice may be a sub-
// slice of dst if dst was large enough to hold the entire decoded block.
// Otherwise, a newly allocated slice will be returned.
// It is valid to pass a nil dst.
func Decode(dst, src []byte) ([]byte, error) {
	dLen, s, err := decodedLen(src)
	if err != nil {
		return nil, err
	}
	if len(dst) < dLen {
		dst = make([]byte, dLen)
	}

	var d, offset, length int
	for s < len(src) {
		switch src[s] & 0x03 {
		case tagLiteral:
			x := uint(src[s] >> 2)
			switch {
			case x < 60:
				s++
			case x == 60:
				s += 2
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-1])
			case x == 61:
				s += 3
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-2]) | uint(src[s-1])<<8
			case x == 62:
				s += 4
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16
			case x == 63:
				s += 5
				if s > len(src) {
					return nil, ErrCorrupt
				}
				x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24
			}
			length = int(x + 1)
			if length <= 0 {
				return nil, errors.New("snappy: unsupported literal length")
			}
			if length > len(dst)-d || length > len(src)-s {
				return nil, ErrCorrupt
			}
			copy(dst[d:], src[s:s+length])
			d += length
			s += length
			continue

		case tagCopy1:
			s += 2
			if s > len(src) {
				return nil, ErrCorrupt
			}
			length = 4 + int(src[s-2])>>2&0x7
			offset = int(src[s-2])&0xe0<<3 | int(src[s-1])

		case tagCopy2:
			s += 3
			if s > len(src) {
				return nil, ErrCorrupt
			}
			length = 1 + int(src[s-3])>>2
			offset = int(src[s-2]) | int(src[s-1])<<8

		case tagCopy4:
			return nil, errors.New("snappy: unsupported COPY_4 tag")
		}

		end := d + length
		if offset > d || end > len(dst) {
			return nil, ErrCorrupt
		}
		for ; d < end; d++ {
			dst[d] = dst[d-offset]
		}
	}
	if d != dLen {
		return nil, ErrCorrupt
	}
	return dst[:d], nil
}

// NewReader returns a new Reader that decompresses from r, using the framing
// format described at
// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
func NewReader(r io.Reader) *Reader {
	return &Reader{
		r:       r,
		decoded: make([]byte, maxUncompressedChunkLen),
		buf:     make([]byte, MaxEncodedLen(maxUncompressedChunkLen)+checksumSize),
	}
}

// Reader is an io.Reader than can read Snappy-compressed bytes.
type Reader struct {
	r       io.Reader
	err     error
	decoded []byte
	buf     []byte
	// decoded[i:j] contains decoded bytes that have not yet been passed on.
	i, j       int
	readHeader bool
}

// Reset discards any buffered data, resets all state, and switches the Snappy
// reader to read from r. This permits reusing a Reader rather than allocating
// a new one.
func (r *Reader) Reset(reader io.Reader) {
	r.r = reader
	r.err = nil
	r.i = 0
	r.j = 0
	r.readHeader = false
}

func (r *Reader) readFull(p []byte) (ok bool) {
	if _, r.err = io.ReadFull(r.r, p); r.err != nil {
		if r.err == io.ErrUnexpectedEOF {
			r.err = ErrCorrupt
		}
		return false
	}
	return true
}

// Read satisfies the io.Reader interface.
func (r *Reader) Read(p []byte) (int, error) {
	if r.err != nil {
		return 0, r.err
	}
	for {
		if r.i < r.j {
			n := copy(p, r.decoded[r.i:r.j])
			r.i += n
			return n, nil
		}
		if !r.readFull(r.buf[:4]) {
			return 0, r.err
		}
		chunkType := r.buf[0]
		if !r.readHeader {
			if chunkType != chunkTypeStreamIdentifier {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.readHeader = true
		}
		chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
		if chunkLen > len(r.buf) {
			r.err = ErrUnsupported
			return 0, r.err
		}

		// The chunk types are specified at
		// https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
		switch chunkType {
		case chunkTypeCompressedData:
			// Section 4.2. Compressed data (chunk type 0x00).
			if chunkLen < checksumSize {
				r.err = ErrCorrupt
				return 0, r.err
			}
			buf := r.buf[:chunkLen]
			if !r.readFull(buf) {
				return 0, r.err
			}
			checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
			buf = buf[checksumSize:]

			n, err := DecodedLen(buf)
			if err != nil {
				r.err = err
				return 0, r.err
			}
			if n > len(r.decoded) {
				r.err = ErrCorrupt
				return 0, r.err
			}
			if _, err := Decode(r.decoded, buf); err != nil {
				r.err = err
				return 0, r.err
			}
			if crc(r.decoded[:n]) != checksum {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.i, r.j = 0, n
			continue

		case chunkTypeUncompressedData:
			// Section 4.3. Uncompressed data (chunk type 0x01).
			if chunkLen < checksumSize {
				r.err = ErrCorrupt
				return 0, r.err
			}
			buf := r.buf[:checksumSize]
			if !r.readFull(buf) {
				return 0, r.err
			}
			checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
			// Read directly into r.decoded instead of via r.buf.
			n := chunkLen - checksumSize
			if !r.readFull(r.decoded[:n]) {
				return 0, r.err
			}
			if crc(r.decoded[:n]) != checksum {
				r.err = ErrCorrupt
				return 0, r.err
			}
			r.i, r.j = 0, n
			continue

		case chunkTypeStreamIdentifier:
			// Section 4.1. Stream identifier (chunk type 0xff).
			if chunkLen != len(magicBody) {
				r.err = ErrCorrupt
				return 0, r.err
			}
			if !r.readFull(r.buf[:len(magicBody)]) {
				return 0, r.err
			}
			for i := 0; i < len(magicBody); i++ {
				if r.buf[i] != magicBody[i] {
					r.err = ErrCorrupt
					return 0, r.err
				}
			}
			continue
		}

		if chunkType <= 0x7f {
			// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
			r.err = ErrUnsupported
			return 0, r.err
		}
		// Section 4.4 Padding (chunk type 0xfe).
		// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
		if !r.readFull(r.buf[:chunkLen]) {
			return 0, r.err
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Alvaro J. Genial. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package form

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"reflect"
	"strconv"
	"time"
)

// NewDecoder returns a new form decoder.
func NewDecoder(r io.Reader) *decoder {
	return &decoder{r}
}

// decoder decodes data from a form (application/x-www-form-urlencoded).
type decoder struct {
	r io.Reader
}

// Decode reads in and decodes form-encoded data into dst.
func (d decoder) Decode(dst interface{}) error {
	bs, err := ioutil.ReadAll(d.r)
	if err != nil {
		return err
	}
	vs, err := url.ParseQuery(string(bs))
	if err != nil {
		return err
	}
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

// DecodeString decodes src into dst.
func DecodeString(dst interface{}, src string) error {
	vs, err := url.ParseQuery(src)
	if err != nil {
		return err
	}
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

// DecodeValues decodes vs into dst.
func DecodeValues(dst interface{}, vs url.Values) error {
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

func decodeNode(v reflect.Value, n node) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	if v.Kind() == reflect.Slice {
		return fmt.Errorf("could not decode directly into slice; use pointer to slice")
	}
	decodeValue(v, n)
	return nil
}

func decodeValue(v reflect.Value, x interface{}) {
	t := v.Type()
	k := v.Kind()

	if k == reflect.Ptr && v.IsNil() {
		v.Set(reflect.New(t.Elem()))
	}

	if unmarshalValue(v, x) {
		return
	}

	empty := isEmpty(x)

	switch k {
	case reflect.Ptr:
		decodeValue(v.Elem(), x)
		return
	case reflect.Interface:
		if !v.IsNil() {
			decodeValue(v.Elem(), x)
			return

		} else if empty {
			return // Allow nil interfaces only if empty.
		} else {
			panic("form: cannot decode non-empty value into into nil interface")
		}
	}

	if empty {
		v.Set(reflect.Zero(t)) // Treat the empty string as the zero value.
		return
	}

	switch k {
	case reflect.Struct:
		if t.ConvertibleTo(timeType) {
			decodeTime(v, x)
		} else if t.ConvertibleTo(urlType) {
			decodeURL(v, x)
		} else {
			decodeStruct(v, x)
		}
	case reflect.Slice:
		decodeSlice(v, x)
	case reflect.Array:
		decodeArray(v, x)
	case reflect.Map:
		decodeMap(v, x)
	case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func:
		panic(t.String() + " has unsupported kind " + k.String())
	default:
		decodeBasic(v, x)
	}
}

func decodeStruct(v reflect.Value, x interface{}) {
	t := v.Type()
	for k, c := range getNode(x) {
		if f, ok := findField(v, k); !ok && k == "" {
			panic(getString(x) + " cannot be decoded as struct " + t.String())
		} else if !ok {
			panic(k + " doesn't exist in struct " + t.String())
		} else if !f.CanSet() {
			panic(k + " cannot be set in struct " + t.String())
		} else {
			decodeValue(f, c)
		}
	}
}

func decodeMap(v reflect.Value, x interface{}) {
	t := v.Type()
	if v.IsNil() {
		v.Set(reflect.MakeMap(t))
	}
	for k, c := range getNode(x) {
		i := reflect.New(t.Key()).Elem()
		decodeValue(i, k)

		w := v.MapIndex(i)
		if w.IsValid() { // We have an actual element value to decode into.
			if w.Kind() == reflect.Interface {
				w = w.Elem()
			}
			w = reflect.New(w.Type()).Elem()
		} else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete.
			w = reflect.New(t.Elem()).Elem()
		} else {
			// The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest).
			// We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast,
			// especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.)
			switch c.(type) {
			case node:
				w = reflect.MakeMap(stringMapType)
			case string:
				w = reflect.New(stringType).Elem()
			default:
				panic("value is neither node nor string")
			}
		}

		decodeValue(w, c)
		v.SetMapIndex(i, w)
	}
}

func decodeArray(v reflect.Value, x interface{}) {
	t := v.Type()
	for k, c := range getNode(x) {
		i, err := strconv.Atoi(k)
		if err != nil {
			panic(k + " is not a valid index for type " + t.String())
		}
		if l := v.Len(); i >= l {
			panic("index is above array size")
		}
		decodeValue(v.Index(i), c)
	}
}

func decodeSlice(v reflect.Value, x interface{}) {
	t := v.Type()
	if t.Elem().Kind() == reflect.Uint8 {
		// Allow, but don't require, byte slices to be encoded as a single string.
		if s, ok := x.(string); ok {
			v.SetBytes([]byte(s))
			return
		}
	}

	// NOTE: Implicit indexing is currently done at the parseValues level,
	//       so if if an implicitKey reaches here it will always replace the last.
	implicit := 0
	for k, c := range getNode(x) {
		var i int
		if k == implicitKey {
			i = implicit
			implicit++
		} else {
			explicit, err := strconv.Atoi(k)
			if err != nil {
				panic(k + " is not a valid index for type " + t.String())
			}
			i = explicit
			implicit = explicit + 1
		}
		// "Extend" the slice if it's too short.
		if l := v.Len(); i >= l {
			delta := i - l + 1
			v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta)))
		}
		decodeValue(v.Index(i), c)
	}
}

func decodeBasic(v reflect.Value, x interface{}) {
	t := v.Type()
	switch k, s := t.Kind(), getString(x); k {
	case reflect.Bool:
		if b, e := strconv.ParseBool(s); e == nil {
			v.SetBool(b)
		} else {
			panic("could not parse bool from " + strconv.Quote(s))
		}
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64:
		if i, e := strconv.ParseInt(s, 10, 64); e == nil {
			v.SetInt(i)
		} else {
			panic("could not parse int from " + strconv.Quote(s))
		}
	case reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64:
		if u, e := strconv.ParseUint(s, 10, 64); e == nil {
			v.SetUint(u)
		} else {
			panic("could not parse uint from " + strconv.Quote(s))
		}
	case reflect.Float32,
		reflect.Float64:
		if f, e := strconv.ParseFloat(s, 64); e == nil {
			v.SetFloat(f)
		} else {
			panic("could not parse float from " + strconv.Quote(s))
		}
	case reflect.Complex64,
		reflect.Complex128:
		var c complex128
		if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil {
			v.SetComplex(c)
		} else {
			panic("could not parse complex from " + strconv.Quote(s))
		}
	case reflect.String:
		v.SetString(s)
	default:
		panic(t.String() + " has unsupported kind " + k.String())
	}
}

func decodeTime(v reflect.Value, x interface{}) {
	t := v.Type()
	s := getString(x)
	// TODO: Find a more efficient way to do this.
	for _, f := range allowedTimeFormats {
		if p, err := time.Parse(f, s); err == nil {
			v.Set(reflect.ValueOf(p).Convert(v.Type()))
			return
		}
	}
	panic("cannot decode string " + strconv.Quote(s) + " as " + t.String())
}

func decodeURL(v reflect.Value, x interface{}) {
	t := v.Type()
	s := getString(x)
	if u, err := url.Parse(s); err == nil {
		v.Set(reflect.ValueOf(*u).Convert(v.Type()))
		return
	}
	panic("cannot decode string `" + s + "` as " + t.String())
}

var allowedTimeFormats = []string{
	"2006-01-02T15:04:05.999999999Z07:00",
	"2006-01-02T15:04:05.999999999Z07",
	"2006-01-02T15:04:05.999999999Z",
	"2006-01-02T15:04:05.999999999",
	"2006-01-02T15:04:05Z07:00",
	"2006-01-02T15:04:05Z07",
	"2006-01-02T15:04:05Z",
	"2006-01-02T15:04:05",
	"2006-01-02T15:04Z",
	"2006-01-02T15:04",
	"2006-01-02T15Z",
	"2006-01-02T15",
	"2006-01-02",
	"2006-01",
	"2006",
	"15:04:05.999999999Z07:00",
	"15:04:05.999999999Z07",
	"15:04:05.999999999Z",
	"15:04:05.999999999",
	"15:04:05Z07:00",
	"15:04:05Z07",
	"15:04:05Z",
	"15:04:05",
	"15:04Z",
	"15:04",
	"15Z",
	"15",
}
<commit_msg>Improve error messages<commit_after>// Copyright 2014 Alvaro J. Genial. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package form

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"reflect"
	"strconv"
	"time"
)

// NewDecoder returns a new form decoder.
func NewDecoder(r io.Reader) *decoder {
	return &decoder{r}
}

// decoder decodes data from a form (application/x-www-form-urlencoded).
type decoder struct {
	r io.Reader
}

// Decode reads in and decodes form-encoded data into dst.
func (d decoder) Decode(dst interface{}) error {
	bs, err := ioutil.ReadAll(d.r)
	if err != nil {
		return err
	}
	vs, err := url.ParseQuery(string(bs))
	if err != nil {
		return err
	}
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

// DecodeString decodes src into dst.
func DecodeString(dst interface{}, src string) error {
	vs, err := url.ParseQuery(src)
	if err != nil {
		return err
	}
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

// DecodeValues decodes vs into dst.
func DecodeValues(dst interface{}, vs url.Values) error {
	v := reflect.ValueOf(dst)
	return decodeNode(v, parseValues(vs, canIndexOrdinally(v)))
}

func decodeNode(v reflect.Value, n node) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	if v.Kind() == reflect.Slice {
		return fmt.Errorf("could not decode directly into slice; use pointer to slice")
	}
	decodeValue(v, n)
	return nil
}

func decodeValue(v reflect.Value, x interface{}) {
	t := v.Type()
	k := v.Kind()

	if k == reflect.Ptr && v.IsNil() {
		v.Set(reflect.New(t.Elem()))
	}

	if unmarshalValue(v, x) {
		return
	}

	empty := isEmpty(x)

	switch k {
	case reflect.Ptr:
		decodeValue(v.Elem(), x)
		return
	case reflect.Interface:
		if !v.IsNil() {
			decodeValue(v.Elem(), x)
			return

		} else if empty {
			return // Allow nil interfaces only if empty.
		} else {
			panic("form: cannot decode non-empty value into into nil interface")
		}
	}

	if empty {
		v.Set(reflect.Zero(t)) // Treat the empty string as the zero value.
		return
	}

	switch k {
	case reflect.Struct:
		if t.ConvertibleTo(timeType) {
			decodeTime(v, x)
		} else if t.ConvertibleTo(urlType) {
			decodeURL(v, x)
		} else {
			decodeStruct(v, x)
		}
	case reflect.Slice:
		decodeSlice(v, x)
	case reflect.Array:
		decodeArray(v, x)
	case reflect.Map:
		decodeMap(v, x)
	case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func:
		panic(t.String() + " has unsupported kind " + k.String())
	default:
		decodeBasic(v, x)
	}
}

func decodeStruct(v reflect.Value, x interface{}) {
	t := v.Type()
	for k, c := range getNode(x) {
		if f, ok := findField(v, k); !ok && k == "" {
			panic(getString(x) + " cannot be decoded as " + t.String())
		} else if !ok {
			panic(k + " doesn't exist in " + t.String())
		} else if !f.CanSet() {
			panic(k + " cannot be set in " + t.String())
		} else {
			decodeValue(f, c)
		}
	}
}

func decodeMap(v reflect.Value, x interface{}) {
	t := v.Type()
	if v.IsNil() {
		v.Set(reflect.MakeMap(t))
	}
	for k, c := range getNode(x) {
		i := reflect.New(t.Key()).Elem()
		decodeValue(i, k)

		w := v.MapIndex(i)
		if w.IsValid() { // We have an actual element value to decode into.
			if w.Kind() == reflect.Interface {
				w = w.Elem()
			}
			w = reflect.New(w.Type()).Elem()
		} else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete.
			w = reflect.New(t.Elem()).Elem()
		} else {
			// The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest).
			// We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast,
			// especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.)
			switch c.(type) {
			case node:
				w = reflect.MakeMap(stringMapType)
			case string:
				w = reflect.New(stringType).Elem()
			default:
				panic("value is neither node nor string")
			}
		}

		decodeValue(w, c)
		v.SetMapIndex(i, w)
	}
}

func decodeArray(v reflect.Value, x interface{}) {
	t := v.Type()
	for k, c := range getNode(x) {
		i, err := strconv.Atoi(k)
		if err != nil {
			panic(k + " is not a valid index for type " + t.String())
		}
		if l := v.Len(); i >= l {
			panic("index is above array size")
		}
		decodeValue(v.Index(i), c)
	}
}

func decodeSlice(v reflect.Value, x interface{}) {
	t := v.Type()
	if t.Elem().Kind() == reflect.Uint8 {
		// Allow, but don't require, byte slices to be encoded as a single string.
		if s, ok := x.(string); ok {
			v.SetBytes([]byte(s))
			return
		}
	}

	// NOTE: Implicit indexing is currently done at the parseValues level,
	//       so if if an implicitKey reaches here it will always replace the last.
	implicit := 0
	for k, c := range getNode(x) {
		var i int
		if k == implicitKey {
			i = implicit
			implicit++
		} else {
			explicit, err := strconv.Atoi(k)
			if err != nil {
				panic(k + " is not a valid index for type " + t.String())
			}
			i = explicit
			implicit = explicit + 1
		}
		// "Extend" the slice if it's too short.
		if l := v.Len(); i >= l {
			delta := i - l + 1
			v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta)))
		}
		decodeValue(v.Index(i), c)
	}
}

func decodeBasic(v reflect.Value, x interface{}) {
	t := v.Type()
	switch k, s := t.Kind(), getString(x); k {
	case reflect.Bool:
		if b, e := strconv.ParseBool(s); e == nil {
			v.SetBool(b)
		} else {
			panic("could not parse bool from " + strconv.Quote(s))
		}
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64:
		if i, e := strconv.ParseInt(s, 10, 64); e == nil {
			v.SetInt(i)
		} else {
			panic("could not parse int from " + strconv.Quote(s))
		}
	case reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64:
		if u, e := strconv.ParseUint(s, 10, 64); e == nil {
			v.SetUint(u)
		} else {
			panic("could not parse uint from " + strconv.Quote(s))
		}
	case reflect.Float32,
		reflect.Float64:
		if f, e := strconv.ParseFloat(s, 64); e == nil {
			v.SetFloat(f)
		} else {
			panic("could not parse float from " + strconv.Quote(s))
		}
	case reflect.Complex64,
		reflect.Complex128:
		var c complex128
		if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil {
			v.SetComplex(c)
		} else {
			panic("could not parse complex from " + strconv.Quote(s))
		}
	case reflect.String:
		v.SetString(s)
	default:
		panic(t.String() + " has unsupported kind " + k.String())
	}
}

func decodeTime(v reflect.Value, x interface{}) {
	t := v.Type()
	s := getString(x)
	// TODO: Find a more efficient way to do this.
	for _, f := range allowedTimeFormats {
		if p, err := time.Parse(f, s); err == nil {
			v.Set(reflect.ValueOf(p).Convert(v.Type()))
			return
		}
	}
	panic("cannot decode string `" + s + "` as " + t.String())
}

func decodeURL(v reflect.Value, x interface{}) {
	t := v.Type()
	s := getString(x)
	if u, err := url.Parse(s); err == nil {
		v.Set(reflect.ValueOf(*u).Convert(v.Type()))
		return
	}
	panic("cannot decode string `" + s + "` as " + t.String())
}

var allowedTimeFormats = []string{
	"2006-01-02T15:04:05.999999999Z07:00",
	"2006-01-02T15:04:05.999999999Z07",
	"2006-01-02T15:04:05.999999999Z",
	"2006-01-02T15:04:05.999999999",
	"2006-01-02T15:04:05Z07:00",
	"2006-01-02T15:04:05Z07",
	"2006-01-02T15:04:05Z",
	"2006-01-02T15:04:05",
	"2006-01-02T15:04Z",
	"2006-01-02T15:04",
	"2006-01-02T15Z",
	"2006-01-02T15",
	"2006-01-02",
	"2006-01",
	"2006",
	"15:04:05.999999999Z07:00",
	"15:04:05.999999999Z07",
	"15:04:05.999999999Z",
	"15:04:05.999999999",
	"15:04:05Z07:00",
	"15:04:05Z07",
	"15:04:05Z",
	"15:04:05",
	"15:04Z",
	"15:04",
	"15Z",
	"15",
}
<|endoftext|>