<commit_before>// Contains functions that don't really belong anywhere else.

package google

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
	"google.golang.org/api/googleapi"
)

type TerraformResourceData interface {
	HasChange(string) bool
	GetOkExists(string) (interface{}, bool)
	GetOk(string) (interface{}, bool)
	Get(string) interface{}
	Set(string, interface{}) error
	SetId(string)
	Id() string
	GetProviderMeta(interface{}) error
}

type TerraformResourceDiff interface {
	HasChange(string) bool
	GetChange(string) (interface{}, interface{})
	Get(string) interface{}
	Clear(string) error
	ForceNew(string) error
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}

// Infers the region based on the following (in order of priority):
// - `region` field in resource schema
// - region extracted from the `zone` field in resource schema
// - provider-level region
// - region extracted from the provider-level zone
func getRegion(d TerraformResourceData, config *Config) (string, error) {
	return getRegionFromSchema("region", "zone", d, config)
}

// getProject reads the "project" field from the given resource data and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProject(d TerraformResourceData, config *Config) (string, error) {
	return getProjectFromSchema("project", d, config)
}

// getBillingProject reads the "billing_project" field from the given resource data and falls
// back to the provider's value if not given. If no value is found, an error is returned.
func getBillingProject(d TerraformResourceData, config *Config) (string, error) {
	return getBillingProjectFromSchema("billing_project", d, config)
}

// getProjectFromDiff reads the "project" field from the given diff and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProjectFromDiff(d *schema.ResourceDiff, config *Config) (string, error) {
	res, ok := d.GetOk("project")
	if ok {
		return res.(string), nil
	}
	if config.Project != "" {
		return config.Project, nil
	}
	return "", fmt.Errorf("%s: required field is not set", "project")
}

func getRouterLockName(region string, router string) string {
	return fmt.Sprintf("router/%s/%s", region, router)
}

func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
	if isGoogleApiErrorWithCode(err, 404) {
		log.Printf("[WARN] Removing %s because it's gone", resource)
		// The resource doesn't exist anymore
		d.SetId("")

		return nil
	}

	return errwrap.Wrapf(
		fmt.Sprintf("Error when reading or editing %s: {{err}}", resource), err)
}

func isGoogleApiErrorWithCode(err error, errCode int) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	return ok && gerr != nil && gerr.Code == errCode
}

func isApiNotEnabledError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 403 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "accessNotConfigured" {
			return true
		}
	}
	return false
}

func isFailedPreconditionError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 400 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "failedPrecondition" {
			return true
		}
	}
	return false
}

func isConflictError(err error) bool {
	if e, ok := err.(*googleapi.Error); ok && (e.Code == 409 || e.Code == 412) {
		return true
	} else if !ok && errwrap.ContainsType(err, &googleapi.Error{}) {
		e := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
		if e.Code == 409 || e.Code == 412 {
			return true
		}
	}
	return false
}

// expandLabels pulls the value of "labels" out of a TerraformResourceData as a map[string]string.
func expandLabels(d TerraformResourceData) map[string]string {
	return expandStringMap(d, "labels")
}

// expandEnvironmentVariables pulls the value of "environment_variables" out of a schema.ResourceData as a map[string]string.
func expandEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "environment_variables")
}

// expandBuildEnvironmentVariables pulls the value of "build_environment_variables" out of a schema.ResourceData as a map[string]string.
func expandBuildEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "build_environment_variables")
}

// expandStringMap pulls the value of key out of a TerraformResourceData as a map[string]string.
func expandStringMap(d TerraformResourceData, key string) map[string]string {
	v, ok := d.GetOk(key)

	if !ok {
		return map[string]string{}
	}

	return convertStringMap(v.(map[string]interface{}))
}

func convertStringMap(v map[string]interface{}) map[string]string {
	m := make(map[string]string)
	for k, val := range v {
		m[k] = val.(string)
	}
	return m
}

func convertStringArr(ifaceArr []interface{}) []string {
	return convertAndMapStringArr(ifaceArr, func(s string) string { return s })
}

func convertAndMapStringArr(ifaceArr []interface{}, f func(string) string) []string {
	var arr []string
	for _, v := range ifaceArr {
		if v == nil {
			continue
		}
		arr = append(arr, f(v.(string)))
	}
	return arr
}

func mapStringArr(original []string, f func(string) string) []string {
	var arr []string
	for _, v := range original {
		arr = append(arr, f(v))
	}
	return arr
}

func convertStringArrToInterface(strs []string) []interface{} {
	arr := make([]interface{}, len(strs))
	for i, str := range strs {
		arr[i] = str
	}
	return arr
}

func convertStringSet(set *schema.Set) []string {
	s := make([]string, 0, set.Len())
	for _, v := range set.List() {
		s = append(s, v.(string))
	}
	sort.Strings(s)

	return s
}

func golangSetFromStringSlice(strings []string) map[string]struct{} {
	set := map[string]struct{}{}
	for _, v := range strings {
		set[v] = struct{}{}
	}

	return set
}

func stringSliceFromGolangSet(sset map[string]struct{}) []string {
	ls := make([]string, 0, len(sset))
	for s := range sset {
		ls = append(ls, s)
	}
	sort.Strings(ls)

	return ls
}

func reverseStringMap(m map[string]string) map[string]string {
	o := map[string]string{}
	for k, v := range m {
		o[v] = k
	}
	return o
}

func mergeStringMaps(a, b map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeSchemas(a, b map[string]*schema.Schema) map[string]*schema.Schema {
	merged := make(map[string]*schema.Schema)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeResourceMaps(ms ...map[string]*schema.Resource) (map[string]*schema.Resource, error) {
	merged := make(map[string]*schema.Resource)
	duplicates := []string{}

	for _, m := range ms {
		for k, v := range m {
			if _, ok := merged[k]; ok {
				duplicates = append(duplicates, k)
			}

			merged[k] = v
		}
	}

	var err error
	if len(duplicates) > 0 {
		err = fmt.Errorf("saw duplicates in mergeResourceMaps: %v", duplicates)
	}

	return merged, err
}

func extractFirstMapConfig(m []interface{}) map[string]interface{} {
	if len(m) == 0 {
		return map[string]interface{}{}
	}

	return m[0].(map[string]interface{})
}

func lockedCall(lockKey string, f func() error) error {
	mutexKV.Lock(lockKey)
	defer mutexKV.Unlock(lockKey)

	return f()
}

// This is a Printf sibling (Nprintf; Named Printf), which handles strings like
// Nprintf("Hello %{target}!", map[string]interface{}{"target":"world"}) == "Hello world!".
// This is particularly useful for generated tests, where we don't want to use Printf,
// since that would require us to generate a very particular ordering of arguments.
func Nprintf(format string, params map[string]interface{}) string {
	for key, val := range params {
		format = strings.Replace(format, "%{"+key+"}", fmt.Sprintf("%v", val), -1)
	}
	return format
}

// serviceAccountFQN will attempt to generate the fully qualified name in the format of:
// "projects/(-|<project>)/serviceAccounts/<service_account_id>@<project>.iam.gserviceaccount.com"
// A project is required if we are trying to build the FQN from a service account id and
// and error will be returned in this case if no project is set in the resource or the
// provider-level config
func serviceAccountFQN(serviceAccount string, d TerraformResourceData, config *Config) (string, error) {
	// If the service account id is already the fully qualified name
	if strings.HasPrefix(serviceAccount, "projects/") {
		return serviceAccount, nil
	}

	// If the service account id is an email
	if strings.Contains(serviceAccount, "@") {
		return "projects/-/serviceAccounts/" + serviceAccount, nil
	}

	// Get the project from the resource or fallback to the project
	// in the provider configuration
	project, err := getProject(d, config)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("projects/-/serviceAccounts/%s@%s.iam.gserviceaccount.com", serviceAccount, project), nil
}

func paginatedListRequest(project, baseUrl, userAgent string, config *Config, flattener func(map[string]interface{}) []interface{}) ([]interface{}, error) {
	res, err := sendRequest(config, "GET", project, baseUrl, userAgent, nil)
	if err != nil {
		return nil, err
	}

	ls := flattener(res)
	pageToken, ok := res["pageToken"]
	for ok {
		if pageToken.(string) == "" {
			break
		}
		url := fmt.Sprintf("%s?pageToken=%s", baseUrl, pageToken.(string))
		res, err = sendRequest(config, "GET", project, url, userAgent, nil)
		if err != nil {
			return nil, err
		}
		ls = append(ls, flattener(res))
		pageToken, ok = res["pageToken"]
	}

	return ls, nil
}

func getInterconnectAttachmentLink(config *Config, project, region, ic, userAgent string) (string, error) {
	if !strings.Contains(ic, "/") {
		icData, err := config.NewComputeClient(userAgent).InterconnectAttachments.Get(
			project, region, ic).Do()
		if err != nil {
			return "", fmt.Errorf("Error reading interconnect attachment: %s", err)
		}
		ic = icData.SelfLink
	}

	return ic, nil
}

// Given two sets of references (with "from" values in self link form),
// determine which need to be added or removed // during an update using
// addX/removeX APIs.
func calcAddRemove(from []string, to []string) (add, remove []string) {
	add = make([]string, 0)
	remove = make([]string, 0)
	for _, u := range to {
		found := false
		for _, v := range from {
			if compareSelfLinkOrResourceName("", v, u, nil) {
				found = true
				break
			}
		}
		if !found {
			add = append(add, u)
		}
	}
	for _, u := range from {
		found := false
		for _, v := range to {
			if compareSelfLinkOrResourceName("", u, v, nil) {
				found = true
				break
			}
		}
		if !found {
			remove = append(remove, u)
		}
	}
	return add, remove
}

func stringInSlice(arr []string, str string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}

	return false
}

func migrateStateNoop(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	return is, nil
}

func expandString(v interface{}, d TerraformResourceData, config *Config) (string, error) {
	return v.(string), nil
}

func changeFieldSchemaToForceNew(sch *schema.Schema) {
	sch.ForceNew = true
	switch sch.Type {
	case schema.TypeList:
	case schema.TypeSet:
		if nestedR, ok := sch.Elem.(*schema.Resource); ok {
			for _, nestedSch := range nestedR.Schema {
				changeFieldSchemaToForceNew(nestedSch)
			}
		}
	}
}

func generateUserAgentString(d TerraformResourceData, currentUserAgent string) (string, error) {
	var m providerMeta

	err := d.GetProviderMeta(&m)
	if err != nil {
		return currentUserAgent, err
	}

	if m.ModuleName != "" {
		return strings.Join([]string{currentUserAgent, m.ModuleName}, " "), nil
	}

	return currentUserAgent, nil
}
<commit_msg>bigquery_table - add customized diff for specific properties and changes on schema (#4400) (#614)<commit_after>// Contains functions that don't really belong anywhere else.

package google

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
	"google.golang.org/api/googleapi"
)

type TerraformResourceData interface {
	HasChange(string) bool
	GetOkExists(string) (interface{}, bool)
	GetOk(string) (interface{}, bool)
	Get(string) interface{}
	Set(string, interface{}) error
	SetId(string)
	Id() string
	GetProviderMeta(interface{}) error
}

type TerraformResourceDiff interface {
	HasChange(string) bool
	GetChange(string) (interface{}, interface{})
	Get(string) interface{}
	GetOk(string) (interface{}, bool)
	Clear(string) error
	ForceNew(string) error
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}

// Infers the region based on the following (in order of priority):
// - `region` field in resource schema
// - region extracted from the `zone` field in resource schema
// - provider-level region
// - region extracted from the provider-level zone
func getRegion(d TerraformResourceData, config *Config) (string, error) {
	return getRegionFromSchema("region", "zone", d, config)
}

// getProject reads the "project" field from the given resource data and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProject(d TerraformResourceData, config *Config) (string, error) {
	return getProjectFromSchema("project", d, config)
}

// getBillingProject reads the "billing_project" field from the given resource data and falls
// back to the provider's value if not given. If no value is found, an error is returned.
func getBillingProject(d TerraformResourceData, config *Config) (string, error) {
	return getBillingProjectFromSchema("billing_project", d, config)
}

// getProjectFromDiff reads the "project" field from the given diff and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProjectFromDiff(d *schema.ResourceDiff, config *Config) (string, error) {
	res, ok := d.GetOk("project")
	if ok {
		return res.(string), nil
	}
	if config.Project != "" {
		return config.Project, nil
	}
	return "", fmt.Errorf("%s: required field is not set", "project")
}

func getRouterLockName(region string, router string) string {
	return fmt.Sprintf("router/%s/%s", region, router)
}

func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
	if isGoogleApiErrorWithCode(err, 404) {
		log.Printf("[WARN] Removing %s because it's gone", resource)
		// The resource doesn't exist anymore
		d.SetId("")

		return nil
	}

	return errwrap.Wrapf(
		fmt.Sprintf("Error when reading or editing %s: {{err}}", resource), err)
}

func isGoogleApiErrorWithCode(err error, errCode int) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	return ok && gerr != nil && gerr.Code == errCode
}

func isApiNotEnabledError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 403 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "accessNotConfigured" {
			return true
		}
	}
	return false
}

func isFailedPreconditionError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 400 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "failedPrecondition" {
			return true
		}
	}
	return false
}

func isConflictError(err error) bool {
	if e, ok := err.(*googleapi.Error); ok && (e.Code == 409 || e.Code == 412) {
		return true
	} else if !ok && errwrap.ContainsType(err, &googleapi.Error{}) {
		e := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
		if e.Code == 409 || e.Code == 412 {
			return true
		}
	}
	return false
}

// expandLabels pulls the value of "labels" out of a TerraformResourceData as a map[string]string.
func expandLabels(d TerraformResourceData) map[string]string {
	return expandStringMap(d, "labels")
}

// expandEnvironmentVariables pulls the value of "environment_variables" out of a schema.ResourceData as a map[string]string.
func expandEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "environment_variables")
}

// expandBuildEnvironmentVariables pulls the value of "build_environment_variables" out of a schema.ResourceData as a map[string]string.
func expandBuildEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "build_environment_variables")
}

// expandStringMap pulls the value of key out of a TerraformResourceData as a map[string]string.
func expandStringMap(d TerraformResourceData, key string) map[string]string {
	v, ok := d.GetOk(key)

	if !ok {
		return map[string]string{}
	}

	return convertStringMap(v.(map[string]interface{}))
}

func convertStringMap(v map[string]interface{}) map[string]string {
	m := make(map[string]string)
	for k, val := range v {
		m[k] = val.(string)
	}
	return m
}

func convertStringArr(ifaceArr []interface{}) []string {
	return convertAndMapStringArr(ifaceArr, func(s string) string { return s })
}

func convertAndMapStringArr(ifaceArr []interface{}, f func(string) string) []string {
	var arr []string
	for _, v := range ifaceArr {
		if v == nil {
			continue
		}
		arr = append(arr, f(v.(string)))
	}
	return arr
}

func mapStringArr(original []string, f func(string) string) []string {
	var arr []string
	for _, v := range original {
		arr = append(arr, f(v))
	}
	return arr
}

func convertStringArrToInterface(strs []string) []interface{} {
	arr := make([]interface{}, len(strs))
	for i, str := range strs {
		arr[i] = str
	}
	return arr
}

func convertStringSet(set *schema.Set) []string {
	s := make([]string, 0, set.Len())
	for _, v := range set.List() {
		s = append(s, v.(string))
	}
	sort.Strings(s)

	return s
}

func golangSetFromStringSlice(strings []string) map[string]struct{} {
	set := map[string]struct{}{}
	for _, v := range strings {
		set[v] = struct{}{}
	}

	return set
}

func stringSliceFromGolangSet(sset map[string]struct{}) []string {
	ls := make([]string, 0, len(sset))
	for s := range sset {
		ls = append(ls, s)
	}
	sort.Strings(ls)

	return ls
}

func reverseStringMap(m map[string]string) map[string]string {
	o := map[string]string{}
	for k, v := range m {
		o[v] = k
	}
	return o
}

func mergeStringMaps(a, b map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeSchemas(a, b map[string]*schema.Schema) map[string]*schema.Schema {
	merged := make(map[string]*schema.Schema)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeResourceMaps(ms ...map[string]*schema.Resource) (map[string]*schema.Resource, error) {
	merged := make(map[string]*schema.Resource)
	duplicates := []string{}

	for _, m := range ms {
		for k, v := range m {
			if _, ok := merged[k]; ok {
				duplicates = append(duplicates, k)
			}

			merged[k] = v
		}
	}

	var err error
	if len(duplicates) > 0 {
		err = fmt.Errorf("saw duplicates in mergeResourceMaps: %v", duplicates)
	}

	return merged, err
}

func extractFirstMapConfig(m []interface{}) map[string]interface{} {
	if len(m) == 0 {
		return map[string]interface{}{}
	}

	return m[0].(map[string]interface{})
}

func lockedCall(lockKey string, f func() error) error {
	mutexKV.Lock(lockKey)
	defer mutexKV.Unlock(lockKey)

	return f()
}

// This is a Printf sibling (Nprintf; Named Printf), which handles strings like
// Nprintf("Hello %{target}!", map[string]interface{}{"target":"world"}) == "Hello world!".
// This is particularly useful for generated tests, where we don't want to use Printf,
// since that would require us to generate a very particular ordering of arguments.
func Nprintf(format string, params map[string]interface{}) string {
	for key, val := range params {
		format = strings.Replace(format, "%{"+key+"}", fmt.Sprintf("%v", val), -1)
	}
	return format
}

// serviceAccountFQN will attempt to generate the fully qualified name in the format of:
// "projects/(-|<project>)/serviceAccounts/<service_account_id>@<project>.iam.gserviceaccount.com"
// A project is required if we are trying to build the FQN from a service account id and
// and error will be returned in this case if no project is set in the resource or the
// provider-level config
func serviceAccountFQN(serviceAccount string, d TerraformResourceData, config *Config) (string, error) {
	// If the service account id is already the fully qualified name
	if strings.HasPrefix(serviceAccount, "projects/") {
		return serviceAccount, nil
	}

	// If the service account id is an email
	if strings.Contains(serviceAccount, "@") {
		return "projects/-/serviceAccounts/" + serviceAccount, nil
	}

	// Get the project from the resource or fallback to the project
	// in the provider configuration
	project, err := getProject(d, config)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("projects/-/serviceAccounts/%s@%s.iam.gserviceaccount.com", serviceAccount, project), nil
}

func paginatedListRequest(project, baseUrl, userAgent string, config *Config, flattener func(map[string]interface{}) []interface{}) ([]interface{}, error) {
	res, err := sendRequest(config, "GET", project, baseUrl, userAgent, nil)
	if err != nil {
		return nil, err
	}

	ls := flattener(res)
	pageToken, ok := res["pageToken"]
	for ok {
		if pageToken.(string) == "" {
			break
		}
		url := fmt.Sprintf("%s?pageToken=%s", baseUrl, pageToken.(string))
		res, err = sendRequest(config, "GET", project, url, userAgent, nil)
		if err != nil {
			return nil, err
		}
		ls = append(ls, flattener(res))
		pageToken, ok = res["pageToken"]
	}

	return ls, nil
}

func getInterconnectAttachmentLink(config *Config, project, region, ic, userAgent string) (string, error) {
	if !strings.Contains(ic, "/") {
		icData, err := config.NewComputeClient(userAgent).InterconnectAttachments.Get(
			project, region, ic).Do()
		if err != nil {
			return "", fmt.Errorf("Error reading interconnect attachment: %s", err)
		}
		ic = icData.SelfLink
	}

	return ic, nil
}

// Given two sets of references (with "from" values in self link form),
// determine which need to be added or removed // during an update using
// addX/removeX APIs.
func calcAddRemove(from []string, to []string) (add, remove []string) {
	add = make([]string, 0)
	remove = make([]string, 0)
	for _, u := range to {
		found := false
		for _, v := range from {
			if compareSelfLinkOrResourceName("", v, u, nil) {
				found = true
				break
			}
		}
		if !found {
			add = append(add, u)
		}
	}
	for _, u := range from {
		found := false
		for _, v := range to {
			if compareSelfLinkOrResourceName("", u, v, nil) {
				found = true
				break
			}
		}
		if !found {
			remove = append(remove, u)
		}
	}
	return add, remove
}

func stringInSlice(arr []string, str string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}

	return false
}

func migrateStateNoop(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	return is, nil
}

func expandString(v interface{}, d TerraformResourceData, config *Config) (string, error) {
	return v.(string), nil
}

func changeFieldSchemaToForceNew(sch *schema.Schema) {
	sch.ForceNew = true
	switch sch.Type {
	case schema.TypeList:
	case schema.TypeSet:
		if nestedR, ok := sch.Elem.(*schema.Resource); ok {
			for _, nestedSch := range nestedR.Schema {
				changeFieldSchemaToForceNew(nestedSch)
			}
		}
	}
}

func generateUserAgentString(d TerraformResourceData, currentUserAgent string) (string, error) {
	var m providerMeta

	err := d.GetProviderMeta(&m)
	if err != nil {
		return currentUserAgent, err
	}

	if m.ModuleName != "" {
		return strings.Join([]string{currentUserAgent, m.ModuleName}, " "), nil
	}

	return currentUserAgent, nil
}
<|endoftext|>


<commit_before>package gopwned

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"fmt"

	"github.com/stretchr/testify/assert"
)

var (
	expectedMethod  = "GET"
	expectedHeaders = map[string]string{
		"Accept": MediaTypeV2,
	}

	mockHandler *http.ServeMux
	mockServer  *httptest.Server
)

func init() {
	mockHandler = http.NewServeMux()
	mockServer = httptest.NewServer(mockHandler)

	// overwrite the package's default
	localURL, _ := url.Parse(mockServer.URL)

	defaultClient.BaseURL = localURL
	defaultClient.PwnPwdURL = localURL
}

func checkHeader(t *testing.T) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != expectedMethod {
			t.Fatalf("Expected %s for request method, got %s", expectedMethod, r.Method)
		}

		for k, v := range expectedHeaders {
			header := r.Header.Get(k)
			if header != v {
				t.Fatalf("Expected %s for request header, got %s", v, header)
			}
		}
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	if got, want := c.BaseURL.String(), Endpoint; got != want {
		t.Errorf("NewClient BaseURL is %v, want %v", got, want)
	}

	if got, want := c.PwnPwdURL.String(), PwnPwdEndpoint; got != want {
		t.Errorf("NewClient PwnPwdURL is %v, want %v", got, want)
	}

	if got, want := c.UserAgent, UserAgent; got != want {
		t.Errorf("NewClient UserAgent is %v, want %v", got, want)
	}
}

// XXX Test for *Client

func TestAllBreachesForAccount(t *testing.T) {
	assert := assert.New(t)

	mockHandler.HandleFunc("/breachedaccount/test@example.com", func(w http.ResponseWriter, r *http.Request) {
		checkHeader(t)(w, r)
		fmt.Fprint(w, `[{"Name":"000webhost"},{"Name": "Adobe"}]`)
	})

	account, err := GetAllBreachesForAccount("test@example.com", "", "")
	if err != nil {
		t.Fatalf("[Get All Breaches For Account] returned error: %v", err)
	}

	want := []*Breach{
		{Name: "000webhost"},
		{Name: "Adobe"},
	}
	assert.Equal(want, account, "Expected equal value")
}

func TestAllBreachedSites(t *testing.T) {
	assert := assert.New(t)

	mockHandler.HandleFunc("/breaches", func(w http.ResponseWriter, r *http.Request) {
		checkHeader(t)(w, r)
		fmt.Fprint(w, `[{"Name": "Adobe"}]`)
	})

	account, err := GetAllBreachedSites("adobe.com")
	if err != nil {
		t.Fatalf("[Get All Breached Sites] returned error: %v", err)
	}

	want := []*Breach{
		{Name: "Adobe"},
	}
	assert.Equal(want, account, "they should be the same output.")
}

func TestGetBreachedSite(t *testing.T) {
	assert := assert.New(t)

	mockHandler.HandleFunc("/breach/Adobe", func(w http.ResponseWriter, r *http.Request) {
		checkHeader(t)(w, r)
		fmt.Fprint(w, `[{"Name": "Adobe"}]`)
	})

	account, err := GetBreachedSite("Adobe")
	if err != nil {
		t.Fatalf("[Get Breached Site] returned error: %v", err)
	}

	want := []*Breach{
		{Name: "Adobe"},
	}
	assert.Equal(want, account, "they should be the same output.")
}

func TestGetDataClasses(t *testing.T) {
	assert := assert.New(t)

	mockHandler.HandleFunc("/dataclasses", func(w http.ResponseWriter, r *http.Request) {
		checkHeader(t)(w, r)
		fmt.Fprint(w, `["Account balances","Age groups"]`)
	})

	account, err := GetDataClasses()
	if err != nil {
		t.Fatalf("[Get All Data Classes] returned error: %v", err)
	}

	want := &DataClasses{
		"Account balances",
		"Age groups",
	}
	assert.Equal(want, account, "they should be the same output.")
}

func TestGetAllPastesForAccount(t *testing.T) {
	assert := assert.New(t)

	mockHandler.HandleFunc("/pasteaccount/test@example.com", func(w http.ResponseWriter, r *http.Request) {
		checkHeader(t)(w, r)
		fmt.Fprint(w, `[{"Source":"Pastebin","Id":"Ab2ZYrq4","EmailCount":48},{"Source":"Pastebin","Id":"46g62dvD","EmailCount":1670}]`)
	})

	account, err := GetAllPastesForAccount("test@example.com")
	if err != nil {
		t.Errorf("[Get All Breaches For Account] returned error: %v", err)
	}

	want := []*Paste{
		{
			Source:     "Pastebin",
			ID:         "Ab2ZYrq4",
			EmailCount: 48,
		},
		{
			Source:     "Pastebin",
			ID:         "46g62dvD",
			EmailCount: 1670,
		},
	}
	assert.Equal(want, account, "they should be the same output.")
}

func TestPwnedPasswords(t *testing.T) {
	assert := assert.New(t)

	want := "2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1\n2DC183F740EE76F27B78EB39C8AD972A757:49938"

	mockHandler.HandleFunc("/21BD1", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1\n2DC183F740EE76F27B78EB39C8AD972A757:49938")
	})

	pwds, err := PwnedPasswords("21BD1")
	if err != nil {
		t.Errorf("[PwnedPasswords] returned error: %v", err)
	}

	assert.Equal(want, string(pwds), "they should be the same output.")
}
<commit_msg>Refactored the test file as well.<commit_after>package gopwned

import (
	"encoding/json"
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestUnAuthReq(t *testing.T) {
	assert := assert.New(t)

	want_result := []*Breach([]*Breach(nil))
	want_error := errors.New("the function you're trying to request requires an API key")

	gopwn := NewClient(nil, "")
	got_result, got_err := gopwn.GetAllBreaches("multiple-breaches@hibp-integration-tests.com", "", true, false)
	if got_err != nil {
		assert.Equal(want_error, got_err, "[Error Test] The test expected a failure in authentication due to missing API key.")
	}
	assert.Equal(want_result, got_result, "[Return Test] The test expected a failure in authentication due to missing API key.")
}

func TestUnAuthzAPI(t *testing.T) {
	assert := assert.New(t)

	want_error := errors.New(respCodes[401])

	gopwn := NewClient(nil, "InvalidAPIKey")
	_, got_err := gopwn.GetAllBreaches("account-exists@hibp-integration-tests.com", "", true, false)
	if got_err != nil {
		assert.Equal(want_error, got_err, "The test expected to return a message based on HTTP Status Code 401.")
	}
}

func TestBreachesStruct(t *testing.T) {
	assert := assert.New(t)

	breach_struct := []*Breach{}

	raw_breaches := `
	[
	{
	"Name":"Adobe",
	"Title":"Adobe",
	"Domain":"adobe.com",
	"BreachDate":"2013-10-04",
	"AddedDate":"2013-12-04T00:00Z",
	"ModifiedDate":"2013-12-04T00:00Z",
	"PwnCount":152445165,
	"Description":"In October 2013, 153 million Adobe accounts were breached with each containing an internal ID, username, email, encrypted password and a password hint in plain text. The password cryptography was poorly done and many were quickly resolved back to plain text. The unencrypted hints also disclosed much about the passwords adding further to the risk that hundreds of millions of Adobe customers already faced.",
	"DataClasses":["Email addresses","Password hints","Passwords","Usernames"],
	"IsVerified":true,
	"IsFabricated":false,
	"IsSensitive":false,
	"IsRetired":false,
	"IsSpamList":false,
	"LogoPath":"https://haveibeenpwned.com/Content/Images/PwnedLogos/Adobe.png"
	},
	{
	"Name":"BattlefieldHeroes",
	"Title":"Battlefield Heroes",
	"Domain":"battlefieldheroes.com",
	"BreachDate":"2011-06-26",
	"AddedDate":"2014-01-23T13:10Z",
	"ModifiedDate":"2014-01-23T13:10Z",
	"PwnCount":530270,
	"Description":"In June 2011 as part of a final breached data dump, the hacker collective "LulzSec" obtained and released over half a million usernames and passwords from the game Battlefield Heroes. The passwords were stored as MD5 hashes with no salt and many were easily converted back to their plain text versions.",
	"DataClasses":["Passwords","Usernames"],
	"IsVerified":true,
	"IsFabricated":false,
	"IsSensitive":false,
	"IsRetired":false,
	"IsSpamList":false,
	"LogoPath":"https://haveibeenpwned.com/Content/Images/PwnedLogos/BattlefieldHeroes.png"
	}
	]
	`

	err := json.Unmarshal([]byte(raw_breaches), &breach_struct)
	if err != nil {
		t.Fatalf("[TestBreachesStruct] returned error: %v", err)
	}

	want := []*Breach{
		{
			Name:         "Adobe",
			Title:        "Adobe",
			Domain:       "adobe.com",
			BreachDate:   "2013-10-04",
			AddedDate:    "2013-12-04T00:00Z",
			ModifiedDate: "2013-12-04T00:00Z",
			PwnCount:     152445165,
			Description:  "In October 2013, 153 million Adobe accounts were breached with each containing an internal ID, username, email, encrypted password and a password hint in plain text. The password cryptography was poorly done and many were quickly resolved back to plain text. The unencrypted hints also disclosed much about the passwords adding further to the risk that hundreds of millions of Adobe customers already faced.",
			DataClasses: &DataClasses{
				"Email addresses",
				"Password hints",
				"Passwords",
				"Usernames",
			},
			IsVerified:   true,
			IsFabricated: false,
			IsSensitive:  false,
			IsRetired:    false,
			IsSpamList:   false,
			LogoPath:     "https://haveibeenpwned.com/Content/Images/PwnedLogos/Adobe.png",
		},
		{
			Name:         "BattlefieldHeroes",
			Title:        "Battlefield Heroes",
			Domain:       "battlefieldheroes.com",
			BreachDate:   "2011-06-26",
			AddedDate:    "2014-01-23T13:10Z",
			ModifiedDate: "2014-01-23T13:10Z",
			PwnCount:     530270,
			Description:  "In June 2011 as part of a final breached data dump, the hacker collective "LulzSec" obtained and released over half a million usernames and passwords from the game Battlefield Heroes. The passwords were stored as MD5 hashes with no salt and many were easily converted back to their plain text versions.",
			DataClasses: &DataClasses{
				"Passwords",
				"Usernames",
			},
			IsVerified:   true,
			IsFabricated: false,
			IsSensitive:  false,
			IsRetired:    false,
			IsSpamList:   false,
			LogoPath:     "https://haveibeenpwned.com/Content/Images/PwnedLogos/BattlefieldHeroes.png",
		},
	}

	assert.Equal(want, breach_struct, "Expected equal value for Breaches in TestBreaches.")
}

func TestPastesStruct(t *testing.T) {
	assert := assert.New(t)

	paste_struct := []*Paste{}

	raw_pastes := `
	[
	{
	"Source":"Pastebin",
	"Id":"8Q0BvKD8",
	"Title":"syslog",
	"Date":"2014-03-04T19:14:54Z",
	"EmailCount":139
	},
	{
	"Source":"Pastie",
	"Id":"7152479",
	"Date":"2013-03-28T16:51:10Z",
	"EmailCount":30
	}
	]
	`

	err := json.Unmarshal([]byte(raw_pastes), &paste_struct)
	if err != nil {
		t.Fatalf("[TestPastesStruct] returned error: %v", err)
	}

	want := []*Paste{
		{
			Source:     "Pastebin",
			ID:         "8Q0BvKD8",
			Title:      "syslog",
			Date:       "2014-03-04T19:14:54Z",
			EmailCount: 139,
		},
		{
			Source:     "Pastie",
			ID:         "7152479",
			Date:       "2013-03-28T16:51:10Z",
			EmailCount: 30,
		},
	}

	assert.Equal(want, paste_struct, "Expected equal value for Pastes in TestPastes.")
}
<|endoftext|>


<commit_before>// Copyright 2011 The 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 main

import (
	"bytes"
	"exec"
	"flag"
	"fmt"
	"go/build"
	"http"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"

	// Imports so that goinstall automatically installs them.
	_ "go-tour.googlecode.com/hg/pic"
	_ "go-tour.googlecode.com/hg/tree"
	_ "go-tour.googlecode.com/hg/wc"
)

const basePkg = "go-tour.googlecode.com/hg"

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
	// where gc and ld should find the go-tour packages
	pkgDir string
)

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// set archChar
	var err os.Error
	archChar, err = build.ArchChar(runtime.GOARCH)
	if err != nil {
		log.Fatal(err)
	}

	// find and serve the go tour files
	t, _, err := build.FindTree(basePkg)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := filepath.Join(t.SrcDir(), basePkg, "static")
	log.Println("Serving content from", root)
	//tip: http.Handle("/", http.FileServer(http.Dir(root)))
	http.Handle("/", http.FileServer(root, "/"))

	// set include path for ld and gc
	pkgDir = t.PkgDir()

	if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
		!strings.HasPrefix(*httpListen, "localhost") {
		log.Print(localhostWarning)
	}

	http.HandleFunc("/kill", kill)

	log.Printf("Serving at http://%s/", *httpListen)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

func compile(req *http.Request) (out []byte, err os.Error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := new(bytes.Buffer)
	if _, err = body.ReadFrom(req.Body); err != nil {
		return
	}
	if err = ioutil.WriteFile(src, body.Bytes(), 0666); err != nil {
		return
	}

	// build x.go, creating x.6
	out, err = run(archChar+"g", "-I", pkgDir, "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-L", pkgDir, "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run(bin)
}

// run executes the specified command and returns its output and an error.
func run(args ...string) ([]byte, os.Error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}
<commit_msg>update to work with latest release<commit_after>// Copyright 2011 The 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 main

import (
	"bytes"
	"exec"
	"flag"
	"fmt"
	"go/build"
	"http"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"

	// Imports so that goinstall automatically installs them.
	_ "go-tour.googlecode.com/hg/pic"
	_ "go-tour.googlecode.com/hg/tree"
	_ "go-tour.googlecode.com/hg/wc"
)

const basePkg = "go-tour.googlecode.com/hg"

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
	// where gc and ld should find the go-tour packages
	pkgDir string
)

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// set archChar
	var err os.Error
	archChar, err = build.ArchChar(runtime.GOARCH)
	if err != nil {
		log.Fatal(err)
	}

	// find and serve the go tour files
	t, _, err := build.FindTree(basePkg)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := filepath.Join(t.SrcDir(), basePkg, "static")
	log.Println("Serving content from", root)
	http.Handle("/", http.FileServer(http.Dir(root)))

	// set include path for ld and gc
	pkgDir = t.PkgDir()

	if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
		!strings.HasPrefix(*httpListen, "localhost") {
		log.Print(localhostWarning)
	}

	http.HandleFunc("/kill", kill)

	log.Printf("Serving at http://%s/", *httpListen)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

func compile(req *http.Request) (out []byte, err os.Error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := new(bytes.Buffer)
	if _, err = body.ReadFrom(req.Body); err != nil {
		return
	}
	if err = ioutil.WriteFile(src, body.Bytes(), 0666); err != nil {
		return
	}

	// build x.go, creating x.6
	out, err = run(archChar+"g", "-I", pkgDir, "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-L", pkgDir, "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run(bin)
}

// run executes the specified command and returns its output and an error.
func run(args ...string) ([]byte, os.Error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"testing"
)

func TestTokenize(t *testing.T) {
	// TODO write actual tests
	t.Log(tokenize([]rune("2+3/234.0")))
	t.Log(tokenize([]rune("-2+3")))
	t.Log(tokenize([]rune("-(-2)")))
	t.Log(tokenize([]rune("a=b(2,3),0")))
	t.Log(tokenize([]rune("4*(2+8)*4/2")))
}

func TestParse(t *testing.T) {
	env := map[string]Var{
		"x": NewVarExpr(5),
	}
	funcs := map[string]Func{
		"add3": NewFunc(func(args FuncArgs, env FuncEnv) Num {
			return args[0].Eval() + args[1].Eval() + args[2].Eval()
		}),
	}
	for input, result := range map[string]Num{
		"":  0,
		"2": 2,
		"x": 5,

		"-2": -2,
		"~2": -3,
		"!2": 0,
		"!0": 1,

		"3+2":       5,
		"3/2":       1.5,
		"2+3/2":     2 + 3/2.0,
		"4/2+8*4/2": 18,

		"2*x": 10,
		"2/x": 2 / 5.0,

		"4*(2+8)+4/2": 42,

		"2, 3, 5":  5,
		"2+3, 5*3": 15,

		"z=10":     10,
		"y=10,x+y": 15,

		"2+add3(3, 7, 9)":             21,
		"2+add3(3, add3(1, 2, 3), 9)": 20,
	} {
		if e, err := Parse(input, env, funcs); err != nil {
			t.Error(err)
		} else if n := e.Eval(); n != result {
			t.Error(n, result)
		}
	}
}

func TestExprString(t *testing.T) {
	env := map[string]Var{
		"x": NewVarExpr(5),
	}
	funcs := map[string]Func{
		"plusone": NewFunc(func(args FuncArgs, env FuncEnv) Num {
			return args[0].Eval() + 1
		}),
	}
	e, _ := Parse("-2+plusone(x)", env, funcs)
	if s := fmt.Sprintf("%v", e); s != "fn[<8>(<1>(#2), fn[{5}])]" {
		t.Error(e)
	}
}
<commit_msg>added tests for tokenizer<commit_after>package expr

import (
	"fmt"
	"testing"
)

func TestTokenize(t *testing.T) {
	// Let's preter there is no "&" operator, but there is "&&"
	defer func() {
		ops["&"] = bitwiseAnd
	}()
	delete(ops, "&")

	for s, parts := range map[string][]string{
		"2":         {"2"},
		"2+3/234.0": {"2", "+", "3", "/", "234.0"},
		"2+-3":      {"2", "+", "-u", "3"},
		"2--3":      {"2", "-", "-u", "3"},
		"-(-2)":     {"-u", "(", "-u", "2", ")"},
		"foo":       {"foo"},
		"1>2":       {"1", ">", "2"},
		"1>-2":      {"1", ">", "-u", "2"},
		"1>>2":      {"1", ">>", "2"},
		"1>>-2":     {"1", ">>", "-u", "2"},
		"1>>!2":     {"1", ">>", "!", "2"},
		"1&&2":      {"1", "&&", "2"},
		"1&&":       {"1", "&&"},
		"1&&&":      nil, // This should return an error: 'no such operator &'
	} {
		if tokens, err := tokenize([]rune(s)); err != nil {
			if parts != nil {
				t.Error(err)
			}
		} else if len(tokens) != len(parts) {
			t.Error(tokens, parts)
		} else {
			for i, tok := range tokens {
				if tok != parts[i] {
					t.Error(tokens, parts)
					break
				}
			}
		}
	}
}

func TestParse(t *testing.T) {
	env := map[string]Var{
		"x": NewVarExpr(5),
	}
	funcs := map[string]Func{
		"add3": NewFunc(func(args FuncArgs, env FuncEnv) Num {
			return args[0].Eval() + args[1].Eval() + args[2].Eval()
		}),
	}
	for input, result := range map[string]Num{
		"":  0,
		"2": 2,
		"x": 5,

		"-2": -2,
		"~2": -3,
		"!2": 0,
		"!0": 1,

		"3+2":       5,
		"3/2":       1.5,
		"2+3/2":     2 + 3/2.0,
		"4/2+8*4/2": 18,

		"2*x": 10,
		"2/x": 2 / 5.0,

		"4*(2+8)+4/2": 42,

		"2, 3, 5":  5,
		"2+3, 5*3": 15,

		"z=10":     10,
		"y=10,x+y": 15,

		"2+add3(3, 7, 9)":             21,
		"2+add3(3, add3(1, 2, 3), 9)": 20,
	} {
		if e, err := Parse(input, env, funcs); err != nil {
			t.Error(err)
		} else if n := e.Eval(); n != result {
			t.Error(n, result)
		}
	}
}

func TestExprString(t *testing.T) {
	env := map[string]Var{
		"x": NewVarExpr(5),
	}
	funcs := map[string]Func{
		"plusone": NewFunc(func(args FuncArgs, env FuncEnv) Num {
			return args[0].Eval() + 1
		}),
	}
	e, _ := Parse("-2+plusone(x)", env, funcs)
	if s := fmt.Sprintf("%v", e); s != "fn[<8>(<1>(#2), fn[{5}])]" {
		t.Error(e)
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"github.com/coreos/go-etcd/etcd"
	"github.com/materials-commons/config/cfg"
)

type etcdHandler struct {
	client *etcd.Client // Etcd client connection
}

// Etcd returns a Handler that access keys that are stored in Etcd.
func Etcd(client *etcd.Client) cfg.Handler {
	return &etcdHandler{
		client: client,
	}
}

// Init doesn't do anything
func (h *etcdHandler) Init() error {
	return nil
}

// Get retrieves a key from etcd. Any error calling etcd is mapped to ErrKeyNotFound.
func (h *etcdHandler) Get(key string, args ...interface{}) (interface{}, error) {
	if len(args) != 0 {
		return nil, cfg.ErrArgsNotSupported
	}

	resp, err := h.client.Get(key, false, false)
	if err != nil {
		return nil, cfg.ErrKeyNotFound
	}

	return resp.Node.Value, nil
}

// Set will set a key value. Values will be converted and stored as strings. It will
// attempt to convert the value to a string before it stores it. If the value cannot
// be converted to a string it will return ErrBadType. Any error calling etcd will
// be returned as ErrKeyNotSet.
func (h *etcdHandler) Set(key string, value interface{}, args ...interface{}) error {
	if len(args) != 0 {
		return cfg.ErrArgsNotSupported
	}

	sval, err := cfg.ToString(value)
	if err != nil {
		return cfg.ErrBadType
	}

	if _, err := h.client.Set(key, sval, 0); err != nil {
		return cfg.ErrKeyNotSet
	}

	return nil
}

// Args returns false. This handler doesn't accept addtional arguments.
func (h *etcdHandler) Args() bool {
	return false
}
<commit_msg>Move etcd out of config<commit_after><|endoftext|>


<commit_before>package handler

import (
	"github.com/Sirupsen/logrus"
	"github.com/unrolled/render"
)

var (
	l *logrus.Logger
	output = render.New(render.Options{Layout: "layout"})
)

func SetLogger(logger *logrus.Logger) {
	l = logger
}
<commit_msg>Rename `l` to `log` for readability<commit_after>package handler

import (
	"github.com/Sirupsen/logrus"
	"github.com/unrolled/render"
)

var (
	log    *logrus.Logger
	output = render.New(render.Options{Layout: "layout"})
)

func SetLogger(l *logrus.Logger) {
	log = l
}
<|endoftext|>


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

package health

import (
	"sync"
	"time"
)

// HealthStatusKey is the key to the health status item in the cache.
type HealthStatusKey struct {
	ServiceID       string
	InstanceID      int
	HealthCheckName string
}

// HealthStatusItem is an item stored in the health status cache.
type HealthStatusItem struct {
	value   HealthStatus
	expires time.Time
}

// Value returns the HealthStatus data
func (item *HealthStatusItem) Value() HealthStatus {
	return item.value
}

// Expired returns true when a health status item has expired in the cache.
func (item *HealthStatusItem) Expired() bool {
	return time.Now().After(item.expires)
}

// HealthStatusCache keeps track of the health status items in memory.
type HealthStatusCache struct {
	mu   *sync.Mutex
	data map[HealthStatusKey]HealthStatusItem
	stop chan struct{}
	wg   *sync.WaitGroup
}

// New returns a new HealthStatusCache instance
func New() *HealthStatusCache {
	cache := &HealthStatusCache{
		mu:   &sync.Mutex{},
		data: make(map[HealthStatusKey]HealthStatusItem),
		wg:   &sync.WaitGroup{},
	}
	return cache
}

// SetPurgeFrequency sets the autopurge interval for cache cleanup.
// Stops autopurge if interval is <= 0.
func (cache *HealthStatusCache) SetPurgeFrequency(interval time.Duration) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	cache.setPurgeFrequency(interval)
}

func (cache *HealthStatusCache) setPurgeFrequency(interval time.Duration) {
	if cache.stop != nil {
		close(cache.stop)
		cache.wg.Wait()
	}
	if interval > 0 {
		cache.stop = make(chan struct{})
		cache.wg.Add(1)
		go func() {
			defer cache.wg.Done()
			timer := time.NewTimer(interval)
			defer timer.Stop()
			for {
				select {
				case <-timer.C:
					cache.DeleteExpired()
					timer.Reset(interval)
				case <-cache.stop:
					return
				}
			}
		}()
	} else {
		cache.stop = nil
	}
}

// Size returns the size of the cache.
func (cache *HealthStatusCache) Size() int {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	return len(cache.data)
}

// Get returns an item from the cache if it hasn't yet expired.
func (cache *HealthStatusCache) Get(key HealthStatusKey) (HealthStatus, bool) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	item, ok := cache.get(key)
	return item.Value(), ok
}

// get is non thread-safe
func (cache *HealthStatusCache) get(key HealthStatusKey) (item HealthStatusItem, ok bool) {
	if item, ok = cache.data[key]; ok {
		if item.Expired() {
			cache.delete(key)
			return HealthStatusItem{}, false
		}
	}
	return
}

// Set sets an item into the cache.
func (cache *HealthStatusCache) Set(key HealthStatusKey, value HealthStatus, expire time.Duration) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	cache.set(key, value, time.Now().Add(expire))
}

// set is non thread-safe
func (cache *HealthStatusCache) set(key HealthStatusKey, value HealthStatus, expires time.Time) {
	cache.data[key] = HealthStatusItem{value: value, expires: expires}
}

// Delete removes an item from the cache.
func (cache *HealthStatusCache) Delete(key HealthStatusKey) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	if _, ok := cache.get(key); ok {
		cache.delete(key)
	}
}

// DeleteExpired removes all expired items from the cache.
func (cache *HealthStatusCache) DeleteExpired() {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	for key, item := range cache.data {
		if item.Expired() {
			cache.delete(key)
		}
	}
}

// DeleteInstance removes all health checks per instance.
func (cache *HealthStatusCache) DeleteInstance(serviceID string, instanceID int) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	for key := range cache.data {
		if key.ServiceID == serviceID && key.InstanceID == instanceID {
			cache.delete(key)
		}
	}
}

// delete is non thread-safe
func (cache *HealthStatusCache) delete(key HealthStatusKey) {
	delete(cache.data, key)
}
<commit_msg>Fix deadlock btwn SetPurgeFrequency and DeleteExpired<commit_after>// Copyright 2016 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package health

import (
	"sync"
	"time"
)

// HealthStatusKey is the key to the health status item in the cache.
type HealthStatusKey struct {
	ServiceID       string
	InstanceID      int
	HealthCheckName string
}

// HealthStatusItem is an item stored in the health status cache.
type HealthStatusItem struct {
	value   HealthStatus
	expires time.Time
}

// Value returns the HealthStatus data
func (item *HealthStatusItem) Value() HealthStatus {
	return item.value
}

// Expired returns true when a health status item has expired in the cache.
func (item *HealthStatusItem) Expired() bool {
	return time.Now().After(item.expires)
}

// HealthStatusCache keeps track of the health status items in memory.
type HealthStatusCache struct {
	mu   *sync.Mutex
	data map[HealthStatusKey]HealthStatusItem
	stop chan struct{}
	wg   *sync.WaitGroup
}

// New returns a new HealthStatusCache instance
func New() *HealthStatusCache {
	cache := &HealthStatusCache{
		mu:   &sync.Mutex{},
		data: make(map[HealthStatusKey]HealthStatusItem),
		wg:   &sync.WaitGroup{},
	}
	return cache
}

// SetPurgeFrequency sets the autopurge interval for cache cleanup.
// Stops autopurge if interval is <= 0.
func (cache *HealthStatusCache) SetPurgeFrequency(interval time.Duration) {
	cache.mu.Lock()
	if cache.stop != nil {
		close(cache.stop)
		// Unlock before the Wait to avoid deadlock with DeleteExpired()
		cache.mu.Unlock()

		cache.wg.Wait()

		// Reacquire the lock because we're about to change something in cache
		cache.mu.Lock()
	}
	defer cache.mu.Unlock()

	if interval > 0 {
		cache.stop = make(chan struct{})
		cache.wg.Add(1)
		go func(stopChannel chan struct{}) {
			defer cache.wg.Done()
			timer := time.NewTimer(interval)
			defer timer.Stop()
			for {
				select {
				case <-timer.C:
					cache.DeleteExpired()
					timer.Reset(interval)
				case <-stopChannel:
					return
				}
			}
		}(cache.stop)
	} else {
		cache.stop = nil
	}
}

// Size returns the size of the cache.
func (cache *HealthStatusCache) Size() int {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	return len(cache.data)
}

// Get returns an item from the cache if it hasn't yet expired.
func (cache *HealthStatusCache) Get(key HealthStatusKey) (HealthStatus, bool) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	item, ok := cache.get(key)
	return item.Value(), ok
}

// get is non thread-safe
func (cache *HealthStatusCache) get(key HealthStatusKey) (item HealthStatusItem, ok bool) {
	if item, ok = cache.data[key]; ok {
		if item.Expired() {
			cache.delete(key)
			return HealthStatusItem{}, false
		}
	}
	return
}

// Set sets an item into the cache.
func (cache *HealthStatusCache) Set(key HealthStatusKey, value HealthStatus, expire time.Duration) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	cache.set(key, value, time.Now().Add(expire))
}

// set is non thread-safe
func (cache *HealthStatusCache) set(key HealthStatusKey, value HealthStatus, expires time.Time) {
	cache.data[key] = HealthStatusItem{value: value, expires: expires}
}

// Delete removes an item from the cache.
func (cache *HealthStatusCache) Delete(key HealthStatusKey) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	if _, ok := cache.get(key); ok {
		cache.delete(key)
	}
}

// DeleteExpired removes all expired items from the cache.
func (cache *HealthStatusCache) DeleteExpired() {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	for key, item := range cache.data {
		if item.Expired() {
			cache.delete(key)
		}
	}
}

// DeleteInstance removes all health checks per instance.
func (cache *HealthStatusCache) DeleteInstance(serviceID string, instanceID int) {
	cache.mu.Lock()
	defer cache.mu.Unlock()
	for key := range cache.data {
		if key.ServiceID == serviceID && key.InstanceID == instanceID {
			cache.delete(key)
		}
	}
}

// delete is non thread-safe
func (cache *HealthStatusCache) delete(key HealthStatusKey) {
	delete(cache.data, key)
}
<|endoftext|>


<commit_before>package http

import (
	"context"
	"encoding/json"
	"net/http"
	_ "net/http/pprof" // used for debug pprof at the default path.
	"strings"
	"time"

	"github.com/influxdata/influxdb/kit/prom"
	"github.com/influxdata/influxdb/kit/tracing"
	"github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
)

const (
	// MetricsPath exposes the prometheus metrics over /metrics.
	MetricsPath = "/metrics"
	// ReadyPath exposes the readiness of the service over /ready.
	ReadyPath = "/ready"
	// HealthPath exposes the health of the service over /health.
	HealthPath = "/health"
	// DebugPath exposes /debug/pprof for go debugging.
	DebugPath = "/debug"
)

// Handler provides basic handling of metrics, health and debug endpoints.
// All other requests are passed down to the sub handler.
type Handler struct {
	name string
	// MetricsHandler handles metrics requests
	MetricsHandler http.Handler
	// ReadyHandler handles readiness checks
	ReadyHandler http.Handler
	// HealthHandler handles health requests
	HealthHandler http.Handler
	// DebugHandler handles debug requests
	DebugHandler http.Handler
	// Handler handles all other requests
	Handler http.Handler

	requests   *prometheus.CounterVec
	requestDur *prometheus.HistogramVec

	// log logs all HTTP requests as they are served
	log *zap.Logger
}

// NewHandler creates a new handler with the given name.
// The name is used to tag the metrics produced by this handler.
//
// The MetricsHandler is set to the default prometheus handler.
// It is the caller's responsibility to call prometheus.MustRegister(h.PrometheusCollectors()...).
// In most cases, you want to use NewHandlerFromRegistry instead.
func NewHandler(name string) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: promhttp.Handler(),
		DebugHandler:   http.DefaultServeMux,
	}
	h.initMetrics()
	return h
}

// NewHandlerFromRegistry creates a new handler with the given name,
// and sets the /metrics endpoint to use the metrics from the given registry,
// after self-registering h's metrics.
func NewHandlerFromRegistry(log *zap.Logger, name string, reg *prom.Registry) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: reg.HTTPHandler(),
		ReadyHandler:   http.HandlerFunc(ReadyHandler),
		HealthHandler:  http.HandlerFunc(HealthHandler),
		DebugHandler:   http.DefaultServeMux,
		log:            log,
	}
	h.initMetrics()
	reg.MustRegister(h.PrometheusCollectors()...)
	return h
}

// ServeHTTP delegates a request to the appropriate subhandler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var span opentracing.Span
	span, r = tracing.ExtractFromHTTPRequest(r, h.name)
	ua := userAgent(r)
	span.LogKV("user_agent", ua, "referer", r.Referer(), "remote_addr", r.RemoteAddr)
	for k, v := range r.Header {
		if len(v) == 0 {
			continue
		}
		// If header has multiple values, only the first value will be logged on the trace.
		span.LogKV(k, v[0])
	}

	defer span.Finish()

	// TODO: better way to do this?
	statusW := newStatusResponseWriter(w)
	w = statusW

	// TODO: This could be problematic eventually. But for now it should be fine.
	defer func(start time.Time) {
		duration := time.Since(start)
		statusClass := statusW.statusCodeClass()
		h.requests.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Inc()
		h.requestDur.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Observe(duration.Seconds())
	}(time.Now())

	switch {
	case r.URL.Path == MetricsPath:
		h.MetricsHandler.ServeHTTP(w, r)
	case r.URL.Path == ReadyPath:
		h.ReadyHandler.ServeHTTP(w, r)
	case r.URL.Path == HealthPath:
		h.HealthHandler.ServeHTTP(w, r)
	case strings.HasPrefix(r.URL.Path, DebugPath):
		h.DebugHandler.ServeHTTP(w, r)
	default:
		h.Handler.ServeHTTP(w, r)
	}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, code int, res interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	return json.NewEncoder(w).Encode(res)
}

// PrometheusCollectors satisifies prom.PrometheusCollector.
func (h *Handler) PrometheusCollectors() []prometheus.Collector {
	return []prometheus.Collector{
		h.requests,
		h.requestDur,
	}
}

func (h *Handler) initMetrics() {
	const namespace = "http"
	const handlerSubsystem = "api"

	h.requests = prometheus.NewCounterVec(prometheus.CounterOpts{
		Namespace: namespace,
		Subsystem: handlerSubsystem,
		Name:      "requests_total",
		Help:      "Number of http requests received",
	}, []string{"handler", "method", "path", "status", "user_agent"})

	h.requestDur = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: handlerSubsystem,
		Name:      "request_duration_seconds",
		Help:      "Time taken to respond to HTTP request",
	}, []string{"handler", "method", "path", "status", "user_agent"})
}

func logEncodingError(log *zap.Logger, r *http.Request, err error) {
	// If we encounter an error while encoding the response to an http request
	// the best thing we can do is log that error, as we may have already written
	// the headers for the http request in question.
	log.Info("Error encoding response",
		zap.String("path", r.URL.Path),
		zap.String("method", r.Method),
		zap.Error(err))
}
<commit_msg>fix(http): remove authorization and user-agent headers from trace<commit_after>package http

import (
	"context"
	"encoding/json"
	"net/http"
	_ "net/http/pprof" // used for debug pprof at the default path.
	"strings"
	"time"

	"github.com/influxdata/influxdb/kit/prom"
	"github.com/influxdata/influxdb/kit/tracing"
	"github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
)

const (
	// MetricsPath exposes the prometheus metrics over /metrics.
	MetricsPath = "/metrics"
	// ReadyPath exposes the readiness of the service over /ready.
	ReadyPath = "/ready"
	// HealthPath exposes the health of the service over /health.
	HealthPath = "/health"
	// DebugPath exposes /debug/pprof for go debugging.
	DebugPath = "/debug"
)

// Handler provides basic handling of metrics, health and debug endpoints.
// All other requests are passed down to the sub handler.
type Handler struct {
	name string
	// MetricsHandler handles metrics requests
	MetricsHandler http.Handler
	// ReadyHandler handles readiness checks
	ReadyHandler http.Handler
	// HealthHandler handles health requests
	HealthHandler http.Handler
	// DebugHandler handles debug requests
	DebugHandler http.Handler
	// Handler handles all other requests
	Handler http.Handler

	requests   *prometheus.CounterVec
	requestDur *prometheus.HistogramVec

	// log logs all HTTP requests as they are served
	log *zap.Logger
}

// NewHandler creates a new handler with the given name.
// The name is used to tag the metrics produced by this handler.
//
// The MetricsHandler is set to the default prometheus handler.
// It is the caller's responsibility to call prometheus.MustRegister(h.PrometheusCollectors()...).
// In most cases, you want to use NewHandlerFromRegistry instead.
func NewHandler(name string) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: promhttp.Handler(),
		DebugHandler:   http.DefaultServeMux,
	}
	h.initMetrics()
	return h
}

// NewHandlerFromRegistry creates a new handler with the given name,
// and sets the /metrics endpoint to use the metrics from the given registry,
// after self-registering h's metrics.
func NewHandlerFromRegistry(log *zap.Logger, name string, reg *prom.Registry) *Handler {
	h := &Handler{
		name:           name,
		MetricsHandler: reg.HTTPHandler(),
		ReadyHandler:   http.HandlerFunc(ReadyHandler),
		HealthHandler:  http.HandlerFunc(HealthHandler),
		DebugHandler:   http.DefaultServeMux,
		log:            log,
	}
	h.initMetrics()
	reg.MustRegister(h.PrometheusCollectors()...)
	return h
}

// ServeHTTP delegates a request to the appropriate subhandler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var span opentracing.Span
	span, r = tracing.ExtractFromHTTPRequest(r, h.name)

	statusW := newStatusResponseWriter(w)
	w = statusW

	// record prometheus metrics and finish traces
	defer func(start time.Time) {
		duration := time.Since(start)
		statusClass := statusW.statusCodeClass()
		ua := userAgent(r)

		h.requests.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Inc()
		h.requestDur.With(prometheus.Labels{
			"handler":    h.name,
			"method":     r.Method,
			"path":       r.URL.Path,
			"status":     statusClass,
			"user_agent": ua,
		}).Observe(duration.Seconds())

		span.LogKV("user_agent", ua)
		for k, v := range r.Header {
			if len(v) == 0 {
				continue
			}

			// yeah, we don't need these
			if k == "Authorization" || k == "User-Agent" {
				continue
			}

			// If header has multiple values, only the first value will be logged on the trace.
			span.LogKV(k, v[0])
		}
		span.Finish()
	}(time.Now())

	switch {
	case r.URL.Path == MetricsPath:
		h.MetricsHandler.ServeHTTP(w, r)
	case r.URL.Path == ReadyPath:
		h.ReadyHandler.ServeHTTP(w, r)
	case r.URL.Path == HealthPath:
		h.HealthHandler.ServeHTTP(w, r)
	case strings.HasPrefix(r.URL.Path, DebugPath):
		h.DebugHandler.ServeHTTP(w, r)
	default:
		h.Handler.ServeHTTP(w, r)
	}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, code int, res interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	return json.NewEncoder(w).Encode(res)
}

// PrometheusCollectors satisifies prom.PrometheusCollector.
func (h *Handler) PrometheusCollectors() []prometheus.Collector {
	return []prometheus.Collector{
		h.requests,
		h.requestDur,
	}
}

func (h *Handler) initMetrics() {
	const namespace = "http"
	const handlerSubsystem = "api"

	h.requests = prometheus.NewCounterVec(prometheus.CounterOpts{
		Namespace: namespace,
		Subsystem: handlerSubsystem,
		Name:      "requests_total",
		Help:      "Number of http requests received",
	}, []string{"handler", "method", "path", "status", "user_agent"})

	h.requestDur = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: namespace,
		Subsystem: handlerSubsystem,
		Name:      "request_duration_seconds",
		Help:      "Time taken to respond to HTTP request",
	}, []string{"handler", "method", "path", "status", "user_agent"})
}

func logEncodingError(log *zap.Logger, r *http.Request, err error) {
	// If we encounter an error while encoding the response to an http request
	// the best thing we can do is log that error, as we may have already written
	// the headers for the http request in question.
	log.Info("Error encoding response",
		zap.String("path", r.URL.Path),
		zap.String("method", r.Method),
		zap.Error(err))
}
<|endoftext|>


<commit_before>package httpcli

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/mesos/mesos-go/encoding"
)

var (
	// ErrNotLeader is returned by Do calls that are sent to a non leading Mesos master.
	ErrNotLeader = errors.New("scheduler: call sent to a non-leading master")
	// ErrAuth is returned by Do calls that are not successfully authenticated.
	ErrAuth = errors.New("scheduler: call not authenticated")
	// ErrUnsubscribed is returned by Do calls that are sent before a subscription is established.
	ErrUnsubscribed = errors.New("scheduler: no subscription established")
	// ErrVersion is returned by Do calls that are sent to an	incompatible API version.
	ErrVersion = errors.New("scheduler: incompatible API version")
	// ErrMalformed is returned by Do calls that are malformed.
	ErrMalformed = errors.New("scheduler: malformed request")
	// ErrMediaType is returned by Do calls that are sent with an unsupported media type.
	ErrMediaType = errors.New("scheduler: unsupported media type")
	// ErrRateLimit is returned by Do calls that are rate limited.
	ErrRateLimit = errors.New("scheduler: rate limited")

	// codeErrors maps HTTP response codes to their respective errors.
	codeErrors = map[int]error{
		http.StatusOK:                nil,
		http.StatusAccepted:          nil,
		http.StatusTemporaryRedirect: ErrNotLeader,
		http.StatusBadRequest:        ErrMalformed,
		http.StatusConflict:          ErrVersion,
		http.StatusForbidden:         ErrUnsubscribed,
		http.StatusUnauthorized:      ErrAuth,
		http.StatusNotAcceptable:     ErrMediaType,
		429: ErrRateLimit,
	}
)

// A Client is a Mesos HTTP APIs client.
type Client struct {
	url   *url.URL
	cli   *http.Client
	hdr   http.Header
	codec encoding.Codec
}

// Opt defines a functional option for the HTTP client type.
type Opt func(*Client)

// With applies the given Opts to a Client and returns itself.
func (c *Client) With(opts ...Opt) *Client {
	for _, opt := range opts {
		opt(c)
	}
	return c
}

// Do sends a Call and returns a streaming Decoder from which callers can read
// Events from, an io.Closer to close the event stream on graceful termination
// and an error in case of failure.
func (c *Client) Do(m encoding.Marshaler) (encoding.Decoder, io.Closer, error) {
	var body bytes.Buffer
	if err := c.codec.NewEncoder(&body).Encode(m); err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("POST", c.url.String(), &body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Content-Type", c.codec.MediaTypes[0])
	req.Header.Set("Accept", c.codec.MediaTypes[1])

	res, err := c.cli.Do(req)
	if err != nil {
		return nil, nil, err
	}

	return c.codec.NewDecoder(res.Body), res.Body, codeErrors[res.StatusCode]
}

// URL returns an Opt that sets a Client's URL.
func URL(u *url.URL) Opt { return func(c *Client) { c.url = u } }

// RoundTripper returns an Opt that sets a Client's http.RoundTripper.
func RoundTripper(rt http.RoundTripper) Opt {
	return func(c *Client) { c.cli.Transport = rt }
}

// Timeout returns an Opt that sets a Client's timeout.
func Timeout(d time.Duration) Opt { return func(c *Client) { c.cli.Timeout = d } }

// Codec returns an Opt that sets a Client's Codec.
func Codec(codec encoding.Codec) Opt { return func(c *Client) { c.codec = codec } }

// Header returns an Opt that adds a header to an Client's headers.
func Header(k, v string) Opt { return func(c *Client) { c.hdr.Add(k, v) } }
<commit_msg>httpcli: Add New method<commit_after>package httpcli

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/mesos/mesos-go/encoding"
)

var (
	// ErrNotLeader is returned by Do calls that are sent to a non leading Mesos master.
	ErrNotLeader = errors.New("scheduler: call sent to a non-leading master")
	// ErrAuth is returned by Do calls that are not successfully authenticated.
	ErrAuth = errors.New("scheduler: call not authenticated")
	// ErrUnsubscribed is returned by Do calls that are sent before a subscription is established.
	ErrUnsubscribed = errors.New("scheduler: no subscription established")
	// ErrVersion is returned by Do calls that are sent to an	incompatible API version.
	ErrVersion = errors.New("scheduler: incompatible API version")
	// ErrMalformed is returned by Do calls that are malformed.
	ErrMalformed = errors.New("scheduler: malformed request")
	// ErrMediaType is returned by Do calls that are sent with an unsupported media type.
	ErrMediaType = errors.New("scheduler: unsupported media type")
	// ErrRateLimit is returned by Do calls that are rate limited.
	ErrRateLimit = errors.New("scheduler: rate limited")

	// codeErrors maps HTTP response codes to their respective errors.
	codeErrors = map[int]error{
		http.StatusOK:                nil,
		http.StatusAccepted:          nil,
		http.StatusTemporaryRedirect: ErrNotLeader,
		http.StatusBadRequest:        ErrMalformed,
		http.StatusConflict:          ErrVersion,
		http.StatusForbidden:         ErrUnsubscribed,
		http.StatusUnauthorized:      ErrAuth,
		http.StatusNotAcceptable:     ErrMediaType,
		429: ErrRateLimit,
	}
)

// A Client is a Mesos HTTP APIs client.
type Client struct {
	url   *url.URL
	cli   *http.Client
	hdr   http.Header
	codec encoding.Codec
}

// New returns a new Client with the given Opts applied.
func New(opts ...Opt) *Client { return new(Client).With(opts...) }

// Opt defines a functional option for the HTTP client type.
type Opt func(*Client)

// With applies the given Opts to a Client and returns itself.
func (c *Client) With(opts ...Opt) *Client {
	for _, opt := range opts {
		opt(c)
	}
	return c
}

// Do sends a Call and returns a streaming Decoder from which callers can read
// Events from, an io.Closer to close the event stream on graceful termination
// and an error in case of failure.
func (c *Client) Do(m encoding.Marshaler) (encoding.Decoder, io.Closer, error) {
	var body bytes.Buffer
	if err := c.codec.NewEncoder(&body).Encode(m); err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("POST", c.url.String(), &body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Content-Type", c.codec.MediaTypes[0])
	req.Header.Set("Accept", c.codec.MediaTypes[1])

	res, err := c.cli.Do(req)
	if err != nil {
		return nil, nil, err
	}

	return c.codec.NewDecoder(res.Body), res.Body, codeErrors[res.StatusCode]
}

// URL returns an Opt that sets a Client's URL.
func URL(u *url.URL) Opt { return func(c *Client) { c.url = u } }

// RoundTripper returns an Opt that sets a Client's http.RoundTripper.
func RoundTripper(rt http.RoundTripper) Opt {
	return func(c *Client) { c.cli.Transport = rt }
}

// Timeout returns an Opt that sets a Client's timeout.
func Timeout(d time.Duration) Opt { return func(c *Client) { c.cli.Timeout = d } }

// Codec returns an Opt that sets a Client's Codec.
func Codec(codec encoding.Codec) Opt { return func(c *Client) { c.codec = codec } }

// Header returns an Opt that adds a header to an Client's headers.
func Header(k, v string) Opt { return func(c *Client) { c.hdr.Add(k, v) } }
<|endoftext|>


<commit_before>package hub

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestHubRecive(t *testing.T) {
	l, hub := StartServer()
	defer l.Close()

	// Create new session using ws
	send, recv := Connect("", "http://localhost/2233", "ws://localhost:2233/")

	// Expect an
	input_welcome := recv.Next(TypeWelcome)
	assert.Equal(t, input_welcome.Welcome.Heartbeat, 60000, "Expected a heartbeat setting")
	assert.Equal(t, input_welcome.Welcome.SessionTimeout, 180000, "Expected a heartbeat setting")

	send <- Message{
		Type: TypeSubscribe,
		URI:  "/foo",
		Id:   "myfooack",
	}
	ack := recv.Next(TypeAck)
	assert.Equal(t, ack.Id, "myfooack", "Expected ack")

	hub.Emit(Message{
		Type: TypeMessage,
		URI:  "/foo",
		Data: []string{"Hello world"},
	})

	message := recv.Next(TypeMessage)
	assert.Equal(t, message.Data, []interface{}{"Hello world"}, "Expected hello world")
}

func TestHubReciveInternal(t *testing.T) {
	l, hub := StartServer()
	defer l.Close()

	// Create new session not using ws
	_, send, recv := hub.InternalPipe("")

	// Expect an
	input_welcome := recv.Next(TypeWelcome)
	assert.NotEqual(t, input_welcome.Welcome.Heartbeat, 0, "Expected a heartbeat setting")
	assert.NotEqual(t, input_welcome.Welcome.SessionTimeout, 0, "Expected a heartbeat setting")

	send <- Message{
		Type: TypeSubscribe,
		URI:  "/foo",
		Id:   "myfooack",
	}
	ack := recv.Next(TypeAck)
	assert.Equal(t, ack.Id, "myfooack", "Expected ack")

	hub.Emit(Message{
		Type: TypeMessage,
		URI:  "/foo",
		Data: []string{"Hello world"},
	})

	message := recv.Next(TypeMessage)
	assert.Equal(t, message.Data, []string{"Hello world"}, "Expected hello world")
}
<commit_msg>Fixed test.<commit_after>package hub

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"time"
)

func TestHubRecive(t *testing.T) {
	l, hub := StartServer()
	defer l.Close()

	// Create new session using ws
	send, recv := Connect("", "http://localhost/2233", "ws://localhost:2233/")

	// Expect an
	input_welcome := recv.Next(TypeWelcome)
	assert.Equal(t, input_welcome.Welcome.Heartbeat, time.Duration(60000), "Expected a heartbeat setting")
	assert.Equal(t, input_welcome.Welcome.SessionTimeout, time.Duration(180000), "Expected a heartbeat setting")

	send <- Message{
		Type: TypeSubscribe,
		URI:  "/foo",
		Id:   "myfooack",
	}
	ack := recv.Next(TypeAck)
	assert.Equal(t, ack.Id, "myfooack", "Expected ack")

	hub.Emit(Message{
		Type: TypeMessage,
		URI:  "/foo",
		Data: []string{"Hello world"},
	})

	message := recv.Next(TypeMessage)
	assert.Equal(t, message.Data, []interface{}{"Hello world"}, "Expected hello world")
}

func TestHubReciveInternal(t *testing.T) {
	l, hub := StartServer()
	defer l.Close()

	// Create new session not using ws
	_, send, recv := hub.InternalPipe("")

	// Expect an
	input_welcome := recv.Next(TypeWelcome)
	assert.NotEqual(t, input_welcome.Welcome.Heartbeat, 0, "Expected a heartbeat setting")
	assert.NotEqual(t, input_welcome.Welcome.SessionTimeout, 0, "Expected a heartbeat setting")

	send <- Message{
		Type: TypeSubscribe,
		URI:  "/foo",
		Id:   "myfooack",
	}
	ack := recv.Next(TypeAck)
	assert.Equal(t, ack.Id, "myfooack", "Expected ack")

	hub.Emit(Message{
		Type: TypeMessage,
		URI:  "/foo",
		Data: []string{"Hello world"},
	})

	message := recv.Next(TypeMessage)
	assert.Equal(t, message.Data, []string{"Hello world"}, "Expected hello world")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

var helpDialog = []string{
	"[h] - open this help dialog",
	"[q] - exit ctop",
}

func HelpMenu(g *Grid) {
	m := widgets.NewMenu(helpDialog)
	m.TextFgColor = ui.ColorWhite
	m.BorderLabel = "Help"
	m.BorderFg = ui.ColorCyan
	ui.Render(m)
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()
}

func SortMenu(g *Grid) {
	m := widgets.NewMenu(SortFields)
	m.Selectable = true
	m.TextFgColor = ui.ColorWhite
	m.BorderLabel = "Sort Field"
	m.BorderFg = ui.ColorCyan
	ui.Render(m)
	m.NavigationHandlers()
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		g.containerMap.config.sortField = m.Items[m.CursorPos]
		ui.StopLoop()
	})
	ui.Loop()
}
<commit_msg>add sort to help dialog<commit_after>package main

import (
	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

var helpDialog = []string{
	"[h] - open this help dialog",
	"[s] - select container sort field",
	"[q] - exit ctop",
}

func HelpMenu(g *Grid) {
	m := widgets.NewMenu(helpDialog)
	m.TextFgColor = ui.ColorWhite
	m.BorderLabel = "Help"
	m.BorderFg = ui.ColorCyan
	ui.Render(m)
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()
}

func SortMenu(g *Grid) {
	m := widgets.NewMenu(SortFields)
	m.Selectable = true
	m.TextFgColor = ui.ColorWhite
	m.BorderLabel = "Sort Field"
	m.BorderFg = ui.ColorCyan
	ui.Render(m)
	m.NavigationHandlers()
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		g.containerMap.config.sortField = m.Items[m.CursorPos]
		ui.StopLoop()
	})
	ui.Loop()
}
<|endoftext|>


<commit_before>package gopx

import (
	"errors"
	"sort"
	"time"
)

func (p Points) Len() int {
	return len(p)
}

func (p Points) Less(i, j int) bool {
	t1, _ := time.Parse("", p[i].Timestamp)
	t2, _ := time.Parse("", p[j].Timestamp)

	return t1.Before(t2)
}

func (p Points) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// MergeByTimeStamp merges gpxs based on there timetamp
func MergeByTimeStamp(gpxs []*Gpx) (*Gpx, error) {
	points, err := getPoints(gpxs)
	if err != nil {
		return nil, err
	}

	sort.Sort(points)

	return points.createGpx("merged"), nil
}

// MergeByDistance merges gpxs based on distance
func MergeByDistance(gpxs []*Gpx, start Point) (*Gpx, error) {
	points, err := getPoints(gpxs)
	if err != nil {
		return nil, err
	}

	currentPoint := start
	var sorted Points
	for len(points) > 0 {
		closest := points[0]
		closestIndex := 0
		closestDist := currentPoint.Distance(closest)

		// Find the next closest point
		for i, p := range points {
			pDist := currentPoint.Distance(p)
			if pDist < closestDist {
				closest = p
				closestIndex = i
				closestDist = pDist
			}
		}

		points = append(points[:closestIndex], points[closestIndex+1:]...)
		sorted = append(sorted, closest)
		currentPoint = closest
	}

	return sorted.createGpx("merged"), nil
}

// getPoints get points from gpxs.
func getPoints(gpxs []*Gpx) (Points, error) {
	if len(gpxs) == 0 {
		return nil, errors.New("No gpxs to merge")
	}

	var points Points
	for _, gpx := range gpxs {
		points = append(points, gpx.GetPoints()...)
	}

	if len(points) == 0 {
		return nil, errors.New("No Points found to merge")
	}
}
<commit_msg>Fixed return statement<commit_after>package gopx

import (
	"errors"
	"sort"
	"time"
)

func (p Points) Len() int {
	return len(p)
}

func (p Points) Less(i, j int) bool {
	t1, _ := time.Parse("", p[i].Timestamp)
	t2, _ := time.Parse("", p[j].Timestamp)

	return t1.Before(t2)
}

func (p Points) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// MergeByTimeStamp merges gpxs based on there timetamp
func MergeByTimeStamp(gpxs []*Gpx) (*Gpx, error) {
	points, err := getPoints(gpxs)
	if err != nil {
		return nil, err
	}

	sort.Sort(points)

	return points.createGpx("merged"), nil
}

// MergeByDistance merges gpxs based on distance
func MergeByDistance(gpxs []*Gpx, start Point) (*Gpx, error) {
	points, err := getPoints(gpxs)
	if err != nil {
		return nil, err
	}

	currentPoint := start
	var sorted Points
	for len(points) > 0 {
		closest := points[0]
		closestIndex := 0
		closestDist := currentPoint.Distance(closest)

		// Find the next closest point
		for i, p := range points {
			pDist := currentPoint.Distance(p)
			if pDist < closestDist {
				closest = p
				closestIndex = i
				closestDist = pDist
			}
		}

		points = append(points[:closestIndex], points[closestIndex+1:]...)
		sorted = append(sorted, closest)
		currentPoint = closest
	}

	return sorted.createGpx("merged"), nil
}

// getPoints get points from gpxs.
func getPoints(gpxs []*Gpx) (Points, error) {
	if len(gpxs) == 0 {
		return nil, errors.New("No gpxs to merge")
	}

	var points Points
	for _, gpx := range gpxs {
		points = append(points, gpx.GetPoints()...)
	}

	if len(points) == 0 {
		return nil, errors.New("No Points found to merge")
	}

	return points, nil
}
<|endoftext|>


<commit_before>package sha1

/*
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "openssl/sha.h"
#cgo pkg-config: openssl
*/
import "C"
import "unsafe"
import "hash"

//various interface values copied to match crypro/sha1 interface.

const BlockSize = 64
const Size = 20

type SHA1Hash struct {
	sha C.SHA_CTX
}

// New returns a new sha1 hash.Hash
func New() hash.Hash {
	h := new(SHA1Hash)
	if C.SHA1_Init(&h.sha) != 1 {
		panic("problem creating hash")
	}
	return h
}

func (self *SHA1Hash) Write(msg []byte) (n int, err error) {
	size := C.size_t(len(msg))
	if C.SHA1_Update(&self.sha, unsafe.Pointer(C.CString(string(msg))), size) != 1 {
		panic("problem updating hash")
	}
	return len(msg), nil
}
func (self *SHA1Hash) BlockSize() int {
	return C.SHA_DIGEST_LENGTH
}
func (self *SHA1Hash) Size() int {
	return C.SHA_DIGEST_LENGTH
}
func (self *SHA1Hash) Reset() {
	C.SHA1_Init(&self.sha)
}
func (self *SHA1Hash) Sum(b []byte) []byte {
	digest := make([]C.uchar, self.Size())
	// make a copy of the pointer, so our context does not get freed.
	// this allows further writes.
	// TODO perhaps we should think about runtime.SetFinalizer to free the context?
	s_tmp := C.SHA_CTX(self.sha)
	if C.SHA1_Final(&digest[0], &s_tmp) != 1 {
		// TODO maybe not panic here?
		panic("couldn't finalize digest")
	}
	var result []byte
	if b != nil {
		result = make([]byte, 0)
	} else {
		result = b
	}
	for _, value := range digest {
		result = append(result, byte(value))
	}
	return result
}

func Sum(b []byte) [Size]byte {
	s := New()
	s.Reset()
	s.Write(b)
	var cs [Size]byte
	copy(cs[:], s.Sum(nil))
	return cs
}
<commit_msg>sha1: rework & formatting<commit_after>package sha1

/*
#cgo pkg-config: openssl
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "openssl/sha.h"
*/
import "C"
import (
	"hash"
	"unsafe"

	"github.com/shanemhansen/gossl/sslerr"
)

const (
	// The size of a SHA1 checksum in bytes.
	Size = C.SHA_DIGEST_LENGTH

	// The blocksize of SHA1 in bytes.
	BlockSize = 64
)

type digest struct {
	ctx C.SHA_CTX
}

// New returns a new sha1 hash.Hash
// if the returned hash is nil, then make a call to sslerr.Error() to know
// what went wrong
func New() hash.Hash {
	d := new(digest)
	if C.SHA1_Init(&d.ctx) != 1 {
		return nil
	}
	return d
}

func (d *digest) Reset() {
	C.SHA1_Init(&d.ctx)
}

func (digest *digest) BlockSize() int { return BlockSize }

func (digest *digest) Size() int { return Size }

func (d *digest) Write(msg []byte) (n int, err error) {
	size := C.size_t(len(msg))
	if C.SHA1_Update(&d.ctx, unsafe.Pointer(C.CString(string(msg))), size) != 1 {
		return 0, sslerr.Error()
	}
	return len(msg), nil
}

func (d *digest) Sum(b []byte) []byte {
	buf := make([]C.uchar, d.Size())
	// make a copy of the pointer, so our context does not get freed.
	// this allows further writes.
	// TODO perhaps we should think about runtime.SetFinalizer to free the context?
	ctxTmp := C.SHA_CTX(d.ctx)
	if C.SHA1_Final(&buf[0], &ctxTmp) != 1 {
		return make([]byte, 0)
	}
	var result []byte
	if b != nil {
		result = make([]byte, 0)
	} else {
		result = b
	}
	for _, value := range buf {
		result = append(result, byte(value))
	}
	return result
}

func Sum(b []byte) [Size]byte {
	s := New()
	s.Reset()
	s.Write(b)
	var cs [Size]byte
	copy(cs[:], s.Sum(nil))
	return cs
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec

import (
	"crypto/elliptic"
	"errors"
	"fmt"
	"math/big"
)

type Signature struct {
	R *big.Int
	S *big.Int
}

func ParseSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) {
	// Originally this code used encoding/asn1 in order to parse the
	// signature, but a number of problems were found with this approach.
	// Despite the fact that signatures are stored as DER, the difference
	// between go's idea of a bignum (and that they have sign) doesn't agree
	// with the openssl one (where they do not). The above is true as of
	// Go 1.1. In the end it was simpler to rewrite the code to explicitly
	// understand the format which is this:
	// 0x30 <length of whole message> <0x02> <length of R> <R> 0x2
	// <length of S> <S>.

	signature := &Signature{}

	// minimal message is when both numbers are 1 bytes. adding up to:
	// 0x30 + len + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
	if len(sigStr) < 8 {
		return nil, errors.New("malformed signature: too short")
	}
	// 0x30
	index := 0
	if sigStr[index] != 0x30 {
		return nil, errors.New("malformed signature: no header magic")
	}
	index++
	// length of remaining message
	siglen := sigStr[index]
	if int(siglen+2) > len(sigStr) {
		return nil, errors.New("malformed signature: no header magic")
	}
	index++
	// trim the slice we're working on so we only look at what matters.
	sigStr = sigStr[:siglen+2]

	// 0x02
	if sigStr[index] != 0x02 {
		return nil,
			errors.New("malformed signature: no 1st int marker")
	}
	index++

	// Length of signature R.
	rLen := int(sigStr[index])
	if rLen < 0 || rLen > len(sigStr)-index {
		return nil, errors.New("malformed signature: bogus R length")
	}
	index++

	// Then R itself.
	signature.R = new(big.Int).SetBytes(sigStr[index : index+rLen])
	index += rLen
	// 0x02
	if sigStr[index] != 0x02 {
		return nil, errors.New("malformed signature: no 2nd int marker")
	}
	index++

	// Length of signature S.
	sLen := int(sigStr[index])
	if sLen < 0 || sLen > len(sigStr)-index {
		return nil, errors.New("malformed signature: bogus S length")
	}
	index++

	// Then S itself.
	signature.S = new(big.Int).SetBytes(sigStr[index : index+sLen])
	index += sLen

	// sanity check length parsing
	if index != len(sigStr) {
		return nil, fmt.Errorf("malformed signature: bad final length %v != %v",
			index, len(sigStr))
	}

	// Verify also checks this, but we can be more sure that we parsed
	// correctly if we verify here too.
	// FWIW the ecdsa spec states that R and S must be | 1, N - 1 |
	// but crypto/ecdsa only checks for Sign != 0. Mirror that.
	if signature.R.Sign() != 1 {
		return nil, errors.New("Signature R isn't 1 or more")
	}
	if signature.S.Sign() != 1 {
		return nil, errors.New("Signature S isn't 1 or more")
	}
	if signature.R.Cmp(curve.Params().N) >= 0 {
		return nil, errors.New("Signature R is >= curve.N")
	}
	if signature.S.Cmp(curve.Params().N) >= 0 {
		return nil, errors.New("Signature S is >= curve.N")
	}

	return signature, nil
}
<commit_msg>fix up some indexing in ParseSignature.<commit_after>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec

import (
	"crypto/elliptic"
	"errors"
	"fmt"
	"math/big"
)

type Signature struct {
	R *big.Int
	S *big.Int
}

func ParseSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) {
	// Originally this code used encoding/asn1 in order to parse the
	// signature, but a number of problems were found with this approach.
	// Despite the fact that signatures are stored as DER, the difference
	// between go's idea of a bignum (and that they have sign) doesn't agree
	// with the openssl one (where they do not). The above is true as of
	// Go 1.1. In the end it was simpler to rewrite the code to explicitly
	// understand the format which is this:
	// 0x30 <length of whole message> <0x02> <length of R> <R> 0x2
	// <length of S> <S>.

	signature := &Signature{}

	// minimal message is when both numbers are 1 bytes. adding up to:
	// 0x30 + len + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
	if len(sigStr) < 8 {
		return nil, errors.New("malformed signature: too short")
	}
	// 0x30
	index := 0
	if sigStr[index] != 0x30 {
		return nil, errors.New("malformed signature: no header magic")
	}
	index++
	// length of remaining message
	siglen := sigStr[index]
	if int(siglen+2) > len(sigStr) {
		return nil, errors.New("malformed signature: no header magic")
	}
	index++
	// trim the slice we're working on so we only look at what matters.
	sigStr = sigStr[:siglen+2]

	// 0x02
	if sigStr[index] != 0x02 {
		return nil,
			errors.New("malformed signature: no 1st int marker")
	}
	index++

	// Length of signature R.
	rLen := int(sigStr[index])
	// must be positive, must be able to fit in another 0x2, <len> <s>
	// hence the -3. We assume that the length must be at least one byte.
	index++
	if rLen <= 0 || rLen > len(sigStr)-index-3 {
		return nil, errors.New("malformed signature: bogus R length")
	}

	// Then R itself.
	signature.R = new(big.Int).SetBytes(sigStr[index : index+rLen])
	index += rLen
	// 0x02. length already checked in previous if.
	if sigStr[index] != 0x02 {
		return nil, errors.New("malformed signature: no 2nd int marker")
	}
	index++

	// Length of signature S.
	sLen := int(sigStr[index])
	index++
	// S should be the rest of the string.
	if sLen <= 0 || sLen > len(sigStr)-index {
		return nil, errors.New("malformed signature: bogus S length")
	}

	// Then S itself.
	signature.S = new(big.Int).SetBytes(sigStr[index : index+sLen])
	index += sLen

	// sanity check length parsing
	if index != len(sigStr) {
		return nil, fmt.Errorf("malformed signature: bad final length %v != %v",
			index, len(sigStr))
	}

	// Verify also checks this, but we can be more sure that we parsed
	// correctly if we verify here too.
	// FWIW the ecdsa spec states that R and S must be | 1, N - 1 |
	// but crypto/ecdsa only checks for Sign != 0. Mirror that.
	if signature.R.Sign() != 1 {
		return nil, errors.New("Signature R isn't 1 or more")
	}
	if signature.S.Sign() != 1 {
		return nil, errors.New("Signature S isn't 1 or more")
	}
	if signature.R.Cmp(curve.Params().N) >= 0 {
		return nil, errors.New("Signature R is >= curve.N")
	}
	if signature.S.Cmp(curve.Params().N) >= 0 {
		return nil, errors.New("Signature S is >= curve.N")
	}

	return signature, nil
}
<|endoftext|>