<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"net/url"
	"regexp"
	"sort"
	"strings"
	"unicode"
)

// UnsupportedEndpointError is returned when the Endpoint given was not valid.
type UnsupportedEndpointError Endpoint

func (e UnsupportedEndpointError) Error() string {
	return fmt.Sprintf("%d was not a valid endpoint choice", e)
}

// NoDefaultAccountError is returned when the library couldn't figure out what account to use as a default.
type NoDefaultAccountError struct {
	InnerErr error
}

func (e NoDefaultAccountError) Error() string {
	return "Couldn't find a default BigV account - please set one using `bytemark config set account`, or specify one on the command line using the --account flag or server.group.account or group.acccount notation."
}

// APIError is the basic error type which most errors returned by the client library are subclassed from.
type APIError struct {
	Method       string
	URL          *url.URL
	StatusCode   int
	RequestBody  string
	ResponseBody string
}

func (e APIError) Error() string {
	return fmt.Sprintf("HTTP %s %s returned %d\r\n", e.Method, e.URL.String(), e.StatusCode)
}

// BadNameError is returned when a VirtualMachineName / GroupName or AccountName is invalid.
type BadNameError struct {
	APIError
	Type         string
	ProblemField string
	ProblemValue string
}

func (e BadNameError) Error() string {
	return fmt.Sprintf("Invalid name: '%s' is a bad %s for a %s", e.ProblemValue, e.ProblemField, e.Type)
}

// NotFoundError is returned when an object was unable to be found - either because the caller doesn't have permission to see them or because they don't exist.
type NotFoundError struct {
	APIError
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("404 Not found\r\n%s", e.APIError.Error())
}

// NotAuthorizedError is returned when an action was unable to be performed because the caller doesn't have permission.
type NotAuthorizedError struct {
	APIError
}

func (e NotAuthorizedError) Error() string {
	return fmt.Sprintf("403 Unauthorized\r\n%s", e.APIError.Error())

}

// UnknownStatusCodeError is returned when an action caused API to return a strange status code that the client library wasn't expecting. Perhaps it's a protocol mismatch - try updating to the latest version of the library, otherwise file a bug report.
type UnknownStatusCodeError struct {
	APIError
}

func (e UnknownStatusCodeError) Error() string {
	return fmt.Sprintf("An unexpected status code happened (report this as a bug!)\r\n%s", e.APIError.Error())
}

// BadRequestError is returned when a request was malformed.
type BadRequestError struct {
	APIError
	Problems map[string][]string
}

// friendlifyBadRequestPhrases makes the brain's validation messages
// a bit more friendly. De-abbreviates, de-jargonises and removes redundancy
// (no need to say something isn't a number if you're also saying it wasn't set)
func friendlifyBadRequestPhrases(phrases []string) (newPhrases []string) {
	replacer := strings.NewReplacer(
		"can't", "cannot",
		"doesn't", "does not",
	)
	missingParamRE := regexp.MustCompile("^Missing [a-zA-Z_]+ parameter$")

	newPhrases = make([]string, 0, len(phrases))

	found := make(map[string]bool)
	for _, p := range phrases {
		found[p] = true
	}
	for _, p := range phrases {
		switch p {
		case "is not included in the list":
			newPhrases = append(newPhrases, "is invalid")
		case "is not a number":
			if !found["is not included in the list"] {
				newPhrases = append(newPhrases, replacer.Replace(p))
			}
		case "is invalid":
			if len(phrases) == 0 {
				newPhrases = append(newPhrases, replacer.Replace(p))
			}
		default:
			if found["can't be blank"] && p != "can't be blank" {
				continue
			}
			if missingParamRE.MatchString(p) {
				newPhrases = append(newPhrases, "was not specified")
				continue
			}

			newPhrases = append(newPhrases, replacer.Replace(p))
		}
	}
	return
}

func unmarshalStringOrStringSlice(data json.RawMessage) (thoseProblems []string, err error) {
	thoseProblems = make([]string, 0, 1)

	if data[0] == '"' {
		var str string
		err = json.Unmarshal(data, &str)
		if err != nil {
			return
		}
		thoseProblems = append(thoseProblems, str)
	} else {
		err = json.Unmarshal(data, &thoseProblems)
		if err != nil {
			return
		}
	}
	return
}

func newBadRequestError(ctx APIError, response []byte) error {
	problems := make(map[string][]string)
	jsonProblems := make(map[string]json.RawMessage)
	err := json.Unmarshal(response, &jsonProblems)
	if err != nil {
		log.Debug(log.LvlOutline, "Couldn't parse 400 response into JSON, so bunging it into a single Problem in the BadRequestError")
		bytes, _ := json.Marshal([]string{string(response)})
		jsonProblems[""] = bytes
	}
	for t, data := range jsonProblems {
		switch t {
		case "discs":
			discProblems := make([]map[string][]string, 0, 1)
			err = json.Unmarshal(data, &discProblems)
			if err != nil {
				return err
			}
			problems["disc"] = make([]string, 0)
			for i, thisDiscProbs := range discProblems {
				for field, fieldProbs := range thisDiscProbs {
					fieldProbs = friendlifyBadRequestPhrases(fieldProbs)
					for _, p := range fieldProbs {
						problems["disc"] = append(problems[t], fmt.Sprintf("%d - %s %s", i+1, field, p))
					}
				}
			}
		case "memory":
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems["memory_amount"] = friendlifyBadRequestPhrases(thoseProblems)
		case "interval_seconds":
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems["interval"] = friendlifyBadRequestPhrases(thoseProblems)
		default:
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems[t] = friendlifyBadRequestPhrases(thoseProblems)
		}
	}
	return BadRequestError{
		ctx,
		problems}
}

func capitaliseJSON(s string) string {
	rs := []rune(s)
	rs[0] = unicode.ToUpper(rs[0])
	s = string(rs)
	return strings.Replace(s, "_", " ", -1)
}

func (e BadRequestError) Error() string {
	if len(e.Problems) == 0 {
		return fmt.Sprintf("The request was bad:\r\n%s", e.ResponseBody)
	}
	out := make([]string, 0, len(e.Problems))
	keys := make([]string, len(e.Problems))
	for field := range e.Problems {
		keys = append(keys, field)
	}
	sort.Strings(keys)
	for _, field := range keys {
		probs := e.Problems[field]
		for _, p := range probs {
			out = append(out, "• "+capitaliseJSON(field)+" "+p)

		}
	}

	return strings.Join(out, "\r\n")
}

// InternalServerError is returned when the endpoint responds with an HTTP 500 Internal Server Error.
type InternalServerError struct {
	APIError
}

func (e InternalServerError) Error() string {
	out := []string{"The API server returned an error"}
	if e.ResponseBody != "" {
		out = append(out, e.ResponseBody)
	}
	return strings.Join(out, "\r\n")
}

// ServiceUnavailableError is returned by anything that makes an HTTP request resulting in a 503
type ServiceUnavailableError struct {
	APIError
}

func (e ServiceUnavailableError) Error() string {
	return fmt.Sprintf("Bytemark's API seems to be temporarily unavailable - give it another go in a few seconds, or check on http://status.bytemark.org to see if parts of the API are currently known to be down")
}

// NilAuthError is returned when a call attempts to add authentication headers to the request, but the Client.AuthSession is nil. This is always a bug as it's an issue with the code and not with anything external.
type NilAuthError struct {
	APIError
}

func (e NilAuthError) Error() string {
	return fmt.Sprintf("Authorisation wasn't set up.")
}

// AmbiguousKeyError is returned when a call to DeleteUserAuthorizedKey has an insufficiently unique
type AmbiguousKeyError struct {
	APIError
}

func (e AmbiguousKeyError) Error() string {
	return fmt.Sprint("The specified key was ambiguous - please specify the full key")
}

// AccountCreationDeferredError is returned when we get a particular response from bmbilling.
type AccountCreationDeferredError struct{}

func (e AccountCreationDeferredError) Error() string {
	return fmt.Sprintf("Account creation request accepted\r\n\r\nYour account requires a manual check, which shouldn't take long. We'll send an email when your account is ready.")
}
<commit_msg>Fix small bug in capitaliseJSON<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"net/url"
	"regexp"
	"sort"
	"strings"
	"unicode"
)

// UnsupportedEndpointError is returned when the Endpoint given was not valid.
type UnsupportedEndpointError Endpoint

func (e UnsupportedEndpointError) Error() string {
	return fmt.Sprintf("%d was not a valid endpoint choice", e)
}

// NoDefaultAccountError is returned when the library couldn't figure out what account to use as a default.
type NoDefaultAccountError struct {
	InnerErr error
}

func (e NoDefaultAccountError) Error() string {
	return "Couldn't find a default BigV account - please set one using `bytemark config set account`, or specify one on the command line using the --account flag or server.group.account or group.acccount notation."
}

// APIError is the basic error type which most errors returned by the client library are subclassed from.
type APIError struct {
	Method       string
	URL          *url.URL
	StatusCode   int
	RequestBody  string
	ResponseBody string
}

func (e APIError) Error() string {
	return fmt.Sprintf("HTTP %s %s returned %d\r\n", e.Method, e.URL.String(), e.StatusCode)
}

// BadNameError is returned when a VirtualMachineName / GroupName or AccountName is invalid.
type BadNameError struct {
	APIError
	Type         string
	ProblemField string
	ProblemValue string
}

func (e BadNameError) Error() string {
	return fmt.Sprintf("Invalid name: '%s' is a bad %s for a %s", e.ProblemValue, e.ProblemField, e.Type)
}

// NotFoundError is returned when an object was unable to be found - either because the caller doesn't have permission to see them or because they don't exist.
type NotFoundError struct {
	APIError
}

func (e NotFoundError) Error() string {
	return fmt.Sprintf("404 Not found\r\n%s", e.APIError.Error())
}

// NotAuthorizedError is returned when an action was unable to be performed because the caller doesn't have permission.
type NotAuthorizedError struct {
	APIError
}

func (e NotAuthorizedError) Error() string {
	return fmt.Sprintf("403 Unauthorized\r\n%s", e.APIError.Error())

}

// UnknownStatusCodeError is returned when an action caused API to return a strange status code that the client library wasn't expecting. Perhaps it's a protocol mismatch - try updating to the latest version of the library, otherwise file a bug report.
type UnknownStatusCodeError struct {
	APIError
}

func (e UnknownStatusCodeError) Error() string {
	return fmt.Sprintf("An unexpected status code happened (report this as a bug!)\r\n%s", e.APIError.Error())
}

// BadRequestError is returned when a request was malformed.
type BadRequestError struct {
	APIError
	Problems map[string][]string
}

// friendlifyBadRequestPhrases makes the brain's validation messages
// a bit more friendly. De-abbreviates, de-jargonises and removes redundancy
// (no need to say something isn't a number if you're also saying it wasn't set)
func friendlifyBadRequestPhrases(phrases []string) (newPhrases []string) {
	replacer := strings.NewReplacer(
		"can't", "cannot",
		"doesn't", "does not",
	)
	missingParamRE := regexp.MustCompile("^Missing [a-zA-Z_]+ parameter$")

	newPhrases = make([]string, 0, len(phrases))

	found := make(map[string]bool)
	for _, p := range phrases {
		found[p] = true
	}
	for _, p := range phrases {
		switch p {
		case "is not included in the list":
			newPhrases = append(newPhrases, "is invalid")
		case "is not a number":
			if !found["is not included in the list"] {
				newPhrases = append(newPhrases, replacer.Replace(p))
			}
		case "is invalid":
			if len(phrases) == 0 {
				newPhrases = append(newPhrases, replacer.Replace(p))
			}
		default:
			if found["can't be blank"] && p != "can't be blank" {
				continue
			}
			if missingParamRE.MatchString(p) {
				newPhrases = append(newPhrases, "was not specified")
				continue
			}

			newPhrases = append(newPhrases, replacer.Replace(p))
		}
	}
	return
}

func unmarshalStringOrStringSlice(data json.RawMessage) (thoseProblems []string, err error) {
	thoseProblems = make([]string, 0, 1)

	if data[0] == '"' {
		var str string
		err = json.Unmarshal(data, &str)
		if err != nil {
			return
		}
		thoseProblems = append(thoseProblems, str)
	} else {
		err = json.Unmarshal(data, &thoseProblems)
		if err != nil {
			return
		}
	}
	return
}

func newBadRequestError(ctx APIError, response []byte) error {
	problems := make(map[string][]string)
	jsonProblems := make(map[string]json.RawMessage)
	err := json.Unmarshal(response, &jsonProblems)
	if err != nil {
		log.Debug(log.LvlOutline, "Couldn't parse 400 response into JSON, so bunging it into a single Problem in the BadRequestError")
		bytes, _ := json.Marshal([]string{string(response)})
		jsonProblems[""] = bytes
	}
	for t, data := range jsonProblems {
		switch t {
		case "discs":
			discProblems := make([]map[string][]string, 0, 1)
			err = json.Unmarshal(data, &discProblems)
			if err != nil {
				return err
			}
			problems["disc"] = make([]string, 0)
			for i, thisDiscProbs := range discProblems {
				for field, fieldProbs := range thisDiscProbs {
					fieldProbs = friendlifyBadRequestPhrases(fieldProbs)
					for _, p := range fieldProbs {
						problems["disc"] = append(problems[t], fmt.Sprintf("%d - %s %s", i+1, field, p))
					}
				}
			}
		case "memory":
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems["memory_amount"] = friendlifyBadRequestPhrases(thoseProblems)
		case "interval_seconds":
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems["interval"] = friendlifyBadRequestPhrases(thoseProblems)
		default:
			thoseProblems, err := unmarshalStringOrStringSlice(data)
			if err != nil {
				return err
			}
			problems[t] = friendlifyBadRequestPhrases(thoseProblems)
		}
	}
	return BadRequestError{
		ctx,
		problems}
}

func capitaliseJSON(s string) string {
	rs := []rune(s)
	if len(rs) > 0 {
		rs[0] = unicode.ToUpper(rs[0])
	}
	s = string(rs)
	return strings.Replace(s, "_", " ", -1)
}

func (e BadRequestError) Error() string {
	if len(e.Problems) == 0 {
		return fmt.Sprintf("The request was bad:\r\n%s", e.ResponseBody)
	}
	out := make([]string, 0, len(e.Problems))
	keys := make([]string, len(e.Problems))
	for field := range e.Problems {
		keys = append(keys, field)
	}
	sort.Strings(keys)
	for _, field := range keys {
		probs := e.Problems[field]
		for _, p := range probs {
			out = append(out, "• "+capitaliseJSON(field)+" "+p)

		}
	}

	return strings.Join(out, "\r\n")
}

// InternalServerError is returned when the endpoint responds with an HTTP 500 Internal Server Error.
type InternalServerError struct {
	APIError
}

func (e InternalServerError) Error() string {
	out := []string{"The API server returned an error"}
	if e.ResponseBody != "" {
		out = append(out, e.ResponseBody)
	}
	return strings.Join(out, "\r\n")
}

// ServiceUnavailableError is returned by anything that makes an HTTP request resulting in a 503
type ServiceUnavailableError struct {
	APIError
}

func (e ServiceUnavailableError) Error() string {
	return fmt.Sprintf("Bytemark's API seems to be temporarily unavailable - give it another go in a few seconds, or check on http://status.bytemark.org to see if parts of the API are currently known to be down")
}

// NilAuthError is returned when a call attempts to add authentication headers to the request, but the Client.AuthSession is nil. This is always a bug as it's an issue with the code and not with anything external.
type NilAuthError struct {
	APIError
}

func (e NilAuthError) Error() string {
	return fmt.Sprintf("Authorisation wasn't set up.")
}

// AmbiguousKeyError is returned when a call to DeleteUserAuthorizedKey has an insufficiently unique
type AmbiguousKeyError struct {
	APIError
}

func (e AmbiguousKeyError) Error() string {
	return fmt.Sprint("The specified key was ambiguous - please specify the full key")
}

// AccountCreationDeferredError is returned when we get a particular response from bmbilling.
type AccountCreationDeferredError struct{}

func (e AccountCreationDeferredError) Error() string {
	return fmt.Sprintf("Account creation request accepted\r\n\r\nYour account requires a manual check, which shouldn't take long. We'll send an email when your account is ready.")
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package gnmi

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"path"
	"strconv"
	"strings"

	pb "github.com/openconfig/gnmi/proto/gnmi"
	"google.golang.org/grpc/codes"
)

// Get sents a GetRequest to the given client.
func Get(ctx context.Context, client pb.GNMIClient, paths [][]string) error {
	req, err := NewGetRequest(paths)
	if err != nil {
		return err
	}
	resp, err := client.Get(ctx, req)
	if err != nil {
		return err
	}
	for _, notif := range resp.Notification {
		for _, update := range notif.Update {
			fmt.Printf("%s:\n", StrPath(update.Path))
			fmt.Println(StrUpdateVal(update))
		}
	}
	return nil
}

// Capabilities retuns the capabilities of the client.
func Capabilities(ctx context.Context, client pb.GNMIClient) error {
	resp, err := client.Capabilities(ctx, &pb.CapabilityRequest{})
	if err != nil {
		return err
	}
	fmt.Printf("Version: %s\n", resp.GNMIVersion)
	for _, mod := range resp.SupportedModels {
		fmt.Printf("SupportedModel: %s\n", mod)
	}
	for _, enc := range resp.SupportedEncodings {
		fmt.Printf("SupportedEncoding: %s\n", enc)
	}
	return nil
}

// val may be a path to a file or it may be json. First see if it is a
// file, if so return its contents, otherwise return val
func extractJSON(val string) []byte {
	if jsonBytes, err := ioutil.ReadFile(val); err == nil {
		return jsonBytes
	}
	// Best effort check if the value might a string literal, in which
	// case wrap it in quotes. This is to allow a user to do:
	//   gnmi update ../hostname host1234
	//   gnmi update ../description 'This is a description'
	// instead of forcing them to quote the string:
	//   gnmi update ../hostname '"host1234"'
	//   gnmi update ../description '"This is a description"'
	maybeUnquotedStringLiteral := func(s string) bool {
		if s == "true" || s == "false" || s == "null" || // JSON reserved words
			strings.ContainsAny(s, `"'{}[]`) { // Already quoted or is a JSON object or array
			return false
		} else if _, err := strconv.ParseInt(s, 0, 32); err == nil {
			// Integer. Using byte size of 32 because larger integer
			// types are supposed to be sent as strings in JSON.
			return false
		} else if _, err := strconv.ParseFloat(s, 64); err == nil {
			// Float
			return false
		}

		return true
	}
	if maybeUnquotedStringLiteral(val) {
		out := make([]byte, len(val)+2)
		out[0] = '"'
		copy(out[1:], val)
		out[len(out)-1] = '"'
		return out
	}
	return []byte(val)
}

// StrUpdateVal will return a string representing the value within the supplied update
func StrUpdateVal(u *pb.Update) string {
	if u.Value != nil {
		// Backwards compatibility with pre-v0.4 gnmi
		switch u.Value.Type {
		case pb.Encoding_JSON, pb.Encoding_JSON_IETF:
			return strJSON(u.Value.Value)
		case pb.Encoding_BYTES, pb.Encoding_PROTO:
			return base64.StdEncoding.EncodeToString(u.Value.Value)
		case pb.Encoding_ASCII:
			return string(u.Value.Value)
		default:
			return string(u.Value.Value)
		}
	}
	return StrVal(u.Val)
}

// StrVal will return a string representing the supplied value
func StrVal(val *pb.TypedValue) string {
	switch v := val.GetValue().(type) {
	case *pb.TypedValue_StringVal:
		return v.StringVal
	case *pb.TypedValue_JsonIetfVal:
		return strJSON(v.JsonIetfVal)
	case *pb.TypedValue_JsonVal:
		return strJSON(v.JsonVal)
	case *pb.TypedValue_IntVal:
		return strconv.FormatInt(v.IntVal, 10)
	case *pb.TypedValue_UintVal:
		return strconv.FormatUint(v.UintVal, 10)
	case *pb.TypedValue_BoolVal:
		return strconv.FormatBool(v.BoolVal)
	case *pb.TypedValue_BytesVal:
		return base64.StdEncoding.EncodeToString(v.BytesVal)
	case *pb.TypedValue_DecimalVal:
		return strDecimal64(v.DecimalVal)
	case *pb.TypedValue_FloatVal:
		return strconv.FormatFloat(float64(v.FloatVal), 'g', -1, 32)
	case *pb.TypedValue_LeaflistVal:
		return strLeaflist(v.LeaflistVal)
	case *pb.TypedValue_AsciiVal:
		return v.AsciiVal
	case *pb.TypedValue_AnyVal:
		return v.AnyVal.String()
	default:
		panic(v)
	}
}

func strJSON(inJSON []byte) string {
	var out bytes.Buffer
	err := json.Indent(&out, inJSON, "", "  ")
	if err != nil {
		return fmt.Sprintf("(error unmarshalling json: %s)\n", err) + string(inJSON)
	}
	return out.String()
}

func strDecimal64(d *pb.Decimal64) string {
	var i, frac int64
	if d.Precision > 0 {
		div := int64(10)
		it := d.Precision - 1
		for it > 0 {
			div *= 10
			it--
		}
		i = d.Digits / div
		frac = d.Digits % div
	} else {
		i = d.Digits
	}
	if frac < 0 {
		frac = -frac
	}
	return fmt.Sprintf("%d.%d", i, frac)
}

// strLeafList builds a human-readable form of a leaf-list. e.g. [1, 2, 3] or [a, b, c]
func strLeaflist(v *pb.ScalarArray) string {
	var buf bytes.Buffer
	buf.WriteByte('[')

	for i, elm := range v.Element {
		buf.WriteString(StrVal(elm))
		if i < len(v.Element)-1 {
			buf.WriteString(", ")
		}
	}

	buf.WriteByte(']')
	return buf.String()
}

func update(p *pb.Path, val string) *pb.Update {
	var v *pb.TypedValue
	switch p.Origin {
	case "":
		v = &pb.TypedValue{
			Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: extractJSON(val)}}
	case "cli":
		v = &pb.TypedValue{
			Value: &pb.TypedValue_AsciiVal{AsciiVal: val}}
	default:
		panic(fmt.Errorf("unexpected origin: %q", p.Origin))
	}

	return &pb.Update{Path: p, Val: v}
}

// Operation describes an gNMI operation.
type Operation struct {
	Type string
	Path []string
	Val  string
}

func newSetRequest(setOps []*Operation) (*pb.SetRequest, error) {
	req := &pb.SetRequest{}
	for _, op := range setOps {
		p, err := ParseGNMIElements(op.Path)
		if err != nil {
			return nil, err
		}

		switch op.Type {
		case "delete":
			req.Delete = append(req.Delete, p)
		case "update":
			req.Update = append(req.Update, update(p, op.Val))
		case "replace":
			req.Replace = append(req.Replace, update(p, op.Val))
		}
	}
	return req, nil
}

// Set sends a SetRequest to the given client.
func Set(ctx context.Context, client pb.GNMIClient, setOps []*Operation) error {
	req, err := newSetRequest(setOps)
	if err != nil {
		return err
	}
	resp, err := client.Set(ctx, req)
	if err != nil {
		return err
	}
	if resp.Message != nil && codes.Code(resp.Message.Code) != codes.OK {
		return errors.New(resp.Message.Message)
	}
	// TODO: Iterate over SetResponse.Response for more detailed error message?

	return nil
}

// Subscribe sends a SubscribeRequest to the given client.
func Subscribe(ctx context.Context, client pb.GNMIClient, paths [][]string,
	respChan chan<- *pb.SubscribeResponse, errChan chan<- error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	stream, err := client.Subscribe(ctx)
	if err != nil {
		errChan <- err
		return
	}
	req, err := NewSubscribeRequest(paths)
	if err != nil {
		errChan <- err
		return
	}
	if err := stream.Send(req); err != nil {
		errChan <- err
		return
	}

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				return
			}
			errChan <- err
			return
		}
		respChan <- resp
	}
}

// LogSubscribeResponse logs update responses to stderr.
func LogSubscribeResponse(response *pb.SubscribeResponse) error {
	switch resp := response.Response.(type) {
	case *pb.SubscribeResponse_Error:
		return errors.New(resp.Error.Message)
	case *pb.SubscribeResponse_SyncResponse:
		if !resp.SyncResponse {
			return errors.New("initial sync failed")
		}
	case *pb.SubscribeResponse_Update:
		prefix := StrPath(resp.Update.Prefix)
		for _, update := range resp.Update.Update {
			fmt.Printf("%s = %s\n", path.Join(prefix, StrPath(update.Path)),
				StrUpdateVal(update))
		}
	}
	return nil
}
<commit_msg>gnmi: Append prefix to path in Get responses<commit_after>// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package gnmi

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"path"
	"strconv"
	"strings"

	pb "github.com/openconfig/gnmi/proto/gnmi"
	"google.golang.org/grpc/codes"
)

// Get sents a GetRequest to the given client.
func Get(ctx context.Context, client pb.GNMIClient, paths [][]string) error {
	req, err := NewGetRequest(paths)
	if err != nil {
		return err
	}
	resp, err := client.Get(ctx, req)
	if err != nil {
		return err
	}
	for _, notif := range resp.Notification {
		prefix := StrPath(notif.Prefix)
		for _, update := range notif.Update {
			fmt.Printf("%s:\n", path.Join(prefix, StrPath(update.Path)))
			fmt.Println(StrUpdateVal(update))
		}
	}
	return nil
}

// Capabilities retuns the capabilities of the client.
func Capabilities(ctx context.Context, client pb.GNMIClient) error {
	resp, err := client.Capabilities(ctx, &pb.CapabilityRequest{})
	if err != nil {
		return err
	}
	fmt.Printf("Version: %s\n", resp.GNMIVersion)
	for _, mod := range resp.SupportedModels {
		fmt.Printf("SupportedModel: %s\n", mod)
	}
	for _, enc := range resp.SupportedEncodings {
		fmt.Printf("SupportedEncoding: %s\n", enc)
	}
	return nil
}

// val may be a path to a file or it may be json. First see if it is a
// file, if so return its contents, otherwise return val
func extractJSON(val string) []byte {
	if jsonBytes, err := ioutil.ReadFile(val); err == nil {
		return jsonBytes
	}
	// Best effort check if the value might a string literal, in which
	// case wrap it in quotes. This is to allow a user to do:
	//   gnmi update ../hostname host1234
	//   gnmi update ../description 'This is a description'
	// instead of forcing them to quote the string:
	//   gnmi update ../hostname '"host1234"'
	//   gnmi update ../description '"This is a description"'
	maybeUnquotedStringLiteral := func(s string) bool {
		if s == "true" || s == "false" || s == "null" || // JSON reserved words
			strings.ContainsAny(s, `"'{}[]`) { // Already quoted or is a JSON object or array
			return false
		} else if _, err := strconv.ParseInt(s, 0, 32); err == nil {
			// Integer. Using byte size of 32 because larger integer
			// types are supposed to be sent as strings in JSON.
			return false
		} else if _, err := strconv.ParseFloat(s, 64); err == nil {
			// Float
			return false
		}

		return true
	}
	if maybeUnquotedStringLiteral(val) {
		out := make([]byte, len(val)+2)
		out[0] = '"'
		copy(out[1:], val)
		out[len(out)-1] = '"'
		return out
	}
	return []byte(val)
}

// StrUpdateVal will return a string representing the value within the supplied update
func StrUpdateVal(u *pb.Update) string {
	if u.Value != nil {
		// Backwards compatibility with pre-v0.4 gnmi
		switch u.Value.Type {
		case pb.Encoding_JSON, pb.Encoding_JSON_IETF:
			return strJSON(u.Value.Value)
		case pb.Encoding_BYTES, pb.Encoding_PROTO:
			return base64.StdEncoding.EncodeToString(u.Value.Value)
		case pb.Encoding_ASCII:
			return string(u.Value.Value)
		default:
			return string(u.Value.Value)
		}
	}
	return StrVal(u.Val)
}

// StrVal will return a string representing the supplied value
func StrVal(val *pb.TypedValue) string {
	switch v := val.GetValue().(type) {
	case *pb.TypedValue_StringVal:
		return v.StringVal
	case *pb.TypedValue_JsonIetfVal:
		return strJSON(v.JsonIetfVal)
	case *pb.TypedValue_JsonVal:
		return strJSON(v.JsonVal)
	case *pb.TypedValue_IntVal:
		return strconv.FormatInt(v.IntVal, 10)
	case *pb.TypedValue_UintVal:
		return strconv.FormatUint(v.UintVal, 10)
	case *pb.TypedValue_BoolVal:
		return strconv.FormatBool(v.BoolVal)
	case *pb.TypedValue_BytesVal:
		return base64.StdEncoding.EncodeToString(v.BytesVal)
	case *pb.TypedValue_DecimalVal:
		return strDecimal64(v.DecimalVal)
	case *pb.TypedValue_FloatVal:
		return strconv.FormatFloat(float64(v.FloatVal), 'g', -1, 32)
	case *pb.TypedValue_LeaflistVal:
		return strLeaflist(v.LeaflistVal)
	case *pb.TypedValue_AsciiVal:
		return v.AsciiVal
	case *pb.TypedValue_AnyVal:
		return v.AnyVal.String()
	default:
		panic(v)
	}
}

func strJSON(inJSON []byte) string {
	var out bytes.Buffer
	err := json.Indent(&out, inJSON, "", "  ")
	if err != nil {
		return fmt.Sprintf("(error unmarshalling json: %s)\n", err) + string(inJSON)
	}
	return out.String()
}

func strDecimal64(d *pb.Decimal64) string {
	var i, frac int64
	if d.Precision > 0 {
		div := int64(10)
		it := d.Precision - 1
		for it > 0 {
			div *= 10
			it--
		}
		i = d.Digits / div
		frac = d.Digits % div
	} else {
		i = d.Digits
	}
	if frac < 0 {
		frac = -frac
	}
	return fmt.Sprintf("%d.%d", i, frac)
}

// strLeafList builds a human-readable form of a leaf-list. e.g. [1, 2, 3] or [a, b, c]
func strLeaflist(v *pb.ScalarArray) string {
	var buf bytes.Buffer
	buf.WriteByte('[')

	for i, elm := range v.Element {
		buf.WriteString(StrVal(elm))
		if i < len(v.Element)-1 {
			buf.WriteString(", ")
		}
	}

	buf.WriteByte(']')
	return buf.String()
}

func update(p *pb.Path, val string) *pb.Update {
	var v *pb.TypedValue
	switch p.Origin {
	case "":
		v = &pb.TypedValue{
			Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: extractJSON(val)}}
	case "cli":
		v = &pb.TypedValue{
			Value: &pb.TypedValue_AsciiVal{AsciiVal: val}}
	default:
		panic(fmt.Errorf("unexpected origin: %q", p.Origin))
	}

	return &pb.Update{Path: p, Val: v}
}

// Operation describes an gNMI operation.
type Operation struct {
	Type string
	Path []string
	Val  string
}

func newSetRequest(setOps []*Operation) (*pb.SetRequest, error) {
	req := &pb.SetRequest{}
	for _, op := range setOps {
		p, err := ParseGNMIElements(op.Path)
		if err != nil {
			return nil, err
		}

		switch op.Type {
		case "delete":
			req.Delete = append(req.Delete, p)
		case "update":
			req.Update = append(req.Update, update(p, op.Val))
		case "replace":
			req.Replace = append(req.Replace, update(p, op.Val))
		}
	}
	return req, nil
}

// Set sends a SetRequest to the given client.
func Set(ctx context.Context, client pb.GNMIClient, setOps []*Operation) error {
	req, err := newSetRequest(setOps)
	if err != nil {
		return err
	}
	resp, err := client.Set(ctx, req)
	if err != nil {
		return err
	}
	if resp.Message != nil && codes.Code(resp.Message.Code) != codes.OK {
		return errors.New(resp.Message.Message)
	}
	// TODO: Iterate over SetResponse.Response for more detailed error message?

	return nil
}

// Subscribe sends a SubscribeRequest to the given client.
func Subscribe(ctx context.Context, client pb.GNMIClient, paths [][]string,
	respChan chan<- *pb.SubscribeResponse, errChan chan<- error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	stream, err := client.Subscribe(ctx)
	if err != nil {
		errChan <- err
		return
	}
	req, err := NewSubscribeRequest(paths)
	if err != nil {
		errChan <- err
		return
	}
	if err := stream.Send(req); err != nil {
		errChan <- err
		return
	}

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				return
			}
			errChan <- err
			return
		}
		respChan <- resp
	}
}

// LogSubscribeResponse logs update responses to stderr.
func LogSubscribeResponse(response *pb.SubscribeResponse) error {
	switch resp := response.Response.(type) {
	case *pb.SubscribeResponse_Error:
		return errors.New(resp.Error.Message)
	case *pb.SubscribeResponse_SyncResponse:
		if !resp.SyncResponse {
			return errors.New("initial sync failed")
		}
	case *pb.SubscribeResponse_Update:
		prefix := StrPath(resp.Update.Prefix)
		for _, update := range resp.Update.Update {
			fmt.Printf("%s = %s\n", path.Join(prefix, StrPath(update.Path)),
				StrUpdateVal(update))
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package members

import (
	"fmt"

	"github.com/rackspace/gophercloud"
)

// Create member for specific image
//
// Preconditions
//    The specified images must exist.
//    You can only add a new member to an image which 'visibility' attribute is private.
//    You must be the owner of the specified image.
// Synchronous Postconditions
//    With correct permissions, you can see the member status of the image as pending through API calls.
//
// More details here: http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2
func Create(client *gophercloud.ServiceClient, id string, member string) CreateMemberResult {
	var res CreateMemberResult
	body := map[string]interface{}{}
	body["member"] = member

	response, err := client.Post(imageMembersURL(client, id), body, &res.Body,
		&gophercloud.RequestOpts{OkCodes: []int{200, 409, 403}})

	//some problems in http stack or lower
	if err != nil {
		res.Err = err
		return res
	}

	// membership conflict
	if response.StatusCode == 409 {
		res.Err = fmt.Errorf("Given tenant '%s' is already member for image '%s'.", member, id)
		return res
	}

	// visibility conflict
	if response.StatusCode == 403 {
		res.Err = fmt.Errorf("You can only add a new member to an image "+
			"which 'visibility' attribute is private (image '%s')", id)
		return res
	}

	return res
}

// List members returns list of members for specifed image id
// More details: http://developer.openstack.org/api-ref-image-v2.html#listImageMembers-v2
func List(client *gophercloud.ServiceClient, id string) ListMembersResult {
	var res ListMembersResult
	_, res.Err = client.Get(listMembersURL(client, id), &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}

// Get image member details.
// More details: http://developer.openstack.org/api-ref-image-v2.html#getImageMember-v2
func Get(client *gophercloud.ServiceClient, imageID string, memberID string) MemberDetailsResult {
	var res MemberDetailsResult
	_, res.Err = client.Get(imageMemberURL(client, imageID, memberID), &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}

// Delete membership for given image.
// Callee should be image owner
// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2
func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) MemberDeleteResult {
	var res MemberDeleteResult
	response, err := client.Delete(imageMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204, 403}})

	//some problems in http stack or lower
	if err != nil {
		res.Err = err
		return res
	}

	// Callee is not owner of specified image
	if response.StatusCode == 403 {
		res.Err = fmt.Errorf("You must be the owner of the specified image. "+
			"(image '%s')", imageID)
		return res
	}
	return res
}

// Update fuction updates member
// More details: http://developer.openstack.org/api-ref-image-v2.html#updateImageMember-v2
func Update(client *gophercloud.ServiceClient, imageID string, memberID string, status string) MemberUpdateResult {
	var res MemberUpdateResult
	body := map[string]interface{}{}
	body["status"] = status
	_, res.Err = client.Put(imageMemberURL(client, imageID, memberID), body, &res.Body,
		&gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}
<commit_msg>imageservice: Use UpdateOptsBuilder for the members Update() method<commit_after>package members

import (
	"fmt"

	"github.com/rackspace/gophercloud"
)

// Create member for specific image
//
// Preconditions
//    The specified images must exist.
//    You can only add a new member to an image which 'visibility' attribute is private.
//    You must be the owner of the specified image.
// Synchronous Postconditions
//    With correct permissions, you can see the member status of the image as pending through API calls.
//
// More details here: http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2
func Create(client *gophercloud.ServiceClient, id string, member string) CreateMemberResult {
	var res CreateMemberResult
	body := map[string]interface{}{}
	body["member"] = member

	response, err := client.Post(imageMembersURL(client, id), body, &res.Body,
		&gophercloud.RequestOpts{OkCodes: []int{200, 409, 403}})

	//some problems in http stack or lower
	if err != nil {
		res.Err = err
		return res
	}

	// membership conflict
	if response.StatusCode == 409 {
		res.Err = fmt.Errorf("Given tenant '%s' is already member for image '%s'.", member, id)
		return res
	}

	// visibility conflict
	if response.StatusCode == 403 {
		res.Err = fmt.Errorf("You can only add a new member to an image "+
			"which 'visibility' attribute is private (image '%s')", id)
		return res
	}

	return res
}

// List members returns list of members for specifed image id
// More details: http://developer.openstack.org/api-ref-image-v2.html#listImageMembers-v2
func List(client *gophercloud.ServiceClient, id string) ListMembersResult {
	var res ListMembersResult
	_, res.Err = client.Get(listMembersURL(client, id), &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}

// Get image member details.
// More details: http://developer.openstack.org/api-ref-image-v2.html#getImageMember-v2
func Get(client *gophercloud.ServiceClient, imageID string, memberID string) MemberDetailsResult {
	var res MemberDetailsResult
	_, res.Err = client.Get(imageMemberURL(client, imageID, memberID), &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}

// Delete membership for given image.
// Callee should be image owner
// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2
func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) MemberDeleteResult {
	var res MemberDeleteResult
	response, err := client.Delete(imageMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204, 403}})

	//some problems in http stack or lower
	if err != nil {
		res.Err = err
		return res
	}

	// Callee is not owner of specified image
	if response.StatusCode == 403 {
		res.Err = fmt.Errorf("You must be the owner of the specified image. "+
			"(image '%s')", imageID)
		return res
	}
	return res
}

// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
type UpdateOptsBuilder interface {
	ToMemberUpdateMap() map[string]interface{}
}

// UpdateOpts implements UpdateOptsBuilder
type UpdateOpts struct {
	Status string
}

// ToMemberUpdateMap formats an UpdateOpts structure into a request body.
func (opts UpdateOpts) ToMemberUpdateMap() map[string]interface{} {
	m := make(map[string]interface{})

	if opts.Status != "" {
		m["status"] = opts.Status
	}

	return m
}

// Update function updates member
// More details: http://developer.openstack.org/api-ref-image-v2.html#updateImageMember-v2
func Update(client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) MemberUpdateResult {
	var res MemberUpdateResult
	body := opts.ToMemberUpdateMap()
	_, res.Err = client.Put(imageMemberURL(client, imageID, memberID), body, &res.Body,
		&gophercloud.RequestOpts{OkCodes: []int{200}})
	return res
}
<|endoftext|>


<commit_before>package darts

func Score(x, y float64) int {
	panic("Please implement the Score function")
}
<commit_msg>Solve darts<commit_after>package darts

import "math"

func Score(x, y float64) int {
	if isInnerCircle(x, y) {
		return 10
	} else if isMiddleCircle(x, y) {
		return 5
	} else if isOuterCircle(x, y) {
		return 1
	}
	return 0
}

func isInnerCircle(x float64, y float64) bool {
	return distanceToCenter(x, y) <= 1
}

func isMiddleCircle(x float64, y float64) bool {
	return distanceToCenter(x, y) <= 5
}

func isOuterCircle(x float64, y float64) bool {
	return distanceToCenter(x, y) <= 10
}

func distanceToCenter(x float64, y float64) float64 {
	return math.Sqrt(math.Pow(x, 2) + math.Pow(y, 2))
}
<|endoftext|>


<commit_before>// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://github.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
	"https://login.mailchimp.com/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<commit_msg>Add Campaign Monitor API to exceptions<commit_after>// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://github.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
	"https://login.mailchimp.com/",
	"https://api.createsend.com/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<|endoftext|>


<commit_before>package gcs

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"cloud.google.com/go/storage"
	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/util"
	"google.golang.org/api/option"
)

const (
	// GCS bucket where we store test data. Add a folder to this bucket
	// with the tests for a particular component.
	TEST_DATA_BUCKET = "skia-infra-testdata"
)

func getStorangeItem(bucket, gsPath string) (*storage.Reader, error) {
	storageClient, err := storage.NewClient(context.Background(), option.WithHTTPClient(httputils.NewTimeoutClient()))
	if err != nil {
		return nil, err
	}

	return storageClient.Bucket(bucket).Object(gsPath).NewReader(context.Background())
}

// DownloadTestDataFile downloads a file with test data from Google Storage.
// The uriPath identifies what to download from the test bucket in GCS.
// The content must be publicly accessible.
// The file will be downloaded and stored at provided target
// path (regardless of what the original name is).
// If the the uri ends with '.gz' it will be transparently unzipped.
func DownloadTestDataFile(t assert.TestingT, bucket, gsPath, targetPath string) error {
	dir, _ := filepath.Split(targetPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	arch, err := getStorangeItem(bucket, gsPath)
	if err != nil {
		return fmt.Errorf("Could not get gs://%s/%s: %s", bucket, gsPath, err)
	}
	defer func() { assert.NoError(t, arch.Close()) }()

	// Open the output
	var r io.ReadCloser = arch
	if strings.HasSuffix(gsPath, ".gz") {
		r, err = gzip.NewReader(r)
		if err != nil {
			return fmt.Errorf("Could not read gzip file: %s", err)
		}
	}

	f, err := os.Create(targetPath)
	if err != nil {
		return fmt.Errorf("Could not create target path: %s", err)
	}
	defer func() { assert.NoError(t, f.Close()) }()
	_, err = io.Copy(f, r)
	return err
}

// DownloadTestDataArchive downloads testfiles that are stored in
// a gz compressed tar archive and decompresses them into the provided
// target directory.
func DownloadTestDataArchive(t assert.TestingT, bucket, gsPath, targetDir string) error {
	if !strings.HasSuffix(gsPath, ".tar.gz") {
		return fmt.Errorf("Expected .tar.gz file. But got:%s", gsPath)
	}

	if err := os.MkdirAll(targetDir, 0755); err != nil {
		return err
	}

	arch, err := getStorangeItem(bucket, gsPath)
	if err != nil {
		return fmt.Errorf("Could not get gs://%s/%s: %s", bucket, gsPath, err)
	}
	defer func() { assert.NoError(t, arch.Close()) }()

	// Open the output
	r, err := gzip.NewReader(arch)
	if err != nil {
		return fmt.Errorf("Could not read gzip archive: %s", err)
	}
	tarReader := tar.NewReader(r)

	for {
		hdr, err := tarReader.Next()
		if err == io.EOF {
			break
		}

		if err != nil {
			return fmt.Errorf("Problem reading from tar archive: %s", err)
		}

		targetPath := filepath.Join(targetDir, hdr.Name)

		if hdr.Typeflag == tar.TypeDir {
			if err := os.MkdirAll(targetPath, 0755); err != nil {
				return fmt.Errorf("Could not make %s: %s", targetPath, err)
			}
		} else {
			f, err := os.Create(targetPath)
			if err != nil {
				return fmt.Errorf("Could not create target file %s: %s", targetPath, err)
			}
			_, err = io.Copy(f, tarReader)
			if err != nil {
				return fmt.Errorf("Problem while copying: %s", err)
			}
			defer func() { assert.NoError(t, f.Close()) }()
		}
	}

	return nil
}

// MemoryGCSClient is a struct used for testing. Instead of writing to GCS, it
// stores data in memory. Not thread-safe.
type MemoryGCSClient struct {
	bucket string
	data   map[string][]byte
	opts   map[string]FileWriteOptions
}

// Return a MemoryGCSClient instance.
func NewMemoryGCSClient(bucket string) *MemoryGCSClient {
	return &MemoryGCSClient{
		bucket: bucket,
		data:   map[string][]byte{},
		opts:   map[string]FileWriteOptions{},
	}
}

// See documentationn for GCSClient interface.
func (c *MemoryGCSClient) FileReader(ctx context.Context, path string) (io.ReadCloser, error) {
	contents, ok := c.data[path]
	if !ok {
		return nil, storage.ErrObjectNotExist
	}
	rv := ioutil.NopCloser(bytes.NewReader(contents))
	// GCS automatically decodes gzip-encoded files. See
	// https://cloud.google.com/storage/docs/transcoding. We do the same here so that tests acurately
	// reflect what will happen when actually using GCS.
	if c.opts[path].ContentEncoding == "gzip" {
		var err error
		rv, err = gzip.NewReader(rv)
		if err != nil {
			return nil, err
		}
	}
	return rv, nil
}

// io.WriteCloser implementation used by MemoryGCSClient.
type memoryWriter struct {
	buf    *bytes.Buffer
	client *MemoryGCSClient
	path   string
}

// See documentation for io.Writer.
func (w *memoryWriter) Write(p []byte) (int, error) {
	return w.buf.Write(p)
}

// See documentation for io.Closer.
func (w *memoryWriter) Close() error {
	w.client.data[w.path] = w.buf.Bytes()
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) FileWriter(ctx context.Context, path string, opts FileWriteOptions) io.WriteCloser {
	c.opts[path] = opts
	return &memoryWriter{
		buf:    bytes.NewBuffer(nil),
		client: c,
		path:   path,
	}
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) DoesFileExist(ctx context.Context, path string) (bool, error) {
	_, err := c.FileReader(ctx, path)
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) GetFileContents(ctx context.Context, path string) ([]byte, error) {
	r, err := c.FileReader(ctx, path)
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(r)
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) SetFileContents(ctx context.Context, path string, opts FileWriteOptions, contents []byte) error {
	return WithWriteFile(c, ctx, path, opts, func(w io.Writer) error {
		_, err := w.Write(contents)
		return err
	})
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) AllFilesInDirectory(ctx context.Context, prefix string, callback func(item *storage.ObjectAttrs)) error {
	for key, data := range c.data {
		if strings.HasPrefix(key, prefix) {
			opts := c.opts[key]
			item := &storage.ObjectAttrs{
				Bucket:             c.bucket,
				Name:               key,
				ContentType:        opts.ContentType,
				ContentLanguage:    opts.ContentLanguage,
				Size:               int64(len(data)),
				ContentEncoding:    opts.ContentEncoding,
				ContentDisposition: opts.ContentDisposition,
				Metadata:           util.CopyStringMap(opts.Metadata),
			}
			callback(item)
		}
	}
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) DeleteFile(ctx context.Context, path string) error {
	delete(c.data, path)
	delete(c.opts, path)
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) Bucket() string {
	return c.bucket
}

var _ GCSClient = (*MemoryGCSClient)(nil)
<commit_msg>Make MemoryGCSClient goroutine-safe.<commit_after>package gcs

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"cloud.google.com/go/storage"
	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/util"
	"google.golang.org/api/option"
)

const (
	// GCS bucket where we store test data. Add a folder to this bucket
	// with the tests for a particular component.
	TEST_DATA_BUCKET = "skia-infra-testdata"
)

func getStorangeItem(bucket, gsPath string) (*storage.Reader, error) {
	storageClient, err := storage.NewClient(context.Background(), option.WithHTTPClient(httputils.NewTimeoutClient()))
	if err != nil {
		return nil, err
	}

	return storageClient.Bucket(bucket).Object(gsPath).NewReader(context.Background())
}

// DownloadTestDataFile downloads a file with test data from Google Storage.
// The uriPath identifies what to download from the test bucket in GCS.
// The content must be publicly accessible.
// The file will be downloaded and stored at provided target
// path (regardless of what the original name is).
// If the the uri ends with '.gz' it will be transparently unzipped.
func DownloadTestDataFile(t assert.TestingT, bucket, gsPath, targetPath string) error {
	dir, _ := filepath.Split(targetPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	arch, err := getStorangeItem(bucket, gsPath)
	if err != nil {
		return fmt.Errorf("Could not get gs://%s/%s: %s", bucket, gsPath, err)
	}
	defer func() { assert.NoError(t, arch.Close()) }()

	// Open the output
	var r io.ReadCloser = arch
	if strings.HasSuffix(gsPath, ".gz") {
		r, err = gzip.NewReader(r)
		if err != nil {
			return fmt.Errorf("Could not read gzip file: %s", err)
		}
	}

	f, err := os.Create(targetPath)
	if err != nil {
		return fmt.Errorf("Could not create target path: %s", err)
	}
	defer func() { assert.NoError(t, f.Close()) }()
	_, err = io.Copy(f, r)
	return err
}

// DownloadTestDataArchive downloads testfiles that are stored in
// a gz compressed tar archive and decompresses them into the provided
// target directory.
func DownloadTestDataArchive(t assert.TestingT, bucket, gsPath, targetDir string) error {
	if !strings.HasSuffix(gsPath, ".tar.gz") {
		return fmt.Errorf("Expected .tar.gz file. But got:%s", gsPath)
	}

	if err := os.MkdirAll(targetDir, 0755); err != nil {
		return err
	}

	arch, err := getStorangeItem(bucket, gsPath)
	if err != nil {
		return fmt.Errorf("Could not get gs://%s/%s: %s", bucket, gsPath, err)
	}
	defer func() { assert.NoError(t, arch.Close()) }()

	// Open the output
	r, err := gzip.NewReader(arch)
	if err != nil {
		return fmt.Errorf("Could not read gzip archive: %s", err)
	}
	tarReader := tar.NewReader(r)

	for {
		hdr, err := tarReader.Next()
		if err == io.EOF {
			break
		}

		if err != nil {
			return fmt.Errorf("Problem reading from tar archive: %s", err)
		}

		targetPath := filepath.Join(targetDir, hdr.Name)

		if hdr.Typeflag == tar.TypeDir {
			if err := os.MkdirAll(targetPath, 0755); err != nil {
				return fmt.Errorf("Could not make %s: %s", targetPath, err)
			}
		} else {
			f, err := os.Create(targetPath)
			if err != nil {
				return fmt.Errorf("Could not create target file %s: %s", targetPath, err)
			}
			_, err = io.Copy(f, tarReader)
			if err != nil {
				return fmt.Errorf("Problem while copying: %s", err)
			}
			defer func() { assert.NoError(t, f.Close()) }()
		}
	}

	return nil
}

// MemoryGCSClient is a struct used for testing. Instead of writing to GCS, it
// stores data in memory.
type MemoryGCSClient struct {
	bucket string
	data   map[string][]byte
	opts   map[string]FileWriteOptions
	mtx    sync.RWMutex
}

// Return a MemoryGCSClient instance.
func NewMemoryGCSClient(bucket string) *MemoryGCSClient {
	return &MemoryGCSClient{
		bucket: bucket,
		data:   map[string][]byte{},
		opts:   map[string]FileWriteOptions{},
	}
}

// See documentationn for GCSClient interface.
func (c *MemoryGCSClient) FileReader(ctx context.Context, path string) (io.ReadCloser, error) {
	c.mtx.RLock()
	defer c.mtx.RUnlock()
	contents, ok := c.data[path]
	if !ok {
		return nil, storage.ErrObjectNotExist
	}
	rv := ioutil.NopCloser(bytes.NewReader(contents))
	// GCS automatically decodes gzip-encoded files. See
	// https://cloud.google.com/storage/docs/transcoding. We do the same here so that tests acurately
	// reflect what will happen when actually using GCS.
	if c.opts[path].ContentEncoding == "gzip" {
		var err error
		rv, err = gzip.NewReader(rv)
		if err != nil {
			return nil, err
		}
	}
	return rv, nil
}

// io.WriteCloser implementation used by MemoryGCSClient.
type memoryWriter struct {
	buf    *bytes.Buffer
	client *MemoryGCSClient
	path   string
}

// See documentation for io.Writer.
func (w *memoryWriter) Write(p []byte) (int, error) {
	return w.buf.Write(p)
}

// See documentation for io.Closer.
func (w *memoryWriter) Close() error {
	w.client.mtx.Lock()
	defer w.client.mtx.Unlock()
	w.client.data[w.path] = w.buf.Bytes()
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) FileWriter(ctx context.Context, path string, opts FileWriteOptions) io.WriteCloser {
	c.mtx.Lock()
	defer c.mtx.Unlock()
	c.opts[path] = opts
	return &memoryWriter{
		buf:    bytes.NewBuffer(nil),
		client: c,
		path:   path,
	}
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) DoesFileExist(ctx context.Context, path string) (bool, error) {
	_, err := c.FileReader(ctx, path)
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) GetFileContents(ctx context.Context, path string) ([]byte, error) {
	r, err := c.FileReader(ctx, path)
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(r)
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) SetFileContents(ctx context.Context, path string, opts FileWriteOptions, contents []byte) error {
	return WithWriteFile(c, ctx, path, opts, func(w io.Writer) error {
		_, err := w.Write(contents)
		return err
	})
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) AllFilesInDirectory(ctx context.Context, prefix string, callback func(item *storage.ObjectAttrs)) error {
	items := func() []*storage.ObjectAttrs {
		c.mtx.RLock()
		defer c.mtx.RUnlock()
		var items []*storage.ObjectAttrs
		for key, data := range c.data {
			if strings.HasPrefix(key, prefix) {
				opts := c.opts[key]
				items = append(items, &storage.ObjectAttrs{
					Bucket:             c.bucket,
					Name:               key,
					ContentType:        opts.ContentType,
					ContentLanguage:    opts.ContentLanguage,
					Size:               int64(len(data)),
					ContentEncoding:    opts.ContentEncoding,
					ContentDisposition: opts.ContentDisposition,
					Metadata:           util.CopyStringMap(opts.Metadata),
				})
			}
		}
		return items
	}()
	for _, item := range items {
		callback(item)
	}
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) DeleteFile(ctx context.Context, path string) error {
	c.mtx.Lock()
	defer c.mtx.Unlock()
	delete(c.data, path)
	delete(c.opts, path)
	return nil
}

// See documentation for GCSClient interface.
func (c *MemoryGCSClient) Bucket() string {
	return c.bucket
}

var _ GCSClient = (*MemoryGCSClient)(nil)
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/weed-fs/go/operation"
	"code.google.com/p/weed-fs/go/util"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
)

var (
	uploadReplication *string
	uploadDir         *string
)

func init() {
	cmdUpload.Run = runUpload // break init cycle
	cmdUpload.IsDebug = cmdUpload.Flag.Bool("debug", false, "verbose debug information")
	server = cmdUpload.Flag.String("server", "localhost:9333", "weedfs master location")
	uploadDir = cmdUpload.Flag.String("dir", "", "Upload the whole folder recursively if specified.")
	uploadReplication = cmdUpload.Flag.String("replication", "000", "replication type(000,001,010,100,110,200)")
}

var cmdUpload = &Command{
	UsageLine: "upload -server=localhost:9333 file1 [file2 file3]\n upload -server=localhost:9333 -dir=one_directory",
	Short:     "upload one or a list of files",
	Long: `upload one or a list of files, or batch upload one whole folder recursively.
  It uses consecutive file keys for the list of files.
  e.g. If the file1 uses key k, file2 can be read via k_1

  `,
}

type AssignResult struct {
	Fid       string `json:"fid"`
	Url       string `json:"url"`
	PublicUrl string `json:"publicUrl"`
	Count     int
	Error     string `json:"error"`
}

func assign(count int) (*AssignResult, error) {
	values := make(url.Values)
	values.Add("count", strconv.Itoa(count))
	values.Add("replication", *uploadReplication)
	jsonBlob, err := util.Post("http://"+*server+"/dir/assign", values)
	debug("assign result :", string(jsonBlob))
	if err != nil {
		return nil, err
	}
	var ret AssignResult
	err = json.Unmarshal(jsonBlob, &ret)
	if err != nil {
		return nil, err
	}
	if ret.Count <= 0 {
		return nil, errors.New(ret.Error)
	}
	return &ret, nil
}

func upload(filename string, server string, fid string) (int, error) {
	debug("Start uploading file:", filename)
	fh, err := os.Open(filename)
	if err != nil {
		debug("Failed to open file:", filename)
		return 0, err
	}
	fi, fiErr := fh.Stat()
	if fiErr != nil {
		debug("Failed to stat file:", filename)
		return 0, fiErr
	}
	ret, e := operation.Upload("http://"+server+"/"+fid+"?ts="+strconv.Itoa(int(fi.ModTime().Unix())), path.Base(filename), fh)
	if e != nil {
		return 0, e
	}
	return ret.Size, e
}

type SubmitResult struct {
	FileName string `json:"fileName"`
	FileUrl  string `json:"fileUrl"`
	Fid      string `json:"fid"`
	Size     int    `json:"size"`
	Error    string `json:"error"`
}

func submit(files []string) ([]SubmitResult, error) {
	ret, err := assign(len(files))
	if err != nil {
		return nil, err
	}
	results := make([]SubmitResult, len(files))
	for index, file := range files {
		fid := ret.Fid
		if index > 0 {
			fid = fid + "_" + strconv.Itoa(index)
		}
		results[index].Size, err = upload(file, ret.PublicUrl, fid)
		if err != nil {
			fid = ""
			results[index].Error = err.Error()
		}
		results[index].FileName = file
		results[index].Fid = fid
		results[index].FileUrl = ret.PublicUrl + "/" + fid
	}
	return results, nil
}

func runUpload(cmd *Command, args []string) bool {
	if len(cmdUpload.Flag.Args()) == 0 {
		if *uploadDir == "" {
			return false
		}
		filepath.Walk(*uploadDir, func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() {
				if results, err := submit([]string{path}); err == nil {
					bytes, _ := json.Marshal(results)
					if bytes != nil {
						fmt.Println(string(bytes))
					}
				} else {
					fmt.Println(err, "when uploading", path)
					return err
				}
			}
			return err
		})
	} else {
		if results, err := submit(args); err == nil {
			bytes, _ := json.Marshal(results)
			fmt.Println(string(bytes))
		} else {
			fmt.Println(err)
		}
	}
	return true
}
<commit_msg>Consistent result format when error happens<commit_after>package main

import (
	"code.google.com/p/weed-fs/go/operation"
	"code.google.com/p/weed-fs/go/util"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
)

var (
	uploadReplication *string
	uploadDir         *string
)

func init() {
	cmdUpload.Run = runUpload // break init cycle
	cmdUpload.IsDebug = cmdUpload.Flag.Bool("debug", false, "verbose debug information")
	server = cmdUpload.Flag.String("server", "localhost:9333", "weedfs master location")
	uploadDir = cmdUpload.Flag.String("dir", "", "Upload the whole folder recursively if specified.")
	uploadReplication = cmdUpload.Flag.String("replication", "000", "replication type(000,001,010,100,110,200)")
}

var cmdUpload = &Command{
	UsageLine: "upload -server=localhost:9333 file1 [file2 file3]\n upload -server=localhost:9333 -dir=one_directory",
	Short:     "upload one or a list of files",
	Long: `upload one or a list of files, or batch upload one whole folder recursively.
  It uses consecutive file keys for the list of files.
  e.g. If the file1 uses key k, file2 can be read via k_1

  `,
}

type AssignResult struct {
	Fid       string `json:"fid"`
	Url       string `json:"url"`
	PublicUrl string `json:"publicUrl"`
	Count     int
	Error     string `json:"error"`
}

func assign(count int) (*AssignResult, error) {
	values := make(url.Values)
	values.Add("count", strconv.Itoa(count))
	values.Add("replication", *uploadReplication)
	jsonBlob, err := util.Post("http://"+*server+"/dir/assign", values)
	debug("assign result :", string(jsonBlob))
	if err != nil {
		return nil, err
	}
	var ret AssignResult
	err = json.Unmarshal(jsonBlob, &ret)
	if err != nil {
		return nil, err
	}
	if ret.Count <= 0 {
		return nil, errors.New(ret.Error)
	}
	return &ret, nil
}

func upload(filename string, server string, fid string) (int, error) {
	debug("Start uploading file:", filename)
	fh, err := os.Open(filename)
	if err != nil {
		debug("Failed to open file:", filename)
		return 0, err
	}
	fi, fiErr := fh.Stat()
	if fiErr != nil {
		debug("Failed to stat file:", filename)
		return 0, fiErr
	}
	ret, e := operation.Upload("http://"+server+"/"+fid+"?ts="+strconv.Itoa(int(fi.ModTime().Unix())), path.Base(filename), fh)
	if e != nil {
		return 0, e
	}
	return ret.Size, e
}

type SubmitResult struct {
	FileName string `json:"fileName"`
	FileUrl  string `json:"fileUrl"`
	Fid      string `json:"fid"`
	Size     int    `json:"size"`
	Error    string `json:"error"`
}

func submit(files []string) ([]SubmitResult, error) {
	results := make([]SubmitResult, len(files))
	for index, file := range files {
		results[index].FileName = file
	}
	ret, err := assign(len(files))
	if err != nil {
		for index, _ := range files {
			results[index].Error = err.Error()
		}
		return results, err
	}
	for index, file := range files {
		fid := ret.Fid
		if index > 0 {
			fid = fid + "_" + strconv.Itoa(index)
		}
		results[index].Size, err = upload(file, ret.PublicUrl, fid)
		if err != nil {
			fid = ""
			results[index].Error = err.Error()
		}
		results[index].Fid = fid
		results[index].FileUrl = ret.PublicUrl + "/" + fid
	}
	return results, nil
}

func runUpload(cmd *Command, args []string) bool {
	if len(cmdUpload.Flag.Args()) == 0 {
		if *uploadDir == "" {
			return false
		}
		filepath.Walk(*uploadDir, func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() {
        results, e := submit([]string{path})
				bytes, _ := json.Marshal(results)
				fmt.Println(string(bytes))
				if e != nil {
				  return e
				}
			}
			return err
		})
	} else {
		results, _ := submit(args)
		bytes, _ := json.Marshal(results)
		fmt.Println(string(bytes))
	}
	return true
}
<|endoftext|>


<commit_before>package google

import (
	"bytes"
	"fmt"
	"log"
	"regexp"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/googleapi"
)

func resourceComputeBackendService() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeBackendServiceCreate,
		Read:   resourceComputeBackendServiceRead,
		Update: resourceComputeBackendServiceUpdate,
		Delete: resourceComputeBackendServiceDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
					if !regexp.MustCompile(re).MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q (%q) doesn't match regexp %q", k, value, re))
					}
					return
				},
			},

			"health_checks": &schema.Schema{
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Required: true,
				Set:      schema.HashString,
			},

			"backend": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"group": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"balancing_mode": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "UTILIZATION",
						},
						"capacity_scaler": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
							Default:  1,
						},
						"description": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"max_rate": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
						},
						"max_rate_per_instance": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
						},
						"max_utilization": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
							Default:  0.8,
						},
					},
				},
				Optional: true,
				Set:      resourceGoogleComputeBackendServiceBackendHash,
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"enable_cdn": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"fingerprint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"port_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"project": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Removed:  "region has been removed as it was never used",
			},

			"self_link": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"session_affinity": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"timeout_sec": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	hc := d.Get("health_checks").(*schema.Set).List()
	healthChecks := make([]string, 0, len(hc))
	for _, v := range hc {
		healthChecks = append(healthChecks, v.(string))
	}

	service := compute.BackendService{
		Name:         d.Get("name").(string),
		HealthChecks: healthChecks,
	}

	if v, ok := d.GetOk("backend"); ok {
		service.Backends = expandBackends(v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("description"); ok {
		service.Description = v.(string)
	}

	if v, ok := d.GetOk("port_name"); ok {
		service.PortName = v.(string)
	}

	if v, ok := d.GetOk("protocol"); ok {
		service.Protocol = v.(string)
	}

	if v, ok := d.GetOk("session_affinity"); ok {
		service.SessionAffinity = v.(string)
	}

	if v, ok := d.GetOk("timeout_sec"); ok {
		service.TimeoutSec = int64(v.(int))
	}

	if v, ok := d.GetOk("enable_cdn"); ok {
		service.EnableCDN = v.(bool)
	}

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Creating new Backend Service: %#v", service)
	op, err := config.clientCompute.BackendServices.Insert(
		project, &service).Do()
	if err != nil {
		return fmt.Errorf("Error creating backend service: %s", err)
	}

	log.Printf("[DEBUG] Waiting for new backend service, operation: %#v", op)

	// Store the ID now
	d.SetId(service.Name)

	// Wait for the operation to complete
	waitErr := computeOperationWaitGlobal(config, op, project, "Creating Backend Service")
	if waitErr != nil {
		// The resource didn't actually create
		d.SetId("")
		return waitErr
	}

	return resourceComputeBackendServiceRead(d, meta)
}

func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	service, err := config.clientCompute.BackendServices.Get(
		project, d.Id()).Do()
	if err != nil {
		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
			// The resource doesn't exist anymore
			log.Printf("[WARN] Removing Backend Service %q because it's gone", d.Get("name").(string))
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading service: %s", err)
	}

	d.Set("description", service.Description)
	d.Set("enable_cdn", service.EnableCDN)
	d.Set("port_name", service.PortName)
	d.Set("protocol", service.Protocol)
	d.Set("session_affinity", service.SessionAffinity)
	d.Set("timeout_sec", service.TimeoutSec)
	d.Set("fingerprint", service.Fingerprint)
	d.Set("self_link", service.SelfLink)

	d.Set("backend", flattenBackends(service.Backends))
	d.Set("health_checks", service.HealthChecks)

	return nil
}

func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	hc := d.Get("health_checks").(*schema.Set).List()
	healthChecks := make([]string, 0, len(hc))
	for _, v := range hc {
		healthChecks = append(healthChecks, v.(string))
	}

	service := compute.BackendService{
		Name:         d.Get("name").(string),
		Fingerprint:  d.Get("fingerprint").(string),
		HealthChecks: healthChecks,
	}

	// Optional things
	if v, ok := d.GetOk("backend"); ok {
		service.Backends = expandBackends(v.(*schema.Set).List())
	}
	if v, ok := d.GetOk("description"); ok {
		service.Description = v.(string)
	}
	if v, ok := d.GetOk("port_name"); ok {
		service.PortName = v.(string)
	}
	if v, ok := d.GetOk("protocol"); ok {
		service.Protocol = v.(string)
	}
	if v, ok := d.GetOk("timeout_sec"); ok {
		service.TimeoutSec = int64(v.(int))
	}

	if d.HasChange("session_affinity") {
		service.SessionAffinity = d.Get("session_affinity").(string)
	}

	if d.HasChange("enable_cdn") {
		service.EnableCDN = d.Get("enable_cdn").(bool)
	}

	log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service)
	op, err := config.clientCompute.BackendServices.Update(
		project, d.Id(), &service).Do()
	if err != nil {
		return fmt.Errorf("Error updating backend service: %s", err)
	}

	d.SetId(service.Name)

	err = computeOperationWaitGlobal(config, op, project, "Updating Backend Service")
	if err != nil {
		return err
	}

	return resourceComputeBackendServiceRead(d, meta)
}

func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Deleting backend service %s", d.Id())
	op, err := config.clientCompute.BackendServices.Delete(
		project, d.Id()).Do()
	if err != nil {
		return fmt.Errorf("Error deleting backend service: %s", err)
	}

	err = computeOperationWaitGlobal(config, op, project, "Deleting Backend Service")
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}

func expandBackends(configured []interface{}) []*compute.Backend {
	backends := make([]*compute.Backend, 0, len(configured))

	for _, raw := range configured {
		data := raw.(map[string]interface{})

		b := compute.Backend{
			Group: data["group"].(string),
		}

		if v, ok := data["balancing_mode"]; ok {
			b.BalancingMode = v.(string)
		}
		if v, ok := data["capacity_scaler"]; ok {
			b.CapacityScaler = v.(float64)
		}
		if v, ok := data["description"]; ok {
			b.Description = v.(string)
		}
		if v, ok := data["max_rate"]; ok {
			b.MaxRate = int64(v.(int))
		}
		if v, ok := data["max_rate_per_instance"]; ok {
			b.MaxRatePerInstance = v.(float64)
		}
		if v, ok := data["max_utilization"]; ok {
			b.MaxUtilization = v.(float64)
		}

		backends = append(backends, &b)
	}

	return backends
}

func flattenBackends(backends []*compute.Backend) []map[string]interface{} {
	result := make([]map[string]interface{}, 0, len(backends))

	for _, b := range backends {
		data := make(map[string]interface{})

		data["balancing_mode"] = b.BalancingMode
		data["capacity_scaler"] = b.CapacityScaler
		data["description"] = b.Description
		data["group"] = b.Group
		data["max_rate"] = b.MaxRate
		data["max_rate_per_instance"] = b.MaxRatePerInstance
		data["max_utilization"] = b.MaxUtilization

		result = append(result, data)
	}

	return result
}

func resourceGoogleComputeBackendServiceBackendHash(v interface{}) int {
	if v == nil {
		return 0
	}

	var buf bytes.Buffer
	m := v.(map[string]interface{})

	buf.WriteString(fmt.Sprintf("%s-", m["group"].(string)))

	if v, ok := m["balancing_mode"]; ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}
	if v, ok := m["capacity_scaler"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}
	if v, ok := m["description"]; ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}
	if v, ok := m["max_rate"]; ok {
		buf.WriteString(fmt.Sprintf("%d-", int64(v.(int))))
	}
	if v, ok := m["max_rate_per_instance"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}
	if v, ok := m["max_rate_per_instance"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}

	return hashcode.String(buf.String())
}
<commit_msg>provider/google: better visibility for compute_region_backend_service (#14301)<commit_after>package google

import (
	"bytes"
	"fmt"
	"log"
	"regexp"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/googleapi"
)

func resourceComputeBackendService() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeBackendServiceCreate,
		Read:   resourceComputeBackendServiceRead,
		Update: resourceComputeBackendServiceUpdate,
		Delete: resourceComputeBackendServiceDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
					if !regexp.MustCompile(re).MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q (%q) doesn't match regexp %q", k, value, re))
					}
					return
				},
			},

			"health_checks": &schema.Schema{
				Type:     schema.TypeSet,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Required: true,
				Set:      schema.HashString,
			},

			"backend": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"group": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"balancing_mode": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "UTILIZATION",
						},
						"capacity_scaler": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
							Default:  1,
						},
						"description": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"max_rate": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
						},
						"max_rate_per_instance": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
						},
						"max_utilization": &schema.Schema{
							Type:     schema.TypeFloat,
							Optional: true,
							Default:  0.8,
						},
					},
				},
				Optional: true,
				Set:      resourceGoogleComputeBackendServiceBackendHash,
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"enable_cdn": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"fingerprint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"port_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"project": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Removed:  "region has been removed as it was never used. For internal load balancing, use google_compute_region_backend_service",
			},

			"self_link": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"session_affinity": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"timeout_sec": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	hc := d.Get("health_checks").(*schema.Set).List()
	healthChecks := make([]string, 0, len(hc))
	for _, v := range hc {
		healthChecks = append(healthChecks, v.(string))
	}

	service := compute.BackendService{
		Name:         d.Get("name").(string),
		HealthChecks: healthChecks,
	}

	if v, ok := d.GetOk("backend"); ok {
		service.Backends = expandBackends(v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("description"); ok {
		service.Description = v.(string)
	}

	if v, ok := d.GetOk("port_name"); ok {
		service.PortName = v.(string)
	}

	if v, ok := d.GetOk("protocol"); ok {
		service.Protocol = v.(string)
	}

	if v, ok := d.GetOk("session_affinity"); ok {
		service.SessionAffinity = v.(string)
	}

	if v, ok := d.GetOk("timeout_sec"); ok {
		service.TimeoutSec = int64(v.(int))
	}

	if v, ok := d.GetOk("enable_cdn"); ok {
		service.EnableCDN = v.(bool)
	}

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Creating new Backend Service: %#v", service)
	op, err := config.clientCompute.BackendServices.Insert(
		project, &service).Do()
	if err != nil {
		return fmt.Errorf("Error creating backend service: %s", err)
	}

	log.Printf("[DEBUG] Waiting for new backend service, operation: %#v", op)

	// Store the ID now
	d.SetId(service.Name)

	// Wait for the operation to complete
	waitErr := computeOperationWaitGlobal(config, op, project, "Creating Backend Service")
	if waitErr != nil {
		// The resource didn't actually create
		d.SetId("")
		return waitErr
	}

	return resourceComputeBackendServiceRead(d, meta)
}

func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	service, err := config.clientCompute.BackendServices.Get(
		project, d.Id()).Do()
	if err != nil {
		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
			// The resource doesn't exist anymore
			log.Printf("[WARN] Removing Backend Service %q because it's gone", d.Get("name").(string))
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading service: %s", err)
	}

	d.Set("description", service.Description)
	d.Set("enable_cdn", service.EnableCDN)
	d.Set("port_name", service.PortName)
	d.Set("protocol", service.Protocol)
	d.Set("session_affinity", service.SessionAffinity)
	d.Set("timeout_sec", service.TimeoutSec)
	d.Set("fingerprint", service.Fingerprint)
	d.Set("self_link", service.SelfLink)

	d.Set("backend", flattenBackends(service.Backends))
	d.Set("health_checks", service.HealthChecks)

	return nil
}

func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	hc := d.Get("health_checks").(*schema.Set).List()
	healthChecks := make([]string, 0, len(hc))
	for _, v := range hc {
		healthChecks = append(healthChecks, v.(string))
	}

	service := compute.BackendService{
		Name:         d.Get("name").(string),
		Fingerprint:  d.Get("fingerprint").(string),
		HealthChecks: healthChecks,
	}

	// Optional things
	if v, ok := d.GetOk("backend"); ok {
		service.Backends = expandBackends(v.(*schema.Set).List())
	}
	if v, ok := d.GetOk("description"); ok {
		service.Description = v.(string)
	}
	if v, ok := d.GetOk("port_name"); ok {
		service.PortName = v.(string)
	}
	if v, ok := d.GetOk("protocol"); ok {
		service.Protocol = v.(string)
	}
	if v, ok := d.GetOk("timeout_sec"); ok {
		service.TimeoutSec = int64(v.(int))
	}

	if d.HasChange("session_affinity") {
		service.SessionAffinity = d.Get("session_affinity").(string)
	}

	if d.HasChange("enable_cdn") {
		service.EnableCDN = d.Get("enable_cdn").(bool)
	}

	log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service)
	op, err := config.clientCompute.BackendServices.Update(
		project, d.Id(), &service).Do()
	if err != nil {
		return fmt.Errorf("Error updating backend service: %s", err)
	}

	d.SetId(service.Name)

	err = computeOperationWaitGlobal(config, op, project, "Updating Backend Service")
	if err != nil {
		return err
	}

	return resourceComputeBackendServiceRead(d, meta)
}

func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	project, err := getProject(d, config)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Deleting backend service %s", d.Id())
	op, err := config.clientCompute.BackendServices.Delete(
		project, d.Id()).Do()
	if err != nil {
		return fmt.Errorf("Error deleting backend service: %s", err)
	}

	err = computeOperationWaitGlobal(config, op, project, "Deleting Backend Service")
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}

func expandBackends(configured []interface{}) []*compute.Backend {
	backends := make([]*compute.Backend, 0, len(configured))

	for _, raw := range configured {
		data := raw.(map[string]interface{})

		b := compute.Backend{
			Group: data["group"].(string),
		}

		if v, ok := data["balancing_mode"]; ok {
			b.BalancingMode = v.(string)
		}
		if v, ok := data["capacity_scaler"]; ok {
			b.CapacityScaler = v.(float64)
		}
		if v, ok := data["description"]; ok {
			b.Description = v.(string)
		}
		if v, ok := data["max_rate"]; ok {
			b.MaxRate = int64(v.(int))
		}
		if v, ok := data["max_rate_per_instance"]; ok {
			b.MaxRatePerInstance = v.(float64)
		}
		if v, ok := data["max_utilization"]; ok {
			b.MaxUtilization = v.(float64)
		}

		backends = append(backends, &b)
	}

	return backends
}

func flattenBackends(backends []*compute.Backend) []map[string]interface{} {
	result := make([]map[string]interface{}, 0, len(backends))

	for _, b := range backends {
		data := make(map[string]interface{})

		data["balancing_mode"] = b.BalancingMode
		data["capacity_scaler"] = b.CapacityScaler
		data["description"] = b.Description
		data["group"] = b.Group
		data["max_rate"] = b.MaxRate
		data["max_rate_per_instance"] = b.MaxRatePerInstance
		data["max_utilization"] = b.MaxUtilization

		result = append(result, data)
	}

	return result
}

func resourceGoogleComputeBackendServiceBackendHash(v interface{}) int {
	if v == nil {
		return 0
	}

	var buf bytes.Buffer
	m := v.(map[string]interface{})

	buf.WriteString(fmt.Sprintf("%s-", m["group"].(string)))

	if v, ok := m["balancing_mode"]; ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}
	if v, ok := m["capacity_scaler"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}
	if v, ok := m["description"]; ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}
	if v, ok := m["max_rate"]; ok {
		buf.WriteString(fmt.Sprintf("%d-", int64(v.(int))))
	}
	if v, ok := m["max_rate_per_instance"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}
	if v, ok := m["max_rate_per_instance"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
	}

	return hashcode.String(buf.String())
}
<|endoftext|>