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

package validator

import (
	"crypto"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"

	jwtgo "github.com/dgrijalva/jwt-go"
	xnet "github.com/minio/minio/pkg/net"
)

// JWKSArgs - RSA authentication target arguments
type JWKSArgs struct {
	URL       *xnet.URL `json:"url"`
	publicKey crypto.PublicKey
}

// Validate JWT authentication target arguments
func (r *JWKSArgs) Validate() error {
	return nil
}

// PopulatePublicKey - populates a new publickey from the JWKS URL.
func (r *JWKSArgs) PopulatePublicKey() error {
	insecureClient := &http.Client{Transport: newCustomHTTPTransport(true)}
	client := &http.Client{Transport: newCustomHTTPTransport(false)}
	resp, err := client.Get(r.URL.String())
	if err != nil {
		resp, err = insecureClient.Get(r.URL.String())
		if err != nil {
			return err
		}
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(resp.Status)
	}

	var jwk JWKS
	if err = json.NewDecoder(resp.Body).Decode(&jwk); err != nil {
		return err
	}

	r.publicKey, err = jwk.Keys[0].DecodePublicKey()
	if err != nil {
		return err
	}

	return nil
}

// UnmarshalJSON - decodes JSON data.
func (r *JWKSArgs) UnmarshalJSON(data []byte) error {
	// subtype to avoid recursive call to UnmarshalJSON()
	type subJWKSArgs JWKSArgs
	var sr subJWKSArgs

	// IAM related envs.
	if jwksURL, ok := os.LookupEnv("MINIO_IAM_JWKS_URL"); ok {
		u, err := xnet.ParseURL(jwksURL)
		if err != nil {
			return err
		}
		sr.URL = u
	} else {
		if err := json.Unmarshal(data, &sr); err != nil {
			return err
		}
	}

	ar := JWKSArgs(sr)
	if ar.URL == nil || ar.URL.String() == "" {
		*r = ar
		return nil
	}
	if err := ar.Validate(); err != nil {
		return err
	}

	if err := ar.PopulatePublicKey(); err != nil {
		return err
	}

	*r = ar
	return nil
}

// JWT - rs client grants provider details.
type JWT struct {
	args JWKSArgs
}

func expToInt64(expI interface{}) (expAt int64, err error) {
	switch exp := expI.(type) {
	case float64:
		expAt = int64(exp)
	case int64:
		expAt = exp
	case json.Number:
		expAt, err = exp.Int64()
		if err != nil {
			return 0, err
		}
	default:
		return 0, errors.New("invalid expiry value")
	}
	return expAt, nil
}

func getDefaultExpiration(dsecs string) (time.Duration, error) {
	defaultExpiryDuration := time.Duration(60) * time.Minute // Defaults to 1hr.
	if dsecs != "" {
		expirySecs, err := strconv.ParseInt(dsecs, 10, 64)
		if err != nil {
			return 0, err
		}
		// The duration, in seconds, of the role session.
		// The value can range from 900 seconds (15 minutes)
		// to 12 hours.
		if expirySecs < 900 || expirySecs > 43200 {
			return 0, errors.New("out of range value for duration in seconds")
		}

		defaultExpiryDuration = time.Duration(expirySecs) * time.Second
	}
	return defaultExpiryDuration, nil
}

// newCustomHTTPTransport returns a new http configuration
// used while communicating with the cloud backends.
// This sets the value for MaxIdleConnsPerHost from 2 (go default)
// to 100.
func newCustomHTTPTransport(insecure bool) *http.Transport {
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:          1024,
		MaxIdleConnsPerHost:   1024,
		IdleConnTimeout:       30 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: insecure},
		DisableCompression:    true,
	}
}

// Validate - validates the access token.
func (p *JWT) Validate(token, dsecs string) (map[string]interface{}, error) {
	keyFuncCallback := func(jwtToken *jwtgo.Token) (interface{}, error) {
		if _, ok := jwtToken.Method.(*jwtgo.SigningMethodRSA); !ok {
			if _, ok = jwtToken.Method.(*jwtgo.SigningMethodECDSA); ok {
				return p.args.publicKey, nil
			}
			return nil, fmt.Errorf("Unexpected signing method: %v", jwtToken.Header["alg"])
		}
		return p.args.publicKey, nil
	}

	var claims jwtgo.MapClaims
	jwtToken, err := jwtgo.ParseWithClaims(token, &claims, keyFuncCallback)
	if err != nil {
		return nil, err
	}

	if !jwtToken.Valid {
		return nil, fmt.Errorf("Invalid token: %v", token)
	}

	expAt, err := expToInt64(claims["exp"])
	if err != nil {
		return nil, err
	}

	defaultExpiryDuration, err := getDefaultExpiration(dsecs)
	if err != nil {
		return nil, err
	}

	if time.Unix(expAt, 0).UTC().Sub(time.Now().UTC()) < defaultExpiryDuration {
		defaultExpiryDuration = time.Unix(expAt, 0).UTC().Sub(time.Now().UTC())
	}

	expiry := time.Now().UTC().Add(defaultExpiryDuration).Unix()
	if expAt < expiry {
		claims["exp"] = strconv.FormatInt(expAt, 64)
	}

	return claims, nil

}

// ID returns the provider name and authentication type.
func (p *JWT) ID() ID {
	return "jwt"
}

// NewJWT - initialize new jwt authenticator.
func NewJWT(args JWKSArgs) *JWT {
	return &JWT{
		args: args,
	}
}
<commit_msg>Re-populate public key if JWT fails to parse (#6786)<commit_after>/*
 * Minio Cloud Storage, (C) 2018 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package validator

import (
	"crypto"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"

	jwtgo "github.com/dgrijalva/jwt-go"
	xnet "github.com/minio/minio/pkg/net"
)

// JWKSArgs - RSA authentication target arguments
type JWKSArgs struct {
	URL       *xnet.URL `json:"url"`
	publicKey crypto.PublicKey
}

// Validate JWT authentication target arguments
func (r *JWKSArgs) Validate() error {
	return nil
}

// PopulatePublicKey - populates a new publickey from the JWKS URL.
func (r *JWKSArgs) PopulatePublicKey() error {
	insecureClient := &http.Client{Transport: newCustomHTTPTransport(true)}
	client := &http.Client{Transport: newCustomHTTPTransport(false)}
	resp, err := client.Get(r.URL.String())
	if err != nil {
		resp, err = insecureClient.Get(r.URL.String())
		if err != nil {
			return err
		}
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(resp.Status)
	}

	var jwk JWKS
	if err = json.NewDecoder(resp.Body).Decode(&jwk); err != nil {
		return err
	}

	r.publicKey, err = jwk.Keys[0].DecodePublicKey()
	if err != nil {
		return err
	}

	return nil
}

// UnmarshalJSON - decodes JSON data.
func (r *JWKSArgs) UnmarshalJSON(data []byte) error {
	// subtype to avoid recursive call to UnmarshalJSON()
	type subJWKSArgs JWKSArgs
	var sr subJWKSArgs

	// IAM related envs.
	if jwksURL, ok := os.LookupEnv("MINIO_IAM_JWKS_URL"); ok {
		u, err := xnet.ParseURL(jwksURL)
		if err != nil {
			return err
		}
		sr.URL = u
	} else {
		if err := json.Unmarshal(data, &sr); err != nil {
			return err
		}
	}

	ar := JWKSArgs(sr)
	if ar.URL == nil || ar.URL.String() == "" {
		*r = ar
		return nil
	}
	if err := ar.Validate(); err != nil {
		return err
	}

	if err := ar.PopulatePublicKey(); err != nil {
		return err
	}

	*r = ar
	return nil
}

// JWT - rs client grants provider details.
type JWT struct {
	args JWKSArgs
}

func expToInt64(expI interface{}) (expAt int64, err error) {
	switch exp := expI.(type) {
	case float64:
		expAt = int64(exp)
	case int64:
		expAt = exp
	case json.Number:
		expAt, err = exp.Int64()
		if err != nil {
			return 0, err
		}
	default:
		return 0, errors.New("invalid expiry value")
	}
	return expAt, nil
}

func getDefaultExpiration(dsecs string) (time.Duration, error) {
	defaultExpiryDuration := time.Duration(60) * time.Minute // Defaults to 1hr.
	if dsecs != "" {
		expirySecs, err := strconv.ParseInt(dsecs, 10, 64)
		if err != nil {
			return 0, err
		}
		// The duration, in seconds, of the role session.
		// The value can range from 900 seconds (15 minutes)
		// to 12 hours.
		if expirySecs < 900 || expirySecs > 43200 {
			return 0, errors.New("out of range value for duration in seconds")
		}

		defaultExpiryDuration = time.Duration(expirySecs) * time.Second
	}
	return defaultExpiryDuration, nil
}

// newCustomHTTPTransport returns a new http configuration
// used while communicating with the cloud backends.
// This sets the value for MaxIdleConnsPerHost from 2 (go default)
// to 100.
func newCustomHTTPTransport(insecure bool) *http.Transport {
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:          1024,
		MaxIdleConnsPerHost:   1024,
		IdleConnTimeout:       30 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: insecure},
		DisableCompression:    true,
	}
}

// Validate - validates the access token.
func (p *JWT) Validate(token, dsecs string) (map[string]interface{}, error) {
	keyFuncCallback := func(jwtToken *jwtgo.Token) (interface{}, error) {
		if _, ok := jwtToken.Method.(*jwtgo.SigningMethodRSA); !ok {
			if _, ok = jwtToken.Method.(*jwtgo.SigningMethodECDSA); ok {
				return p.args.publicKey, nil
			}
			return nil, fmt.Errorf("Unexpected signing method: %v", jwtToken.Header["alg"])
		}
		return p.args.publicKey, nil
	}

	var claims jwtgo.MapClaims
	jwtToken, err := jwtgo.ParseWithClaims(token, &claims, keyFuncCallback)
	if err != nil {
		if err = p.args.PopulatePublicKey(); err != nil {
			return nil, err
		}
		jwtToken, err = jwtgo.ParseWithClaims(token, &claims, keyFuncCallback)
		if err != nil {
			return nil, err
		}
	}

	if !jwtToken.Valid {
		return nil, fmt.Errorf("Invalid token: %v", token)
	}

	expAt, err := expToInt64(claims["exp"])
	if err != nil {
		return nil, err
	}

	defaultExpiryDuration, err := getDefaultExpiration(dsecs)
	if err != nil {
		return nil, err
	}

	if time.Unix(expAt, 0).UTC().Sub(time.Now().UTC()) < defaultExpiryDuration {
		defaultExpiryDuration = time.Unix(expAt, 0).UTC().Sub(time.Now().UTC())
	}

	expiry := time.Now().UTC().Add(defaultExpiryDuration).Unix()
	if expAt < expiry {
		claims["exp"] = strconv.FormatInt(expAt, 64)
	}

	return claims, nil

}

// ID returns the provider name and authentication type.
func (p *JWT) ID() ID {
	return "jwt"
}

// NewJWT - initialize new jwt authenticator.
func NewJWT(args JWKSArgs) *JWT {
	return &JWT{
		args: args,
	}
}
<|endoftext|>


<commit_before>package roll

import (
	"bytes"
	"encoding/json"
	"fmt"
	"hash/adler32"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"
)

const (
	// By default, all Rollbar API requests are sent to this endpoint.
	endpoint = "https://api.rollbar.com/api/1/item/"

	// Identify this Rollbar client library to the Rollbar API.
	clientName     = "go-roll"
	clientVersion  = "0.0.1"
	clientLanguage = "go"
)

var (
	// Rollbar access token for the global client. If this is blank, no items
	// will be sent to Rollbar.
	Token = ""

	// Environment for all items reported with the global client.
	Environment = "development"
)

type rollbarSuccess struct {
	Result map[string]string `json:"result"`
}

// Client reports items to a single Rollbar project.
type Client interface {
	Critical(err error, custom map[string]string) (uuid string, e error)
	Error(err error, custom map[string]string) (uuid string, e error)
	Warning(err error, custom map[string]string) (uuid string, e error)
	Info(msg string, custom map[string]string) (uuid string, e error)
	Debug(msg string, custom map[string]string) (uuid string, e error)
}

type rollbarClient struct {
	token string
	env   string
}

// New creates a new Rollbar client that reports items to the given project
// token and with the given environment (eg. "production", "development", etc).
func New(token, env string) Client {
	return &rollbarClient{token, env}
}

func Critical(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("critical", err, 3, custom)
}

func Error(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("error", err, 3, custom)
}

func Warning(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("warning", err, 3, custom)
}

func Info(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Info(msg, custom)
}

func Debug(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Debug(msg, custom)
}

func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("critical", err, 3, custom)
}

func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("error", err, 3, custom)
}

func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("warning", err, 3, custom)
}

func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("info", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("debug", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) skipStack(level string, err error, skip int, custom map[string]string) (uuid string, e error) {
	item := c.buildTraceItem(level, err, buildStack(skip), custom)
	return c.send(item)
}

func (c *rollbarClient) buildTraceItem(level string, err error, s stack, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, err.Error(), custom)
	itemData := item["data"].(map[string]interface{})
	itemData["fingerprint"] = stackFingerprint(err.Error(), s)
	itemData["body"] = map[string]interface{}{
		"trace": map[string]interface{}{
			"frames": s,
			"exception": map[string]interface{}{
				"class":   errorClass(err),
				"message": err.Error(),
			},
		},
	}

	return item
}

func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, msg, custom)
	itemData := item["data"].(map[string]interface{})
	itemData["body"] = map[string]interface{}{
		"message": map[string]interface{}{
			"body": msg,
		},
	}

	return item
}

func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} {
	hostname, _ := os.Hostname()

	return map[string]interface{}{
		"access_token": c.token,
		"data": map[string]interface{}{
			"environment": c.env,
			"title":       title,
			"level":       level,
			"timestamp":   time.Now().Unix(),
			"platform":    runtime.GOOS,
			"language":    clientLanguage,
			"server": map[string]interface{}{
				"host": hostname,
			},
			"notifier": map[string]interface{}{
				"name":    clientName,
				"version": clientVersion,
			},
			"custom": custom,
		},
	}
}

// send reports the given item to Rollbar and returns either a UUID for the
// reported item or an error.
func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) {
	if len(c.token) == 0 {
		return "", nil
	}

	jsonBody, err := json.Marshal(item)
	if err != nil {
		return "", err
	}

	resp, err := http.Post(endpoint, "application/json", bytes.NewReader(jsonBody))
	if err != nil {
		return "", err
	}
	defer func() { resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		return "", fmt.Errorf("Rollbar returned %s", resp.Status)
	}

	// Extract UUID from JSON response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", nil
	}
	success := rollbarSuccess{}
	json.Unmarshal(body, &success)

	return success.Result["uuid"], nil
}

// errorClass returns a class name for an error (eg.  "ErrUnexpectedEOF").  For
// string errors, it returns a checksum of the error string.
func errorClass(err error) string {
	class := reflect.TypeOf(err).String()
	if class == "" {
		return "panic"
	} else if class == "*errors.errorString" {
		checksum := adler32.Checksum([]byte(err.Error()))
		return fmt.Sprintf("{%x}", checksum)
	} else {
		return strings.TrimPrefix(class, "*")
	}
}
<commit_msg>Allow HTTP(S) endpoint to be configured.<commit_after>package roll

import (
	"bytes"
	"encoding/json"
	"fmt"
	"hash/adler32"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"
)

const (
	// By default, all Rollbar API requests are sent to this endpoint.
	endpoint = "https://api.rollbar.com/api/1/item/"

	// Identify this Rollbar client library to the Rollbar API.
	clientName     = "go-roll"
	clientVersion  = "0.0.1"
	clientLanguage = "go"
)

var (
	// Endpoint is the default HTTP(S) endpoint that all Rollbar API requests
	// will be sent to. By default, this is Rollbar's "Items" API endpoint. If
	// this is blank, no items will be sent to Rollbar.
	Endpoint = endpoint

	// Rollbar access token for the global client. If this is blank, no items
	// will be sent to Rollbar.
	Token = ""

	// Environment for all items reported with the global client.
	Environment = "development"
)

type rollbarSuccess struct {
	Result map[string]string `json:"result"`
}

// Client reports items to a single Rollbar project.
type Client interface {
	Critical(err error, custom map[string]string) (uuid string, e error)
	Error(err error, custom map[string]string) (uuid string, e error)
	Warning(err error, custom map[string]string) (uuid string, e error)
	Info(msg string, custom map[string]string) (uuid string, e error)
	Debug(msg string, custom map[string]string) (uuid string, e error)
}

type rollbarClient struct {
	token string
	env   string
}

// New creates a new Rollbar client that reports items to the given project
// token and with the given environment (eg. "production", "development", etc).
func New(token, env string) Client {
	return &rollbarClient{token, env}
}

func Critical(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("critical", err, 3, custom)
}

func Error(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("error", err, 3, custom)
}

func Warning(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("warning", err, 3, custom)
}

func Info(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Info(msg, custom)
}

func Debug(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Debug(msg, custom)
}

func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("critical", err, 3, custom)
}

func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("error", err, 3, custom)
}

func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("warning", err, 3, custom)
}

func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("info", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("debug", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) skipStack(level string, err error, skip int, custom map[string]string) (uuid string, e error) {
	item := c.buildTraceItem(level, err, buildStack(skip), custom)
	return c.send(item)
}

func (c *rollbarClient) buildTraceItem(level string, err error, s stack, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, err.Error(), custom)
	itemData := item["data"].(map[string]interface{})
	itemData["fingerprint"] = stackFingerprint(err.Error(), s)
	itemData["body"] = map[string]interface{}{
		"trace": map[string]interface{}{
			"frames": s,
			"exception": map[string]interface{}{
				"class":   errorClass(err),
				"message": err.Error(),
			},
		},
	}

	return item
}

func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, msg, custom)
	itemData := item["data"].(map[string]interface{})
	itemData["body"] = map[string]interface{}{
		"message": map[string]interface{}{
			"body": msg,
		},
	}

	return item
}

func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} {
	hostname, _ := os.Hostname()

	return map[string]interface{}{
		"access_token": c.token,
		"data": map[string]interface{}{
			"environment": c.env,
			"title":       title,
			"level":       level,
			"timestamp":   time.Now().Unix(),
			"platform":    runtime.GOOS,
			"language":    clientLanguage,
			"server": map[string]interface{}{
				"host": hostname,
			},
			"notifier": map[string]interface{}{
				"name":    clientName,
				"version": clientVersion,
			},
			"custom": custom,
		},
	}
}

// send reports the given item to Rollbar and returns either a UUID for the
// reported item or an error.
func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) {
	if len(c.token) == 0 || len(Endpoint) == 0 {
		return "", nil
	}

	jsonBody, err := json.Marshal(item)
	if err != nil {
		return "", err
	}

	resp, err := http.Post(Endpoint, "application/json", bytes.NewReader(jsonBody))
	if err != nil {
		return "", err
	}
	defer func() { resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		return "", fmt.Errorf("Rollbar returned %s", resp.Status)
	}

	// Extract UUID from JSON response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", nil
	}
	success := rollbarSuccess{}
	json.Unmarshal(body, &success)

	return success.Result["uuid"], nil
}

// errorClass returns a class name for an error (eg.  "ErrUnexpectedEOF").  For
// string errors, it returns a checksum of the error string.
func errorClass(err error) string {
	class := reflect.TypeOf(err).String()
	if class == "" {
		return "panic"
	} else if class == "*errors.errorString" {
		checksum := adler32.Checksum([]byte(err.Error()))
		return fmt.Sprintf("{%x}", checksum)
	} else {
		return strings.TrimPrefix(class, "*")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"
	"time"

	"github.com/jonboulle/clockwork"
	"github.com/renstrom/dedent"
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/kubectl"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/strategicpatch"
)

// ApplyOptions stores cmd.Flag values for apply.  As new fields are added,
// add them here instead of referencing the cmd.Flags()
type ApplyOptions struct {
	Filenames []string
	Recursive bool
}

const (
	// maxPatchRetry is the maximum number of conflicts retry for during a patch operation before returning failure
	maxPatchRetry = 5
	// backOffPeriod is the period to back off when apply patch resutls in error.
	backOffPeriod = 1 * time.Second
	// how many times we can retry before back off
	triesBeforeBackOff = 1
)

var (
	apply_long = dedent.Dedent(`
		Apply a configuration to a resource by filename or stdin.
		This resource will be created if it doesn't exist yet.
		To use 'apply', always create the resource initially with either 'apply' or 'create --save-config'.

		JSON and YAML formats are accepted.`)

	apply_example = dedent.Dedent(`
		# Apply the configuration in pod.json to a pod.
		kubectl apply -f ./pod.json

		# Apply the JSON passed into stdin to a pod.
		cat pod.json | kubectl apply -f -`)
)

func NewCmdApply(f *cmdutil.Factory, out io.Writer) *cobra.Command {
	options := &ApplyOptions{}

	cmd := &cobra.Command{
		Use:     "apply -f FILENAME",
		Short:   "Apply a configuration to a resource by filename or stdin",
		Long:    apply_long,
		Example: apply_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(validateArgs(cmd, args))
			cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
			cmdutil.CheckErr(RunApply(f, cmd, out, options))
		},
	}

	usage := "Filename, directory, or URL to file that contains the configuration to apply"
	kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
	cmd.MarkFlagRequired("filename")
	cmd.Flags().Bool("overwrite", true, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
	cmdutil.AddOutputFlagsForMutation(cmd)
	cmdutil.AddRecordFlag(cmd)
	cmdutil.AddInclude3rdPartyFlags(cmd)
	return cmd
}

func validateArgs(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return cmdutil.UsageError(cmd, "Unexpected args: %v", args)
	}

	return nil
}

func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *ApplyOptions) error {
	shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	encoder := f.JSONEncoder()
	decoder := f.Decoder(false)

	count := 0
	err = r.Visit(func(info *resource.Info, err error) error {
		// In this method, info.Object contains the object retrieved from the server
		// and info.VersionedObject contains the object decoded from the input source.
		if err != nil {
			return err
		}

		// Get the modified configuration of the object. Embed the result
		// as an annotation in the modified configuration, so that it will appear
		// in the patch sent to the server.
		modified, err := kubectl.GetModifiedConfiguration(info, true, encoder)
		if err != nil {
			return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err)
		}

		if err := info.Get(); err != nil {
			if !errors.IsNotFound(err) {
				return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
			}
			// Create the resource if it doesn't exist
			// First, update the annotation used by kubectl apply
			if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil {
				return cmdutil.AddSourceToErr("creating", info.Source, err)
			}

			if cmdutil.ShouldRecord(cmd, info) {
				if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil {
					return cmdutil.AddSourceToErr("creating", info.Source, err)
				}
			}

			// Then create the resource and skip the three-way merge
			if err := createAndRefresh(info); err != nil {
				return cmdutil.AddSourceToErr("creating", info.Source, err)
			}
			count++
			cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "created")
			return nil
		}

		overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
		helper := resource.NewHelper(info.Client, info.Mapping)
		patcher := NewPatcher(encoder, decoder, info.Mapping, helper, overwrite)

		patchBytes, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name)
		if err != nil {
			return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err)
		}

		if cmdutil.ShouldRecord(cmd, info) {
			patch, err := cmdutil.ChangeResourcePatch(info, f.Command())
			if err != nil {
				return err
			}
			_, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch)
			if err != nil {
				return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patch, info), info.Source, err)
			}
		}

		count++
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "configured")
		return nil
	})

	if err != nil {
		return err
	}

	if count == 0 {
		return fmt.Errorf("no objects passed to apply")
	}

	return nil
}

type patcher struct {
	encoder runtime.Encoder
	decoder runtime.Decoder

	mapping *meta.RESTMapping
	helper  *resource.Helper

	overwrite bool
	backOff   clockwork.Clock
}

func NewPatcher(encoder runtime.Encoder, decoder runtime.Decoder, mapping *meta.RESTMapping, helper *resource.Helper, overwrite bool) *patcher {
	return &patcher{
		encoder:   encoder,
		decoder:   decoder,
		mapping:   mapping,
		helper:    helper,
		overwrite: overwrite,
		backOff:   clockwork.NewRealClock(),
	}
}

func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string) ([]byte, error) {
	// Serialize the current configuration of the object from the server.
	current, err := runtime.Encode(p.encoder, obj)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", obj), source, err)
	}

	// Retrieve the original configuration of the object from the annotation.
	original, err := kubectl.GetOriginalConfiguration(p.mapping, obj)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", obj), source, err)
	}

	// Create the versioned struct from the original from the server for
	// strategic patch.
	// TODO: Move all structs in apply to use raw data. Can be done once
	// builder has a RawResult method which delivers raw data instead of
	// internal objects.
	versionedObject, _, err := p.decoder.Decode(current, nil, nil)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("converting encoded server-side object back to versioned struct:\n%v\nfor:", obj), source, err)
	}

	// Compute a three way strategic merge patch to send to server.
	patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite)
	if err != nil {
		format := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf(format, original, modified, current), source, err)
	}

	_, err = p.helper.Patch(namespace, name, api.StrategicMergePatchType, patch)
	return patch, err
}

func (p *patcher) patch(current runtime.Object, modified []byte, source, namespace, name string) ([]byte, error) {
	var getErr error
	patchBytes, err := p.patchSimple(current, modified, source, namespace, name)
	for i := 1; i <= maxPatchRetry && errors.IsConflict(err); i++ {
		if i > triesBeforeBackOff {
			p.backOff.Sleep(backOffPeriod)
		}
		current, getErr = p.helper.Get(namespace, name, false)
		if getErr != nil {
			return nil, getErr
		}
		patchBytes, err = p.patchSimple(current, modified, source, namespace, name)
	}

	return patchBytes, err
}
<commit_msg>make apply use the correct versioned obj<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"
	"time"

	"github.com/jonboulle/clockwork"
	"github.com/renstrom/dedent"
	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/kubectl"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/strategicpatch"
)

// ApplyOptions stores cmd.Flag values for apply.  As new fields are added,
// add them here instead of referencing the cmd.Flags()
type ApplyOptions struct {
	Filenames []string
	Recursive bool
}

const (
	// maxPatchRetry is the maximum number of conflicts retry for during a patch operation before returning failure
	maxPatchRetry = 5
	// backOffPeriod is the period to back off when apply patch resutls in error.
	backOffPeriod = 1 * time.Second
	// how many times we can retry before back off
	triesBeforeBackOff = 1
)

var (
	apply_long = dedent.Dedent(`
		Apply a configuration to a resource by filename or stdin.
		This resource will be created if it doesn't exist yet.
		To use 'apply', always create the resource initially with either 'apply' or 'create --save-config'.

		JSON and YAML formats are accepted.`)

	apply_example = dedent.Dedent(`
		# Apply the configuration in pod.json to a pod.
		kubectl apply -f ./pod.json

		# Apply the JSON passed into stdin to a pod.
		cat pod.json | kubectl apply -f -`)
)

func NewCmdApply(f *cmdutil.Factory, out io.Writer) *cobra.Command {
	options := &ApplyOptions{}

	cmd := &cobra.Command{
		Use:     "apply -f FILENAME",
		Short:   "Apply a configuration to a resource by filename or stdin",
		Long:    apply_long,
		Example: apply_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(validateArgs(cmd, args))
			cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
			cmdutil.CheckErr(RunApply(f, cmd, out, options))
		},
	}

	usage := "Filename, directory, or URL to file that contains the configuration to apply"
	kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
	cmd.MarkFlagRequired("filename")
	cmd.Flags().Bool("overwrite", true, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
	cmdutil.AddOutputFlagsForMutation(cmd)
	cmdutil.AddRecordFlag(cmd)
	cmdutil.AddInclude3rdPartyFlags(cmd)
	return cmd
}

func validateArgs(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return cmdutil.UsageError(cmd, "Unexpected args: %v", args)
	}

	return nil
}

func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *ApplyOptions) error {
	shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	encoder := f.JSONEncoder()
	decoder := f.Decoder(false)

	count := 0
	err = r.Visit(func(info *resource.Info, err error) error {
		// In this method, info.Object contains the object retrieved from the server
		// and info.VersionedObject contains the object decoded from the input source.
		if err != nil {
			return err
		}

		// Get the modified configuration of the object. Embed the result
		// as an annotation in the modified configuration, so that it will appear
		// in the patch sent to the server.
		modified, err := kubectl.GetModifiedConfiguration(info, true, encoder)
		if err != nil {
			return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err)
		}

		if err := info.Get(); err != nil {
			if !errors.IsNotFound(err) {
				return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
			}
			// Create the resource if it doesn't exist
			// First, update the annotation used by kubectl apply
			if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil {
				return cmdutil.AddSourceToErr("creating", info.Source, err)
			}

			if cmdutil.ShouldRecord(cmd, info) {
				if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil {
					return cmdutil.AddSourceToErr("creating", info.Source, err)
				}
			}

			// Then create the resource and skip the three-way merge
			if err := createAndRefresh(info); err != nil {
				return cmdutil.AddSourceToErr("creating", info.Source, err)
			}
			count++
			cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "created")
			return nil
		}

		overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
		helper := resource.NewHelper(info.Client, info.Mapping)
		patcher := NewPatcher(encoder, decoder, info.Mapping, helper, overwrite)

		patchBytes, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name)
		if err != nil {
			return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err)
		}

		if cmdutil.ShouldRecord(cmd, info) {
			patch, err := cmdutil.ChangeResourcePatch(info, f.Command())
			if err != nil {
				return err
			}
			_, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch)
			if err != nil {
				return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patch, info), info.Source, err)
			}
		}

		count++
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "configured")
		return nil
	})

	if err != nil {
		return err
	}

	if count == 0 {
		return fmt.Errorf("no objects passed to apply")
	}

	return nil
}

type patcher struct {
	encoder runtime.Encoder
	decoder runtime.Decoder

	mapping *meta.RESTMapping
	helper  *resource.Helper

	overwrite bool
	backOff   clockwork.Clock
}

func NewPatcher(encoder runtime.Encoder, decoder runtime.Decoder, mapping *meta.RESTMapping, helper *resource.Helper, overwrite bool) *patcher {
	return &patcher{
		encoder:   encoder,
		decoder:   decoder,
		mapping:   mapping,
		helper:    helper,
		overwrite: overwrite,
		backOff:   clockwork.NewRealClock(),
	}
}

func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string) ([]byte, error) {
	// Serialize the current configuration of the object from the server.
	current, err := runtime.Encode(p.encoder, obj)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", obj), source, err)
	}

	// Retrieve the original configuration of the object from the annotation.
	original, err := kubectl.GetOriginalConfiguration(p.mapping, obj)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", obj), source, err)
	}

	// Create the versioned struct from the type defined in the restmapping
	// (which is the API version we'll be submitting the patch to)
	versionedObject, err := api.Scheme.New(p.mapping.GroupVersionKind)
	if err != nil {
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf("getting instance of versioned object for %v:", p.mapping.GroupVersionKind), source, err)
	}

	// Compute a three way strategic merge patch to send to server.
	patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite)
	if err != nil {
		format := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
		return nil, cmdutil.AddSourceToErr(fmt.Sprintf(format, original, modified, current), source, err)
	}

	_, err = p.helper.Patch(namespace, name, api.StrategicMergePatchType, patch)
	return patch, err
}

func (p *patcher) patch(current runtime.Object, modified []byte, source, namespace, name string) ([]byte, error) {
	var getErr error
	patchBytes, err := p.patchSimple(current, modified, source, namespace, name)
	for i := 1; i <= maxPatchRetry && errors.IsConflict(err); i++ {
		if i > triesBeforeBackOff {
			p.backOff.Sleep(backOffPeriod)
		}
		current, getErr = p.helper.Get(namespace, name, false)
		if getErr != nil {
			return nil, getErr
		}
		patchBytes, err = p.patchSimple(current, modified, source, namespace, name)
	}

	return patchBytes, err
}
<|endoftext|>


<commit_before>package gordon

import (
	"time"

	"github.com/vito/gordon/warden"
)

type Client struct {
	SocketPath string

	connectionProvider ConnectionProvider
	connection         chan *Connection
}

func NewClient(cp ConnectionProvider) *Client {
	return &Client{
		connectionProvider: cp,
		connection:         make(chan *Connection),
	}
}

func (c *Client) Connect() error {
	conn, err := c.connectionProvider.ProvideConnection()
	if err != nil {
		return err
	}

	go c.serveConnections(conn)

	return nil
}

func (c *Client) Create() (*warden.CreateResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Create()
}

func (c *Client) Destroy(handle string) (*warden.DestroyResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Destroy(handle)
}

func (c *Client) Spawn(handle, script string, discardOutput bool) (*warden.SpawnResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Spawn(handle, script, discardOutput)
}

func (c *Client) NetIn(handle string) (*warden.NetInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.NetIn(handle)
}

func (c *Client) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitMemory(handle, limit)
}

func (c *Client) GetMemoryLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetMemoryLimit(handle)
}

func (c *Client) LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitDisk(handle, limit)
}

func (c *Client) GetDiskLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetDiskLimit(handle)
}

func (c *Client) List() (*warden.ListResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.List()
}

func (c *Client) Info(handle string) (*warden.InfoResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Info(handle)
}

func (c *Client) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.CopyIn(handle, src, dst)
}

func (c *Client) Stream(handle string, jobId uint32) (chan *warden.StreamResponse, error) {
	conn := c.acquireConnection()

	responses, done, err := conn.Stream(handle, jobId)
	if err != nil {
		c.release(conn)
		return nil, err
	}

	go func() {
		<-done
		c.release(conn)
	}()

	return responses, nil
}

func (c *Client) Run(handle, script string) (*warden.RunResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Run(handle, script)
}

func (c *Client) serveConnections(conn *Connection) {
	select {
	case <-conn.disconnected:

	case c.connection <- conn:

	case <-time.After(5 * time.Second):
		conn.Close()
	}
}

func (c *Client) release(conn *Connection) {
	go c.serveConnections(conn)
}

func (c *Client) acquireConnection() *Connection {
	select {
	case conn := <-c.connection:
		return conn

	case <-time.After(1 * time.Second):
		return c.connect()
	}
}

func (c *Client) connect() *Connection {
	for {
		conn, err := c.connectionProvider.ProvideConnection()
		if err == nil {
			return conn
		}

		time.Sleep(500 * time.Millisecond)
	}
}
<commit_msg>remove stale SocketPath attribute from Client<commit_after>package gordon

import (
	"time"

	"github.com/vito/gordon/warden"
)

type Client struct {
	connectionProvider ConnectionProvider
	connection         chan *Connection
}

func NewClient(cp ConnectionProvider) *Client {
	return &Client{
		connectionProvider: cp,
		connection:         make(chan *Connection),
	}
}

func (c *Client) Connect() error {
	conn, err := c.connectionProvider.ProvideConnection()
	if err != nil {
		return err
	}

	go c.serveConnections(conn)

	return nil
}

func (c *Client) Create() (*warden.CreateResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Create()
}

func (c *Client) Destroy(handle string) (*warden.DestroyResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Destroy(handle)
}

func (c *Client) Spawn(handle, script string, discardOutput bool) (*warden.SpawnResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Spawn(handle, script, discardOutput)
}

func (c *Client) NetIn(handle string) (*warden.NetInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.NetIn(handle)
}

func (c *Client) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitMemory(handle, limit)
}

func (c *Client) GetMemoryLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetMemoryLimit(handle)
}

func (c *Client) LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitDisk(handle, limit)
}

func (c *Client) GetDiskLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetDiskLimit(handle)
}

func (c *Client) List() (*warden.ListResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.List()
}

func (c *Client) Info(handle string) (*warden.InfoResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Info(handle)
}

func (c *Client) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.CopyIn(handle, src, dst)
}

func (c *Client) Stream(handle string, jobId uint32) (chan *warden.StreamResponse, error) {
	conn := c.acquireConnection()

	responses, done, err := conn.Stream(handle, jobId)
	if err != nil {
		c.release(conn)
		return nil, err
	}

	go func() {
		<-done
		c.release(conn)
	}()

	return responses, nil
}

func (c *Client) Run(handle, script string) (*warden.RunResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Run(handle, script)
}

func (c *Client) serveConnections(conn *Connection) {
	select {
	case <-conn.disconnected:

	case c.connection <- conn:

	case <-time.After(5 * time.Second):
		conn.Close()
	}
}

func (c *Client) release(conn *Connection) {
	go c.serveConnections(conn)
}

func (c *Client) acquireConnection() *Connection {
	select {
	case conn := <-c.connection:
		return conn

	case <-time.After(1 * time.Second):
		return c.connect()
	}
}

func (c *Client) connect() *Connection {
	for {
		conn, err := c.connectionProvider.ProvideConnection()
		if err == nil {
			return conn
		}

		time.Sleep(500 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"strings"
	"time"

	"github.com/gosimple/slug"
	"github.com/grafana/grafana/pkg/components/simplejson"
)

// Typed errors
var (
	ErrDashboardNotFound           = errors.New("Dashboard not found")
	ErrDashboardSnapshotNotFound   = errors.New("Dashboard snapshot not found")
	ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
	ErrDashboardVersionMismatch    = errors.New("The dashboard has been changed by someone else")
	ErrDashboardTitleEmpty         = errors.New("Dashboard title cannot be empty")
)

type UpdatePluginDashboardError struct {
	PluginId string
}

func (d UpdatePluginDashboardError) Error() string {
	return "Dashboard belong to plugin"
}

var (
	DashTypeJson     = "file"
	DashTypeDB       = "db"
	DashTypeScript   = "script"
	DashTypeSnapshot = "snapshot"
)

// Dashboard model
type Dashboard struct {
	Id       int64
	Slug     string
	OrgId    int64
	GnetId   int64
	Version  int
	PluginId string

	Created time.Time
	Updated time.Time

	UpdatedBy int64
	CreatedBy int64
	ParentId  int64
	IsFolder  bool
	HasAcl    bool

	Title string
	Data  *simplejson.Json
}

// NewDashboard creates a new dashboard
func NewDashboard(title string) *Dashboard {
	dash := &Dashboard{}
	dash.Data = simplejson.New()
	dash.Data.Set("title", title)
	dash.Title = title
	dash.Created = time.Now()
	dash.Updated = time.Now()
	dash.UpdateSlug()
	return dash
}

// GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string {
	return dash.Data.Get("tags").MustStringArray()
}

func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
	dash := &Dashboard{}
	dash.Data = data
	dash.Title = dash.Data.Get("title").MustString()
	dash.UpdateSlug()

	if id, err := dash.Data.Get("id").Float64(); err == nil {
		dash.Id = int64(id)

		if version, err := dash.Data.Get("version").Float64(); err == nil {
			dash.Version = int(version)
			dash.Updated = time.Now()
		}
	} else {
		dash.Data.Set("version", 0)
		dash.Created = time.Now()
		dash.Updated = time.Now()
	}

	if gnetId, err := dash.Data.Get("gnetId").Float64(); err == nil {
		dash.GnetId = int64(gnetId)
	}

	return dash
}

// GetDashboardModel turns the command into the savable model
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
	dash := NewDashboardFromJson(cmd.Dashboard)
	userId := cmd.UserId

	if userId == 0 {
		userId = -1
	}

	if dash.Data.Get("version").MustInt(0) == 0 {
		dash.CreatedBy = userId
	}

	dash.UpdatedBy = userId
	dash.OrgId = cmd.OrgId
	dash.PluginId = cmd.PluginId
	dash.IsFolder = cmd.IsFolder
	dash.ParentId = cmd.ParentId
	dash.UpdateSlug()
	return dash
}

// GetString a
func (dash *Dashboard) GetString(prop string, defaultValue string) string {
	return dash.Data.Get(prop).MustString(defaultValue)
}

// UpdateSlug updates the slug
func (dash *Dashboard) UpdateSlug() {
	title := strings.ToLower(dash.Data.Get("title").MustString())
	dash.Slug = slug.Make(title)
}

//
// COMMANDS
//

type SaveDashboardCommand struct {
	Dashboard    *simplejson.Json `json:"dashboard" binding:"Required"`
	UserId       int64            `json:"userId"`
	Overwrite    bool             `json:"overwrite"`
	Message      string           `json:"message"`
	OrgId        int64            `json:"-"`
	RestoredFrom int              `json:"-"`
	PluginId     string           `json:"-"`
	ParentId  int64               `json:"parentId"`
	IsFolder  bool                `json:"isFolder"`

	Result *Dashboard
}

type DeleteDashboardCommand struct {
	Slug  string
	OrgId int64
}

//
// QUERIES
//

type GetDashboardQuery struct {
	Slug  string // required if no Id is specified
	Id    int64  // optional if slug is set
	OrgId int64

	Result *Dashboard
}

type DashboardTagCloudItem struct {
	Term  string `json:"term"`
	Count int    `json:"count"`
}

type GetDashboardTagsQuery struct {
	OrgId  int64
	Result []*DashboardTagCloudItem
}

type GetDashboardsQuery struct {
	DashboardIds []int64
	Result       []*Dashboard
}

type GetDashboardsByPluginIdQuery struct {
	OrgId    int64
	PluginId string
	Result   []*Dashboard
}

type GetDashboardSlugByIdQuery struct {
	Id     int64
	Result string
}

type GetAllowedDashboardsQuery struct {
	UserId   int64
	OrgId    int64
	DashList []int64

	Result []int64
}
<commit_msg>WIP: fix go fmt error<commit_after>package models

import (
	"errors"
	"strings"
	"time"

	"github.com/gosimple/slug"
	"github.com/grafana/grafana/pkg/components/simplejson"
)

// Typed errors
var (
	ErrDashboardNotFound           = errors.New("Dashboard not found")
	ErrDashboardSnapshotNotFound   = errors.New("Dashboard snapshot not found")
	ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
	ErrDashboardVersionMismatch    = errors.New("The dashboard has been changed by someone else")
	ErrDashboardTitleEmpty         = errors.New("Dashboard title cannot be empty")
)

type UpdatePluginDashboardError struct {
	PluginId string
}

func (d UpdatePluginDashboardError) Error() string {
	return "Dashboard belong to plugin"
}

var (
	DashTypeJson     = "file"
	DashTypeDB       = "db"
	DashTypeScript   = "script"
	DashTypeSnapshot = "snapshot"
)

// Dashboard model
type Dashboard struct {
	Id       int64
	Slug     string
	OrgId    int64
	GnetId   int64
	Version  int
	PluginId string

	Created time.Time
	Updated time.Time

	UpdatedBy int64
	CreatedBy int64
	ParentId  int64
	IsFolder  bool
	HasAcl    bool

	Title string
	Data  *simplejson.Json
}

// NewDashboard creates a new dashboard
func NewDashboard(title string) *Dashboard {
	dash := &Dashboard{}
	dash.Data = simplejson.New()
	dash.Data.Set("title", title)
	dash.Title = title
	dash.Created = time.Now()
	dash.Updated = time.Now()
	dash.UpdateSlug()
	return dash
}

// GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string {
	return dash.Data.Get("tags").MustStringArray()
}

func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
	dash := &Dashboard{}
	dash.Data = data
	dash.Title = dash.Data.Get("title").MustString()
	dash.UpdateSlug()

	if id, err := dash.Data.Get("id").Float64(); err == nil {
		dash.Id = int64(id)

		if version, err := dash.Data.Get("version").Float64(); err == nil {
			dash.Version = int(version)
			dash.Updated = time.Now()
		}
	} else {
		dash.Data.Set("version", 0)
		dash.Created = time.Now()
		dash.Updated = time.Now()
	}

	if gnetId, err := dash.Data.Get("gnetId").Float64(); err == nil {
		dash.GnetId = int64(gnetId)
	}

	return dash
}

// GetDashboardModel turns the command into the savable model
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
	dash := NewDashboardFromJson(cmd.Dashboard)
	userId := cmd.UserId

	if userId == 0 {
		userId = -1
	}

	if dash.Data.Get("version").MustInt(0) == 0 {
		dash.CreatedBy = userId
	}

	dash.UpdatedBy = userId
	dash.OrgId = cmd.OrgId
	dash.PluginId = cmd.PluginId
	dash.IsFolder = cmd.IsFolder
	dash.ParentId = cmd.ParentId
	dash.UpdateSlug()
	return dash
}

// GetString a
func (dash *Dashboard) GetString(prop string, defaultValue string) string {
	return dash.Data.Get(prop).MustString(defaultValue)
}

// UpdateSlug updates the slug
func (dash *Dashboard) UpdateSlug() {
	title := strings.ToLower(dash.Data.Get("title").MustString())
	dash.Slug = slug.Make(title)
}

//
// COMMANDS
//

type SaveDashboardCommand struct {
	Dashboard    *simplejson.Json `json:"dashboard" binding:"Required"`
	UserId       int64            `json:"userId"`
	Overwrite    bool             `json:"overwrite"`
	Message      string           `json:"message"`
	OrgId        int64            `json:"-"`
	RestoredFrom int              `json:"-"`
	PluginId     string           `json:"-"`
	ParentId     int64            `json:"parentId"`
	IsFolder     bool             `json:"isFolder"`

	Result *Dashboard
}

type DeleteDashboardCommand struct {
	Slug  string
	OrgId int64
}

//
// QUERIES
//

type GetDashboardQuery struct {
	Slug  string // required if no Id is specified
	Id    int64  // optional if slug is set
	OrgId int64

	Result *Dashboard
}

type DashboardTagCloudItem struct {
	Term  string `json:"term"`
	Count int    `json:"count"`
}

type GetDashboardTagsQuery struct {
	OrgId  int64
	Result []*DashboardTagCloudItem
}

type GetDashboardsQuery struct {
	DashboardIds []int64
	Result       []*Dashboard
}

type GetDashboardsByPluginIdQuery struct {
	OrgId    int64
	PluginId string
	Result   []*Dashboard
}

type GetDashboardSlugByIdQuery struct {
	Id     int64
	Result string
}

type GetAllowedDashboardsQuery struct {
	UserId   int64
	OrgId    int64
	DashList []int64

	Result []int64
}
<|endoftext|>


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

package registry

import (
	"bytes"
	"fmt"
	"hash"
	"hash/fnv"
	"sort"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/prometheus/statsd_exporter/pkg/clock"
	"github.com/prometheus/statsd_exporter/pkg/mapper"
	"github.com/prometheus/statsd_exporter/pkg/metrics"
)

// uncheckedCollector wraps a Collector but its Describe method yields no Desc.
// This allows incoming metrics to have inconsistent label sets
type uncheckedCollector struct {
	c prometheus.Collector
}

func (u uncheckedCollector) Describe(_ chan<- *prometheus.Desc) {}
func (u uncheckedCollector) Collect(c chan<- prometheus.Metric) {
	u.c.Collect(c)
}

type Registry struct {
	Registerer prometheus.Registerer
	Metrics    map[string]metrics.Metric
	Mapper     *mapper.MetricMapper
	// The below value and label variables are allocated in the registry struct
	// so that we don't have to allocate them every time have to compute a label
	// hash.
	ValueBuf, NameBuf bytes.Buffer
	Hasher            hash.Hash64
}

func NewRegistry(reg prometheus.Registerer, mapper *mapper.MetricMapper) *Registry {
	return &Registry{
		Registerer: reg,
		Metrics:    make(map[string]metrics.Metric),
		Mapper:     mapper,
		Hasher:     fnv.New64a(),
	}
}

func (r *Registry) MetricConflicts(metricName string, metricType metrics.MetricType) bool {
	vector, hasMetrics := r.Metrics[metricName]
	if !hasMetrics {
		// No metrics.Metric with this name exists
		return false
	}

	if vector.MetricType == metricType {
		// We've found a copy of this metrics.Metric with this type, but different
		// labels, so it's safe to create a new one.
		return false
	}

	// The metrics.Metric exists, but it's of a different type than we're trying to
	// create.
	return true
}

func (r *Registry) StoreCounter(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.CounterVec, c prometheus.Counter, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, c, metrics.CounterMetricType, ttl)
}

func (r *Registry) StoreGauge(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.GaugeVec, g prometheus.Gauge, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, g, metrics.GaugeMetricType, ttl)
}

func (r *Registry) StoreHistogram(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.HistogramVec, o prometheus.Observer, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, o, metrics.HistogramMetricType, ttl)
}

func (r *Registry) StoreSummary(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.SummaryVec, o prometheus.Observer, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, o, metrics.SummaryMetricType, ttl)
}

func (r *Registry) Store(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vh metrics.VectorHolder, mh metrics.MetricHolder, metricType metrics.MetricType, ttl time.Duration) {
	metric, hasMetrics := r.Metrics[metricName]
	if !hasMetrics {
		metric.MetricType = metricType
		metric.Vectors = make(map[metrics.NameHash]*metrics.Vector)
		metric.Metrics = make(map[metrics.ValueHash]*metrics.RegisteredMetric)

		r.Metrics[metricName] = metric
	}

	v, ok := metric.Vectors[hash.Names]
	if !ok {
		v = &metrics.Vector{Holder: vh}
		metric.Vectors[hash.Names] = v
	}

	now := clock.Now()
	rm, ok := metric.Metrics[hash.Values]
	if !ok {
		rm = &metrics.RegisteredMetric{
			LastRegisteredAt: now,
			Labels:           labels,
			TTL:              ttl,
			Metric:           mh,
			VecKey:           hash.Names,
		}
		metric.Metrics[hash.Values] = rm
		v.RefCount++
		return
	}
	rm.LastRegisteredAt = now
	// Update ttl from mapping
	rm.TTL = ttl
}

func (r *Registry) Get(metricName string, hash metrics.LabelHash, metricType metrics.MetricType) (metrics.VectorHolder, metrics.MetricHolder) {
	metric, hasMetric := r.Metrics[metricName]

	if !hasMetric {
		return nil, nil
	}
	if metric.MetricType != metricType {
		return nil, nil
	}

	rm, ok := metric.Metrics[hash.Values]
	if ok {
		now := clock.Now()
		rm.LastRegisteredAt = now
		return metric.Vectors[hash.Names].Holder, rm.Metric
	}

	vector, ok := metric.Vectors[hash.Names]
	if ok {
		return vector.Holder, nil
	}

	return nil, nil
}

func (r *Registry) GetCounter(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Counter, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.CounterMetricType)
	if mh != nil {
		return mh.(prometheus.Counter), nil
	}

	if r.MetricConflicts(metricName, metrics.CounterMetricType) {
		return nil, fmt.Errorf("metric with name %s is already registered", metricName)
	}

	var counterVec *prometheus.CounterVec
	if vh == nil {
		metricsCount.WithLabelValues("counter").Inc()
		counterVec = prometheus.NewCounterVec(prometheus.CounterOpts{
			Name: metricName,
			Help: help,
		}, labelNames)

		if err := r.Registerer.Register(uncheckedCollector{counterVec}); err != nil {
			return nil, err
		}
	} else {
		counterVec = vh.(*prometheus.CounterVec)
	}

	var counter prometheus.Counter
	var err error
	if counter, err = counterVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreCounter(metricName, hash, labels, counterVec, counter, mapping.Ttl)

	return counter, nil
}

func (r *Registry) GetGauge(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Gauge, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.GaugeMetricType)
	if mh != nil {
		return mh.(prometheus.Gauge), nil
	}

	if r.MetricConflicts(metricName, metrics.GaugeMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var gaugeVec *prometheus.GaugeVec
	if vh == nil {
		metricsCount.WithLabelValues("gauge").Inc()
		gaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: metricName,
			Help: help,
		}, labelNames)

		if err := r.Registerer.Register(uncheckedCollector{gaugeVec}); err != nil {
			return nil, err
		}
	} else {
		gaugeVec = vh.(*prometheus.GaugeVec)
	}

	var gauge prometheus.Gauge
	var err error
	if gauge, err = gaugeVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreGauge(metricName, hash, labels, gaugeVec, gauge, mapping.Ttl)

	return gauge, nil
}

func (r *Registry) GetHistogram(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Observer, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.HistogramMetricType)
	if mh != nil {
		return mh.(prometheus.Observer), nil
	}

	if r.MetricConflicts(metricName, metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_sum", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_count", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_bucket", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var histogramVec *prometheus.HistogramVec
	if vh == nil {
		metricsCount.WithLabelValues("histogram").Inc()
		buckets := r.Mapper.Defaults.HistogramOptions.Buckets
		if mapping.HistogramOptions != nil && len(mapping.HistogramOptions.Buckets) > 0 {
			buckets = mapping.HistogramOptions.Buckets
		}
		histogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    metricName,
			Help:    help,
			Buckets: buckets,
		}, labelNames)

		if err := prometheus.Register(uncheckedCollector{histogramVec}); err != nil {
			return nil, err
		}
	} else {
		histogramVec = vh.(*prometheus.HistogramVec)
	}

	var observer prometheus.Observer
	var err error
	if observer, err = histogramVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreHistogram(metricName, hash, labels, histogramVec, observer, mapping.Ttl)

	return observer, nil
}

func (r *Registry) GetSummary(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Observer, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.SummaryMetricType)
	if mh != nil {
		return mh.(prometheus.Observer), nil
	}

	if r.MetricConflicts(metricName, metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_sum", metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_count", metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var summaryVec *prometheus.SummaryVec
	if vh == nil {
		metricsCount.WithLabelValues("summary").Inc()
		quantiles := r.Mapper.Defaults.SummaryOptions.Quantiles
		if mapping != nil && mapping.SummaryOptions != nil && len(mapping.SummaryOptions.Quantiles) > 0 {
			quantiles = mapping.SummaryOptions.Quantiles
		}
		summaryOptions := mapper.SummaryOptions{}
		if mapping != nil && mapping.SummaryOptions != nil {
			summaryOptions = *mapping.SummaryOptions
		}
		objectives := make(map[float64]float64)
		for _, q := range quantiles {
			objectives[q.Quantile] = q.Error
		}
		// In the case of no mapping file, explicitly define the default quantiles
		if len(objectives) == 0 {
			objectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
		}
		summaryVec = prometheus.NewSummaryVec(prometheus.SummaryOpts{
			Name:       metricName,
			Help:       help,
			Objectives: objectives,
			MaxAge:     summaryOptions.MaxAge,
			AgeBuckets: summaryOptions.AgeBuckets,
			BufCap:     summaryOptions.BufCap,
		}, labelNames)

		if err := prometheus.Register(uncheckedCollector{summaryVec}); err != nil {
			return nil, err
		}
	} else {
		summaryVec = vh.(*prometheus.SummaryVec)
	}

	var observer prometheus.Observer
	var err error
	if observer, err = summaryVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreSummary(metricName, hash, labels, summaryVec, observer, mapping.Ttl)

	return observer, nil
}

func (r *Registry) RemoveStaleMetrics() {
	now := clock.Now()
	// delete timeseries with expired ttl
	for _, metric := range r.Metrics {
		for hash, rm := range metric.Metrics {
			if rm.TTL == 0 {
				continue
			}
			if rm.LastRegisteredAt.Add(rm.TTL).Before(now) {
				metric.Vectors[rm.VecKey].Holder.Delete(rm.Labels)
				metric.Vectors[rm.VecKey].RefCount--
				delete(metric.Metrics, hash)
			}
		}
	}
}

// Calculates a hash of both the label names and the label names and values.
func (r *Registry) HashLabels(labels prometheus.Labels) (metrics.LabelHash, []string) {
	r.Hasher.Reset()
	r.NameBuf.Reset()
	r.ValueBuf.Reset()
	labelNames := make([]string, 0, len(labels))

	for labelName := range labels {
		labelNames = append(labelNames, labelName)
	}
	sort.Strings(labelNames)

	r.ValueBuf.WriteByte(model.SeparatorByte)
	for _, labelName := range labelNames {
		r.ValueBuf.WriteString(labels[labelName])
		r.ValueBuf.WriteByte(model.SeparatorByte)

		r.NameBuf.WriteString(labelName)
		r.NameBuf.WriteByte(model.SeparatorByte)
	}

	lh := metrics.LabelHash{}
	r.Hasher.Write(r.NameBuf.Bytes())
	lh.Names = metrics.NameHash(r.Hasher.Sum64())

	// Now add the values to the names we've already hashed.
	r.Hasher.Write(r.ValueBuf.Bytes())
	lh.Values = metrics.ValueHash(r.Hasher.Sum64())

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

package registry

import (
	"bytes"
	"fmt"
	"hash"
	"hash/fnv"
	"sort"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/prometheus/statsd_exporter/pkg/clock"
	"github.com/prometheus/statsd_exporter/pkg/mapper"
	"github.com/prometheus/statsd_exporter/pkg/metrics"
)

// uncheckedCollector wraps a Collector but its Describe method yields no Desc.
// This allows incoming metrics to have inconsistent label sets
type uncheckedCollector struct {
	c prometheus.Collector
}

func (u uncheckedCollector) Describe(_ chan<- *prometheus.Desc) {}
func (u uncheckedCollector) Collect(c chan<- prometheus.Metric) {
	u.c.Collect(c)
}

type Registry struct {
	Registerer prometheus.Registerer
	Metrics    map[string]metrics.Metric
	Mapper     *mapper.MetricMapper
	// The below value and label variables are allocated in the registry struct
	// so that we don't have to allocate them every time have to compute a label
	// hash.
	ValueBuf, NameBuf bytes.Buffer
	Hasher            hash.Hash64
}

func NewRegistry(reg prometheus.Registerer, mapper *mapper.MetricMapper) *Registry {
	return &Registry{
		Registerer: reg,
		Metrics:    make(map[string]metrics.Metric),
		Mapper:     mapper,
		Hasher:     fnv.New64a(),
	}
}

func (r *Registry) MetricConflicts(metricName string, metricType metrics.MetricType) bool {
	vector, hasMetrics := r.Metrics[metricName]
	if !hasMetrics {
		// No metrics.Metric with this name exists
		return false
	}

	if vector.MetricType == metricType {
		// We've found a copy of this metrics.Metric with this type, but different
		// labels, so it's safe to create a new one.
		return false
	}

	// The metrics.Metric exists, but it's of a different type than we're trying to
	// create.
	return true
}

func (r *Registry) StoreCounter(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.CounterVec, c prometheus.Counter, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, c, metrics.CounterMetricType, ttl)
}

func (r *Registry) StoreGauge(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.GaugeVec, g prometheus.Gauge, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, g, metrics.GaugeMetricType, ttl)
}

func (r *Registry) StoreHistogram(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.HistogramVec, o prometheus.Observer, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, o, metrics.HistogramMetricType, ttl)
}

func (r *Registry) StoreSummary(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vec *prometheus.SummaryVec, o prometheus.Observer, ttl time.Duration) {
	r.Store(metricName, hash, labels, vec, o, metrics.SummaryMetricType, ttl)
}

func (r *Registry) Store(metricName string, hash metrics.LabelHash, labels prometheus.Labels, vh metrics.VectorHolder, mh metrics.MetricHolder, metricType metrics.MetricType, ttl time.Duration) {
	metric, hasMetrics := r.Metrics[metricName]
	if !hasMetrics {
		metric.MetricType = metricType
		metric.Vectors = make(map[metrics.NameHash]*metrics.Vector)
		metric.Metrics = make(map[metrics.ValueHash]*metrics.RegisteredMetric)

		r.Metrics[metricName] = metric
	}

	v, ok := metric.Vectors[hash.Names]
	if !ok {
		v = &metrics.Vector{Holder: vh}
		metric.Vectors[hash.Names] = v
	}

	now := clock.Now()
	rm, ok := metric.Metrics[hash.Values]
	if !ok {
		rm = &metrics.RegisteredMetric{
			LastRegisteredAt: now,
			Labels:           labels,
			TTL:              ttl,
			Metric:           mh,
			VecKey:           hash.Names,
		}
		metric.Metrics[hash.Values] = rm
		v.RefCount++
		return
	}
	rm.LastRegisteredAt = now
	// Update ttl from mapping
	rm.TTL = ttl
}

func (r *Registry) Get(metricName string, hash metrics.LabelHash, metricType metrics.MetricType) (metrics.VectorHolder, metrics.MetricHolder) {
	metric, hasMetric := r.Metrics[metricName]

	if !hasMetric {
		return nil, nil
	}
	if metric.MetricType != metricType {
		return nil, nil
	}

	rm, ok := metric.Metrics[hash.Values]
	if ok {
		now := clock.Now()
		rm.LastRegisteredAt = now
		return metric.Vectors[hash.Names].Holder, rm.Metric
	}

	vector, ok := metric.Vectors[hash.Names]
	if ok {
		return vector.Holder, nil
	}

	return nil, nil
}

func (r *Registry) GetCounter(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Counter, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.CounterMetricType)
	if mh != nil {
		return mh.(prometheus.Counter), nil
	}

	if r.MetricConflicts(metricName, metrics.CounterMetricType) {
		return nil, fmt.Errorf("metric with name %s is already registered", metricName)
	}

	var counterVec *prometheus.CounterVec
	if vh == nil {
		metricsCount.WithLabelValues("counter").Inc()
		counterVec = prometheus.NewCounterVec(prometheus.CounterOpts{
			Name: metricName,
			Help: help,
		}, labelNames)

		if err := r.Registerer.Register(uncheckedCollector{counterVec}); err != nil {
			return nil, err
		}
	} else {
		counterVec = vh.(*prometheus.CounterVec)
	}

	var counter prometheus.Counter
	var err error
	if counter, err = counterVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreCounter(metricName, hash, labels, counterVec, counter, mapping.Ttl)

	return counter, nil
}

func (r *Registry) GetGauge(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Gauge, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.GaugeMetricType)
	if mh != nil {
		return mh.(prometheus.Gauge), nil
	}

	if r.MetricConflicts(metricName, metrics.GaugeMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var gaugeVec *prometheus.GaugeVec
	if vh == nil {
		metricsCount.WithLabelValues("gauge").Inc()
		gaugeVec = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: metricName,
			Help: help,
		}, labelNames)

		if err := r.Registerer.Register(uncheckedCollector{gaugeVec}); err != nil {
			return nil, err
		}
	} else {
		gaugeVec = vh.(*prometheus.GaugeVec)
	}

	var gauge prometheus.Gauge
	var err error
	if gauge, err = gaugeVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreGauge(metricName, hash, labels, gaugeVec, gauge, mapping.Ttl)

	return gauge, nil
}

func (r *Registry) GetHistogram(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Observer, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.HistogramMetricType)
	if mh != nil {
		return mh.(prometheus.Observer), nil
	}

	if r.MetricConflicts(metricName, metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_sum", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_count", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_bucket", metrics.HistogramMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var histogramVec *prometheus.HistogramVec
	if vh == nil {
		metricsCount.WithLabelValues("histogram").Inc()
		buckets := r.Mapper.Defaults.HistogramOptions.Buckets
		if mapping.HistogramOptions != nil && len(mapping.HistogramOptions.Buckets) > 0 {
			buckets = mapping.HistogramOptions.Buckets
		}
		histogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
			Name:    metricName,
			Help:    help,
			Buckets: buckets,
		}, labelNames)

		if err := prometheus.Register(uncheckedCollector{histogramVec}); err != nil {
			return nil, err
		}
	} else {
		histogramVec = vh.(*prometheus.HistogramVec)
	}

	var observer prometheus.Observer
	var err error
	if observer, err = histogramVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreHistogram(metricName, hash, labels, histogramVec, observer, mapping.Ttl)

	return observer, nil
}

func (r *Registry) GetSummary(metricName string, labels prometheus.Labels, help string, mapping *mapper.MetricMapping, metricsCount *prometheus.GaugeVec) (prometheus.Observer, error) {
	hash, labelNames := r.HashLabels(labels)
	vh, mh := r.Get(metricName, hash, metrics.SummaryMetricType)
	if mh != nil {
		return mh.(prometheus.Observer), nil
	}

	if r.MetricConflicts(metricName, metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_sum", metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}
	if r.MetricConflicts(metricName+"_count", metrics.SummaryMetricType) {
		return nil, fmt.Errorf("metrics.Metric with name %s is already registered", metricName)
	}

	var summaryVec *prometheus.SummaryVec
	if vh == nil {
		metricsCount.WithLabelValues("summary").Inc()
		quantiles := r.Mapper.Defaults.SummaryOptions.Quantiles
		if mapping != nil && mapping.SummaryOptions != nil && len(mapping.SummaryOptions.Quantiles) > 0 {
			quantiles = mapping.SummaryOptions.Quantiles
		}

		summaryOptions := mapper.SummaryOptions{
			MaxAge: r.Mapper.Defaults.SummaryOptions.MaxAge,
			AgeBuckets: r.Mapper.Defaults.SummaryOptions.AgeBuckets,
			BufCap: r.Mapper.Defaults.SummaryOptions.BufCap,
		}

		if mapping != nil && mapping.SummaryOptions != nil {
			summaryOptions = *mapping.SummaryOptions
		}

		objectives := make(map[float64]float64)
		for _, q := range quantiles {
			objectives[q.Quantile] = q.Error
		}
		// In the case of no mapping file, explicitly define the default quantiles
		if len(objectives) == 0 {
			objectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
		}
		summaryVec = prometheus.NewSummaryVec(prometheus.SummaryOpts{
			Name:       metricName,
			Help:       help,
			Objectives: objectives,
			MaxAge:     summaryOptions.MaxAge,
			AgeBuckets: summaryOptions.AgeBuckets,
			BufCap:     summaryOptions.BufCap,
		}, labelNames)

		if err := prometheus.Register(uncheckedCollector{summaryVec}); err != nil {
			return nil, err
		}
	} else {
		summaryVec = vh.(*prometheus.SummaryVec)
	}

	var observer prometheus.Observer
	var err error
	if observer, err = summaryVec.GetMetricWith(labels); err != nil {
		return nil, err
	}
	r.StoreSummary(metricName, hash, labels, summaryVec, observer, mapping.Ttl)

	return observer, nil
}

func (r *Registry) RemoveStaleMetrics() {
	now := clock.Now()
	// delete timeseries with expired ttl
	for _, metric := range r.Metrics {
		for hash, rm := range metric.Metrics {
			if rm.TTL == 0 {
				continue
			}
			if rm.LastRegisteredAt.Add(rm.TTL).Before(now) {
				metric.Vectors[rm.VecKey].Holder.Delete(rm.Labels)
				metric.Vectors[rm.VecKey].RefCount--
				delete(metric.Metrics, hash)
			}
		}
	}
}

// Calculates a hash of both the label names and the label names and values.
func (r *Registry) HashLabels(labels prometheus.Labels) (metrics.LabelHash, []string) {
	r.Hasher.Reset()
	r.NameBuf.Reset()
	r.ValueBuf.Reset()
	labelNames := make([]string, 0, len(labels))

	for labelName := range labels {
		labelNames = append(labelNames, labelName)
	}
	sort.Strings(labelNames)

	r.ValueBuf.WriteByte(model.SeparatorByte)
	for _, labelName := range labelNames {
		r.ValueBuf.WriteString(labels[labelName])
		r.ValueBuf.WriteByte(model.SeparatorByte)

		r.NameBuf.WriteString(labelName)
		r.NameBuf.WriteByte(model.SeparatorByte)
	}

	lh := metrics.LabelHash{}
	r.Hasher.Write(r.NameBuf.Bytes())
	lh.Names = metrics.NameHash(r.Hasher.Sum64())

	// Now add the values to the names we've already hashed.
	r.Hasher.Write(r.ValueBuf.Bytes())
	lh.Values = metrics.ValueHash(r.Hasher.Sum64())

	return lh, labelNames
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package main

import (
	"encoding/json"
	"launchpad.net/xmlpath"
	"log"
	"net/http"
	"os"
	"strconv"
)

const NextBusPublicXMLFeed = "http://webservices.nextbus.com/service/publicXMLFeed"

type RouteListRoute struct {
	Tag   string
	Title string
}

type RouteList struct {
	Copyright string
	Routes    []RouteListRoute
}

// Experiment with xmlpath. Not sure if I like this just yet.

var (
	routePath             = xmlpath.MustCompile("/body/route")
	tagPath               = xmlpath.MustCompile("@tag")
	titlePath             = xmlpath.MustCompile("@title")
	directionTagPath      = xmlpath.MustCompile("@tag")
	directionTitlePath    = xmlpath.MustCompile("@title")
	directionNamePath     = xmlpath.MustCompile("@name")
	directionUseForUIPath = xmlpath.MustCompile("@useForUI")
	directionBranchPath   = xmlpath.MustCompile("@branch")
	routeTagPath          = xmlpath.MustCompile("/body/route/@tag")
	routeTitlePath        = xmlpath.MustCompile("/body/route/@title")
	stopTagPath           = xmlpath.MustCompile("@tag")
	stopTitlePath         = xmlpath.MustCompile("@title")
	stopLatPath           = xmlpath.MustCompile("@lat")
	stopLonPath           = xmlpath.MustCompile("@lon")
	stopStopIdPath        = xmlpath.MustCompile("@stopId")
	routeStopPath         = xmlpath.MustCompile("/body/route/stop")
	routeDirectionPath    = xmlpath.MustCompile("/body/route/direction")
)

//
// <body copyright="All data copyright Toronto Transit Commission 2015.">
//   <route tag="501" title="501-Queen"/>
// </body>
//

func FetchRouteList(agency string) (RouteList, error) {
	resp, err := http.Get(NextBusPublicXMLFeed + "?command=routeList&a=" + agency)
	if err != nil {
		return RouteList{}, err
	}

	defer resp.Body.Close()

	root, err := xmlpath.Parse(resp.Body)
	if err != nil {
		return RouteList{}, err
	}

	var routeList RouteList

	iter := routePath.Iter(root)
	for iter.Next() {
		tag, _ := tagPath.String(iter.Node())
		title, _ := titlePath.String(iter.Node())
		routeList.Routes = append(routeList.Routes, RouteListRoute{Tag: tag, Title: title})
	}

	return routeList, nil
}

type RouteConfigStop struct {
	Tag    string
	Title  string
	Lat    float64
	Lon    float64
	StopId string
}

type RouteConfigDirection struct {
	Tag      string
	Title    string
	Name     string
	UseForUI bool
	Branch   string
	Stops    []RouteConfigStop
}

type RouteConfig struct {
	Tag           string
	Title         string
	Color         string
	OppositeColor string
	LatMin        float64
	LatMax        float64
	LonMin        float64
	LonMax        float64
	Directions    []RouteConfigDirection
}

func ParseRouteConfigDirection(node *xmlpath.Node, stopsByTag map[string]RouteConfigStop) (RouteConfigDirection, error) {
	tag, _ := directionTagPath.String(node)
	title, _ := directionTitlePath.String(node)
	name, _ := directionNamePath.String(node)
	useForUI, _ := directionUseForUIPath.String(node)
	branch, _ := directionBranchPath.String(node)

	direction := RouteConfigDirection{
		Tag:      tag,
		Title:    title,
		Name:     name,
		UseForUI: parseBool(useForUI),
		Branch:   branch,
	}

	directionStopPath := xmlpath.MustCompile("stop")
	directionStopTagPath := xmlpath.MustCompile("@tag")

	directionStopIter := directionStopPath.Iter(node)
	for directionStopIter.Next() {
		tag, _ := directionStopTagPath.String(directionStopIter.Node())
		if stop, ok := stopsByTag[tag]; ok {
			direction.Stops = append(direction.Stops, stop)
		}
	}

	return direction, nil
}

func FetchRouteConfig(agency, route string) (RouteConfig, error) {
	resp, err := http.Get(NextBusPublicXMLFeed + "?command=routeConfig&a=" + agency + "&r=" + route)
	if err != nil {
		return RouteConfig{}, err
	}

	defer resp.Body.Close()

	root, err := xmlpath.Parse(resp.Body)
	if err != nil {
		return RouteConfig{}, err
	}

	var routeConfig RouteConfig

	routeTag, _ := routeTagPath.String(root)
	routeConfig.Tag = routeTag

	routeTitle, _ := routeTitlePath.String(root)
	routeConfig.Title = routeTitle

	// Parse the stops

	var stopsByTag map[string]RouteConfigStop = map[string]RouteConfigStop{}

	iter := routeStopPath.Iter(root)
	for iter.Next() {
		tag, _ := stopTagPath.String(iter.Node())
		title, _ := stopTitlePath.String(iter.Node())
		lat, _ := stopLatPath.String(iter.Node())
		lon, _ := stopLonPath.String(iter.Node())
		stopId, _ := stopStopIdPath.String(iter.Node())

		stopsByTag[tag] = RouteConfigStop{
			Tag:    tag,
			Title:  title,
			Lat:    parseFloat(lat),
			Lon:    parseFloat(lon),
			StopId: stopId,
		}
	}

	// Parse the directions

	routeDirectionIter := routeDirectionPath.Iter(root)
	for routeDirectionIter.Next() {
		direction, err := parseRouteConfigDirection(routeDirectionIter.Node(), stopsByTag)
		if err != nil {
			return RouteConfig{}, err
		}
		routeConfig.Directions = append(routeConfig.Directions, direction)
	}

	return routeConfig, nil
}
<commit_msg>Updates<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package nextbus

import (
	"launchpad.net/xmlpath"
	"net/http"
)

const NextBusPublicXMLFeed = "http://webservices.nextbus.com/service/publicXMLFeed"

type RouteListRoute struct {
	Tag   string
	Title string
}

type RouteList struct {
	Copyright string
	Routes    []RouteListRoute
}

// Experiment with xmlpath. Not sure if I like this just yet.

var (
	routePath             = xmlpath.MustCompile("/body/route")
	tagPath               = xmlpath.MustCompile("@tag")
	titlePath             = xmlpath.MustCompile("@title")
	directionTagPath      = xmlpath.MustCompile("@tag")
	directionTitlePath    = xmlpath.MustCompile("@title")
	directionNamePath     = xmlpath.MustCompile("@name")
	directionUseForUIPath = xmlpath.MustCompile("@useForUI")
	directionBranchPath   = xmlpath.MustCompile("@branch")
	routeTagPath          = xmlpath.MustCompile("/body/route/@tag")
	routeTitlePath        = xmlpath.MustCompile("/body/route/@title")
	stopTagPath           = xmlpath.MustCompile("@tag")
	stopTitlePath         = xmlpath.MustCompile("@title")
	stopLatPath           = xmlpath.MustCompile("@lat")
	stopLonPath           = xmlpath.MustCompile("@lon")
	stopStopIdPath        = xmlpath.MustCompile("@stopId")
	routeStopPath         = xmlpath.MustCompile("/body/route/stop")
	routeDirectionPath    = xmlpath.MustCompile("/body/route/direction")
)

//
// <body copyright="All data copyright Toronto Transit Commission 2015.">
//   <route tag="501" title="501-Queen"/>
// </body>
//

func FetchRouteList(agency string) (RouteList, error) {
	resp, err := http.Get(NextBusPublicXMLFeed + "?command=routeList&a=" + agency)
	if err != nil {
		return RouteList{}, err
	}

	defer resp.Body.Close()

	root, err := xmlpath.Parse(resp.Body)
	if err != nil {
		return RouteList{}, err
	}

	var routeList RouteList

	iter := routePath.Iter(root)
	for iter.Next() {
		tag, _ := tagPath.String(iter.Node())
		title, _ := titlePath.String(iter.Node())
		routeList.Routes = append(routeList.Routes, RouteListRoute{Tag: tag, Title: title})
	}

	return routeList, nil
}

type RouteConfigStop struct {
	Tag    string
	Title  string
	Lat    float64
	Lon    float64
	StopId string
}

type RouteConfigDirection struct {
	Tag      string
	Title    string
	Name     string
	UseForUI bool
	Branch   string
	Stops    []RouteConfigStop
}

type RouteConfig struct {
	Tag           string
	Title         string
	Color         string
	OppositeColor string
	LatMin        float64
	LatMax        float64
	LonMin        float64
	LonMax        float64
	Directions    []RouteConfigDirection
}

func parseRouteConfigDirection(node *xmlpath.Node, stopsByTag map[string]RouteConfigStop) (RouteConfigDirection, error) {
	tag, _ := directionTagPath.String(node)
	title, _ := directionTitlePath.String(node)
	name, _ := directionNamePath.String(node)
	useForUI, _ := directionUseForUIPath.String(node)
	branch, _ := directionBranchPath.String(node)

	direction := RouteConfigDirection{
		Tag:      tag,
		Title:    title,
		Name:     name,
		UseForUI: parseBool(useForUI),
		Branch:   branch,
	}

	directionStopPath := xmlpath.MustCompile("stop")
	directionStopTagPath := xmlpath.MustCompile("@tag")

	directionStopIter := directionStopPath.Iter(node)
	for directionStopIter.Next() {
		tag, _ := directionStopTagPath.String(directionStopIter.Node())
		if stop, ok := stopsByTag[tag]; ok {
			direction.Stops = append(direction.Stops, stop)
		}
	}

	return direction, nil
}

func FetchRouteConfig(agency, route string) (RouteConfig, error) {
	resp, err := http.Get(NextBusPublicXMLFeed + "?command=routeConfig&a=" + agency + "&r=" + route)
	if err != nil {
		return RouteConfig{}, err
	}

	defer resp.Body.Close()

	root, err := xmlpath.Parse(resp.Body)
	if err != nil {
		return RouteConfig{}, err
	}

	var routeConfig RouteConfig

	routeTag, _ := routeTagPath.String(root)
	routeConfig.Tag = routeTag

	routeTitle, _ := routeTitlePath.String(root)
	routeConfig.Title = routeTitle

	// Parse the stops

	var stopsByTag map[string]RouteConfigStop = map[string]RouteConfigStop{}

	iter := routeStopPath.Iter(root)
	for iter.Next() {
		tag, _ := stopTagPath.String(iter.Node())
		title, _ := stopTitlePath.String(iter.Node())
		lat, _ := stopLatPath.String(iter.Node())
		lon, _ := stopLonPath.String(iter.Node())
		stopId, _ := stopStopIdPath.String(iter.Node())

		stopsByTag[tag] = RouteConfigStop{
			Tag:    tag,
			Title:  title,
			Lat:    parseFloat(lat),
			Lon:    parseFloat(lon),
			StopId: stopId,
		}
	}

	// Parse the directions

	routeDirectionIter := routeDirectionPath.Iter(root)
	for routeDirectionIter.Next() {
		direction, err := parseRouteConfigDirection(routeDirectionIter.Node(), stopsByTag)
		if err != nil {
			return RouteConfig{}, err
		}
		routeConfig.Directions = append(routeConfig.Directions, direction)
	}

	return routeConfig, nil
}
<|endoftext|>