<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"runtime/debug"

	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	jsonw "github.com/keybase/go-jsonw"
	"golang.org/x/net/context"
)

type PostProofRes struct {
	Text     string
	ID       string
	Metadata *jsonw.Wrapper
}

type PostProofArg struct {
	Sig            string
	ID             keybase1.SigID
	RemoteUsername string
	ProofType      string
	Supersede      bool
	RemoteKey      string
	SigningKey     GenericKey
}

func PostProof(ctx context.Context, g *GlobalContext, arg PostProofArg) (*PostProofRes, error) {
	hargs := HTTPArgs{
		"sig_id_base":     S{arg.ID.ToString(false)},
		"sig_id_short":    S{arg.ID.ToShortID()},
		"sig":             S{arg.Sig},
		"is_remote_proof": B{true},
		"supersede":       B{arg.Supersede},
		"signing_kid":     S{arg.SigningKey.GetKID().String()},
		"type":            S{arg.ProofType},
	}
	hargs.Add(arg.RemoteKey, S{arg.RemoteUsername})

	res, err := g.API.Post(APIArg{
		Endpoint:    "sig/post",
		SessionType: APISessionTypeREQUIRED,
		Args:        hargs,
		NetContext:  ctx,
	})

	if err != nil {
		return nil, err
	}
	var tmp PostProofRes
	res.Body.AtKey("proof_text").GetStringVoid(&tmp.Text, &err)
	res.Body.AtKey("proof_id").GetStringVoid(&tmp.ID, &err)
	tmp.Metadata = res.Body.AtKey("proof_metadata")

	var ret *PostProofRes
	if err == nil {
		ret = &tmp
	}
	return ret, err
}

type PostAuthProofArg struct {
	uid keybase1.UID
	sig string
	key GenericKey
}

type PostAuthProofRes struct {
	SessionID string `json:"session"`
	AuthID    string `json:"auth_id"`
	CSRFToken string `json:"csrf_token"`
	UIDHex    string `json:"uid"`
	Username  string `json:"username"`
	PPGen     int    `json:"passphrase_generation"`
}

func PostAuthProof(ctx context.Context, g *GlobalContext, arg PostAuthProofArg) (*PostAuthProofRes, error) {
	hargs := HTTPArgs{
		"uid":         UIDArg(arg.uid),
		"sig":         S{arg.sig},
		"signing_kid": S{arg.key.GetKID().String()},
	}
	res, err := g.API.Post(APIArg{
		Endpoint:    "sig/post_auth",
		SessionType: APISessionTypeNONE,
		Args:        hargs,
		NetContext:  ctx,
	})
	if err != nil {
		return nil, err
	}
	var ret *PostAuthProofRes
	var tmp PostAuthProofRes
	if err = res.Body.UnmarshalAgain(&tmp); err == nil {
		ret = &tmp
	}
	return ret, err
}

type InviteRequestArg struct {
	Email    string
	Fullname string
	Notes    string
}

func PostInviteRequest(ctx context.Context, g *GlobalContext, arg InviteRequestArg) (err error) {
	_, err = g.API.Post(APIArg{
		Endpoint: "invitation_request",
		Args: HTTPArgs{
			"email":     S{arg.Email},
			"full_name": S{arg.Fullname},
			"notes":     S{arg.Notes},
		},
		NetContext: ctx,
	})
	return err
}

func DeletePrimary(ctx context.Context, g *GlobalContext) (err error) {
	_, err = g.API.Post(APIArg{
		Endpoint:    "key/revoke",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"revoke_primary":  I{1},
			"revocation_type": I{RevSimpleDelete},
		},
		NetContext: ctx,
	})
	return
}

func CheckPosted(ctx context.Context, g *GlobalContext, proofID string) (found bool, status keybase1.ProofStatus, state keybase1.ProofState, err error) {
	res, e2 := g.API.Post(APIArg{
		Endpoint:    "sig/posted",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"proof_id": S{proofID},
		},
		NetContext: ctx,
	})
	if e2 != nil {
		err = e2
		return
	}
	var (
		rfound  bool
		rstatus int
		rstate  int
		rerr    error
	)
	res.Body.AtKey("proof_ok").GetBoolVoid(&rfound, &rerr)
	res.Body.AtPath("proof_res.status").GetIntVoid(&rstatus, &rerr)
	res.Body.AtPath("proof_res.state").GetIntVoid(&rstate, &rerr)
	return rfound, keybase1.ProofStatus(rstatus), keybase1.ProofState(rstate), rerr
}

func CheckPostedViaSigID(ctx context.Context, g *GlobalContext, sigID keybase1.SigID) (found bool, status keybase1.ProofStatus, state keybase1.ProofState, err error) {
	res, e2 := g.API.Post(APIArg{
		Endpoint:    "sig/posted",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"sig_id": S{sigID.ToString(true)},
		},
		NetContext: ctx,
	})
	if e2 != nil {
		err = e2
		return
	}

	var (
		rfound  bool
		rstatus int
		rstate  int
		rerr    error
	)
	res.Body.AtKey("proof_ok").GetBoolVoid(&rfound, &rerr)
	res.Body.AtPath("proof_res.status").GetIntVoid(&rstatus, &rerr)
	res.Body.AtPath("proof_res.state").GetIntVoid(&rstate, &rerr)
	return rfound, keybase1.ProofStatus(rstatus), keybase1.ProofState(rstate), rerr
}

func PostDeviceLKS(ctx context.Context, g *GlobalContext, sr SessionReader, deviceID keybase1.DeviceID, deviceType string, serverHalf LKSecServerHalf,
	ppGen PassphraseGeneration,
	clientHalfRecovery string, clientHalfRecoveryKID keybase1.KID) error {
	g.Log.Debug("| PostDeviceLKS: %s", deviceID)
	if serverHalf.IsNil() {
		return fmt.Errorf("PostDeviceLKS: called with empty serverHalf")
	}
	if ppGen < 1 {
		g.Log.Warning("PostDeviceLKS: ppGen < 1 (%d)", ppGen)
		debug.PrintStack()
	}
	arg := APIArg{
		Endpoint:    "device/update",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"device_id":       S{Val: deviceID.String()},
			"type":            S{Val: deviceType},
			"lks_server_half": S{Val: serverHalf.EncodeToHex()},
			"ppgen":           I{Val: int(ppGen)},
			"lks_client_half": S{Val: clientHalfRecovery},
			"kid":             S{Val: clientHalfRecoveryKID.String()},
			"platform":        S{Val: GetPlatformString()},
		},
		SessionR:   sr,
		NetContext: ctx,
	}
	_, err := g.API.Post(arg)
	return err
}

func CheckInvitationCode(ctx context.Context, g *GlobalContext, code string) error {
	arg := APIArg{
		Endpoint:    "invitation/check",
		SessionType: APISessionTypeNONE,
		Args: HTTPArgs{
			"invitation_id": S{Val: code},
		},
		NetContext: ctx,
	}
	_, err := g.API.Get(arg)
	return err
}

func GetInvitationCode(net context.Context, g *GlobalContext) (string, error) {
	arg := APIArg{
		Endpoint:    "invitation_bypass_request",
		SessionType: APISessionTypeNONE,
		NetContext:  net,
	}
	res, err := g.API.Get(arg)
	var invitationID string
	if err == nil {
		invitationID, err = res.Body.AtKey("invitation_id").GetString()
	}
	return invitationID, err
}
<commit_msg>simple retry for now (#8810)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"runtime/debug"

	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	jsonw "github.com/keybase/go-jsonw"
	"golang.org/x/net/context"
)

type PostProofRes struct {
	Text     string
	ID       string
	Metadata *jsonw.Wrapper
}

type PostProofArg struct {
	Sig            string
	ID             keybase1.SigID
	RemoteUsername string
	ProofType      string
	Supersede      bool
	RemoteKey      string
	SigningKey     GenericKey
}

func PostProof(ctx context.Context, g *GlobalContext, arg PostProofArg) (*PostProofRes, error) {
	hargs := HTTPArgs{
		"sig_id_base":     S{arg.ID.ToString(false)},
		"sig_id_short":    S{arg.ID.ToShortID()},
		"sig":             S{arg.Sig},
		"is_remote_proof": B{true},
		"supersede":       B{arg.Supersede},
		"signing_kid":     S{arg.SigningKey.GetKID().String()},
		"type":            S{arg.ProofType},
	}
	hargs.Add(arg.RemoteKey, S{arg.RemoteUsername})

	res, err := g.API.Post(APIArg{
		Endpoint:    "sig/post",
		SessionType: APISessionTypeREQUIRED,
		Args:        hargs,
		NetContext:  ctx,
	})

	if err != nil {
		return nil, err
	}
	var tmp PostProofRes
	res.Body.AtKey("proof_text").GetStringVoid(&tmp.Text, &err)
	res.Body.AtKey("proof_id").GetStringVoid(&tmp.ID, &err)
	tmp.Metadata = res.Body.AtKey("proof_metadata")

	var ret *PostProofRes
	if err == nil {
		ret = &tmp
	}
	return ret, err
}

type PostAuthProofArg struct {
	uid keybase1.UID
	sig string
	key GenericKey
}

type PostAuthProofRes struct {
	SessionID string `json:"session"`
	AuthID    string `json:"auth_id"`
	CSRFToken string `json:"csrf_token"`
	UIDHex    string `json:"uid"`
	Username  string `json:"username"`
	PPGen     int    `json:"passphrase_generation"`
}

func PostAuthProof(ctx context.Context, g *GlobalContext, arg PostAuthProofArg) (*PostAuthProofRes, error) {
	hargs := HTTPArgs{
		"uid":         UIDArg(arg.uid),
		"sig":         S{arg.sig},
		"signing_kid": S{arg.key.GetKID().String()},
	}
	res, err := g.API.Post(APIArg{
		Endpoint:    "sig/post_auth",
		SessionType: APISessionTypeNONE,
		Args:        hargs,
		NetContext:  ctx,
	})
	if err != nil {
		return nil, err
	}
	var ret *PostAuthProofRes
	var tmp PostAuthProofRes
	if err = res.Body.UnmarshalAgain(&tmp); err == nil {
		ret = &tmp
	}
	return ret, err
}

type InviteRequestArg struct {
	Email    string
	Fullname string
	Notes    string
}

func PostInviteRequest(ctx context.Context, g *GlobalContext, arg InviteRequestArg) (err error) {
	_, err = g.API.Post(APIArg{
		Endpoint: "invitation_request",
		Args: HTTPArgs{
			"email":     S{arg.Email},
			"full_name": S{arg.Fullname},
			"notes":     S{arg.Notes},
		},
		NetContext: ctx,
	})
	return err
}

func DeletePrimary(ctx context.Context, g *GlobalContext) (err error) {
	_, err = g.API.Post(APIArg{
		Endpoint:    "key/revoke",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"revoke_primary":  I{1},
			"revocation_type": I{RevSimpleDelete},
		},
		NetContext: ctx,
	})
	return
}

func CheckPosted(ctx context.Context, g *GlobalContext, proofID string) (found bool, status keybase1.ProofStatus, state keybase1.ProofState, err error) {
	res, e2 := g.API.Post(APIArg{
		Endpoint:    "sig/posted",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"proof_id": S{proofID},
		},
		NetContext: ctx,
	})
	if e2 != nil {
		err = e2
		return
	}
	var (
		rfound  bool
		rstatus int
		rstate  int
		rerr    error
	)
	res.Body.AtKey("proof_ok").GetBoolVoid(&rfound, &rerr)
	res.Body.AtPath("proof_res.status").GetIntVoid(&rstatus, &rerr)
	res.Body.AtPath("proof_res.state").GetIntVoid(&rstate, &rerr)
	return rfound, keybase1.ProofStatus(rstatus), keybase1.ProofState(rstate), rerr
}

func CheckPostedViaSigID(ctx context.Context, g *GlobalContext, sigID keybase1.SigID) (found bool, status keybase1.ProofStatus, state keybase1.ProofState, err error) {
	res, e2 := g.API.Post(APIArg{
		Endpoint:    "sig/posted",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"sig_id": S{sigID.ToString(true)},
		},
		NetContext: ctx,
	})
	if e2 != nil {
		err = e2
		return
	}

	var (
		rfound  bool
		rstatus int
		rstate  int
		rerr    error
	)
	res.Body.AtKey("proof_ok").GetBoolVoid(&rfound, &rerr)
	res.Body.AtPath("proof_res.status").GetIntVoid(&rstatus, &rerr)
	res.Body.AtPath("proof_res.state").GetIntVoid(&rstate, &rerr)
	return rfound, keybase1.ProofStatus(rstatus), keybase1.ProofState(rstate), rerr
}

func PostDeviceLKS(ctx context.Context, g *GlobalContext, sr SessionReader, deviceID keybase1.DeviceID, deviceType string, serverHalf LKSecServerHalf,
	ppGen PassphraseGeneration,
	clientHalfRecovery string, clientHalfRecoveryKID keybase1.KID) error {
	g.Log.Debug("| PostDeviceLKS: %s", deviceID)
	if serverHalf.IsNil() {
		return fmt.Errorf("PostDeviceLKS: called with empty serverHalf")
	}
	if ppGen < 1 {
		g.Log.Warning("PostDeviceLKS: ppGen < 1 (%d)", ppGen)
		debug.PrintStack()
	}
	arg := APIArg{
		Endpoint:    "device/update",
		SessionType: APISessionTypeREQUIRED,
		Args: HTTPArgs{
			"device_id":       S{Val: deviceID.String()},
			"type":            S{Val: deviceType},
			"lks_server_half": S{Val: serverHalf.EncodeToHex()},
			"ppgen":           I{Val: int(ppGen)},
			"lks_client_half": S{Val: clientHalfRecovery},
			"kid":             S{Val: clientHalfRecoveryKID.String()},
			"platform":        S{Val: GetPlatformString()},
		},
		RetryCount: 10,
		SessionR:   sr,
		NetContext: ctx,
	}
	_, err := g.API.Post(arg)
	if err != nil {
		g.Log.Info("device/update(%+v) failed: %s", arg.Args, err)
	}
	return err
}

func CheckInvitationCode(ctx context.Context, g *GlobalContext, code string) error {
	arg := APIArg{
		Endpoint:    "invitation/check",
		SessionType: APISessionTypeNONE,
		Args: HTTPArgs{
			"invitation_id": S{Val: code},
		},
		NetContext: ctx,
	}
	_, err := g.API.Get(arg)
	return err
}

func GetInvitationCode(net context.Context, g *GlobalContext) (string, error) {
	arg := APIArg{
		Endpoint:    "invitation_bypass_request",
		SessionType: APISessionTypeNONE,
		NetContext:  net,
	}
	res, err := g.API.Get(arg)
	var invitationID string
	if err == nil {
		invitationID, err = res.Body.AtKey("invitation_id").GetString()
	}
	return invitationID, err
}
<|endoftext|>


<commit_before>// Package google provides you access to Google's OAuth2
// infrastructure. The implementation is based on this blog post:
// http://skarlso.github.io/2016/06/12/google-signin-with-go/
package google

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang/glog"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// Credentials stores google client-ids.
type Credentials struct {
	ClientID     string `json:"clientid"`
	ClientSecret string `json:"secret"`
}

// User is a retrieved and authentiacted user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Gender        string `json:"gender"`
	Hd            string `json:"hd"`
}

var cred Credentials
var conf *oauth2.Config
var state string
var store sessions.CookieStore

func randToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	return base64.StdEncoding.EncodeToString(b)
}

// Setup the authorization path
func Setup(redirectURL, credFile string, scopes []string, secret []byte) {
	store = sessions.NewCookieStore(secret)
	var c Credentials
	file, err := ioutil.ReadFile(credFile)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	json.Unmarshal(file, &c)

	conf = &oauth2.Config{
		ClientID:     c.ClientID,
		ClientSecret: c.ClientSecret,
		RedirectURL:  redirectURL,
		Scopes:       scopes,
		Endpoint:     google.Endpoint,
	}
}

func Session(name string) gin.HandlerFunc {
	return sessions.Sessions(name, store)
}

func LoginHandler(ctx *gin.Context) {
	state = randToken()
	session := sessions.Default(ctx)
	session.Set("state", state)
	session.Save()
	ctx.Writer.Write([]byte("<title>Golang Google</title>  <button>Login with Google!</button>  "))
}

func GetLoginURL(state string) string {
	return conf.AuthCodeURL(state)
}

// Auth is the google authorziation middleware. You can use them to protect a routergroup.
// Example:
//
//        private.Use(google.Auth())
//        private.GET("/", UserInfoHandler)
//        private.GET("/api", func(ctx *gin.Context) {
//            ctx.JSON(200, gin.H{"message": "Hello from private for groups"})
//        })
//    func UserInfoHandler(ctx *gin.Context) {
//        ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": ctx.MustGet("user").(google.User)})
//    }
func Auth() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// Handle the exchange code to initiate a transport.
		session := sessions.Default(ctx)
		retrievedState := session.Get("state")
		if retrievedState != ctx.Query("state") {
			ctx.AbortWithError(http.StatusUnauthorized, fmt.Errorf("Invalid session state: %s", retrievedState))
			return
		}

		tok, err := conf.Exchange(oauth2.NoContext, ctx.Query("code"))
		if err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}

		client := conf.Client(oauth2.NoContext, tok)
		email, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}
		defer email.Body.Close()
		data, err := ioutil.ReadAll(email.Body)
		if err != nil {
			glog.Errorf("Could not read Body: %s", err)
			ctx.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		var user User
		err = json.Unmarshal(data, &user)
		if err != nil {
			glog.Errorf("Unmarshal userinfo failed: %s", err)
			ctx.AbortWithError(http.StatusInternalServerError, err)
			return
		}
		// save userinfo, which could be used in Handlers
		ctx.Set("user", user)
	}
}
<commit_msg>address issues that x0rg found<commit_after>// Package google provides you access to Google's OAuth2
// infrastructure. The implementation is based on this blog post:
// http://skarlso.github.io/2016/06/12/google-signin-with-go/
package google

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang/glog"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// Credentials stores google client-ids.
type Credentials struct {
	ClientID     string `json:"clientid"`
	ClientSecret string `json:"secret"`
}

// User is a retrieved and authenticated user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Gender        string `json:"gender"`
	Hd            string `json:"hd"`
}

var cred Credentials
var conf *oauth2.Config
var state string
var store sessions.CookieStore

func randToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	return base64.StdEncoding.EncodeToString(b)
}

// Setup the authorization path
func Setup(redirectURL, credFile string, scopes []string, secret []byte) {
	store = sessions.NewCookieStore(secret)
	var c Credentials
	file, err := ioutil.ReadFile(credFile)
	if err != nil {
		glog.Fatalf("File error: %v\n", err)
	}
	json.Unmarshal(file, &c)

	conf = &oauth2.Config{
		ClientID:     c.ClientID,
		ClientSecret: c.ClientSecret,
		RedirectURL:  redirectURL,
		Scopes:       scopes,
		Endpoint:     google.Endpoint,
	}
}

func Session(name string) gin.HandlerFunc {
	return sessions.Sessions(name, store)
}

func LoginHandler(ctx *gin.Context) {
	state = randToken()
	session := sessions.Default(ctx)
	session.Set("state", state)
	session.Save()
	ctx.Writer.Write([]byte("<title>Golang Google</title>  <button>Login with Google!</button>  "))
}

func GetLoginURL(state string) string {
	return conf.AuthCodeURL(state)
}

// Auth is the google authorization middleware. You can use them to protect a routergroup.
// Example:
//
//        private.Use(google.Auth())
//        private.GET("/", UserInfoHandler)
//        private.GET("/api", func(ctx *gin.Context) {
//            ctx.JSON(200, gin.H{"message": "Hello from private for groups"})
//        })
//    func UserInfoHandler(ctx *gin.Context) {
//        ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": ctx.MustGet("user").(google.User)})
//    }
func Auth() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// Handle the exchange code to initiate a transport.
		session := sessions.Default(ctx)
		retrievedState := session.Get("state")
		if retrievedState != ctx.Query("state") {
			ctx.AbortWithError(http.StatusUnauthorized, fmt.Errorf("Invalid session state: %s", retrievedState))
			return
		}

		tok, err := conf.Exchange(oauth2.NoContext, ctx.Query("code"))
		if err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}

		client := conf.Client(oauth2.NoContext, tok)
		email, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}
		defer email.Body.Close()
		data, err := ioutil.ReadAll(email.Body)
		if err != nil {
			glog.Errorf("Could not read Body: %s", err)
			ctx.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		var user User
		err = json.Unmarshal(data, &user)
		if err != nil {
			glog.Errorf("Unmarshal userinfo failed: %s", err)
			ctx.AbortWithError(http.StatusInternalServerError, err)
			return
		}
		// save userinfo, which could be used in Handlers
		ctx.Set("user", user)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"math/rand"
	"net"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// server maintains an array of connected peers to which it gossips
// newly arrived information on a periodic basis.
type server struct {
	stopper *stop.Stopper

	mu       sync.Mutex                             // Protects the fields below
	is       *infoStore                             // The backing infostore
	incoming nodeSet                                // Incoming client node IDs
	nodeMap  map[util.UnresolvedAddr]roachpb.NodeID // Incoming client's local address -> node ID
	tighten  chan roachpb.NodeID                    // Channel of too-distant node IDs
	sent     int                                    // Count of infos sent from this server to clients
	received int                                    // Count of infos received from clients
	ready    chan struct{}                          // Broadcasts wakeup to waiting gossip requests

	simulationCycler *sync.Cond // Used when simulating the network to signal next cycle
}

// newServer creates and returns a server struct.
func newServer(stopper *stop.Stopper) *server {
	return &server{
		stopper:  stopper,
		is:       newInfoStore(0, util.UnresolvedAddr{}, stopper),
		incoming: makeNodeSet(minPeers),
		nodeMap:  make(map[util.UnresolvedAddr]roachpb.NodeID),
		tighten:  make(chan roachpb.NodeID, 1),
		ready:    make(chan struct{}),
	}
}

// Gossip receives gossiped information from a peer node.
// The received delta is combined with the infostore, and this
// node's own gossip is returned to requesting client.
func (s *server) Gossip(stream Gossip_GossipServer) error {
	args, err := stream.Recv()
	if err != nil {
		return err
	}

	ctx, cancel := context.WithCancel(stream.Context())
	defer cancel()
	syncChan := make(chan struct{}, 1)
	send := func(reply *Response) error {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case syncChan <- struct{}{}:
			defer func() { <-syncChan }()
			return stream.Send(reply)
		}
	}

	defer func() { syncChan <- struct{}{} }()

	// Verify that there aren't multiple incoming connections from the same
	// node. This can happen when bootstrap connections are initiated through
	// a load balancer.
	s.mu.Lock()
	_, ok := s.nodeMap[args.Addr]
	s.mu.Unlock()
	if ok {
		return util.Errorf("duplicate connection from node at %s", args.Addr)
	}

	errCh := make(chan error, 1)

	// Starting workers in a task prevents data races during shutdown.
	s.stopper.RunTask(func() {
		s.stopper.RunWorker(func() {
			errCh <- s.gossipReceiver(&args, send, stream.Recv)
		})
	})

	reply := new(Response)

	for {
		s.mu.Lock()

		delta := s.is.delta(args.HighWaterStamps)

		if infoCount := len(delta); infoCount > 0 {
			if log.V(1) {
				log.Infof("node %d returned %d info(s) to node %d", s.is.NodeID, infoCount, args.NodeID)
			}

			*reply = Response{
				NodeID:          s.is.NodeID,
				HighWaterStamps: s.is.getHighWaterStamps(),
				Delta:           delta,
			}

			s.mu.Unlock()
			if err := send(reply); err != nil {
				return err
			}
			s.mu.Lock()
			s.sent += infoCount
		}

		ready := s.ready
		s.mu.Unlock()

		select {
		case <-s.stopper.ShouldDrain():
			return nil
		case err := <-errCh:
			return err
		case <-ready:
		}
	}
}

func (s *server) gossipReceiver(argsPtr **Request, senderFn func(*Response) error, receiverFn func() (*Request, error)) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	reply := new(Response)

	// This loop receives gossip from the client. It does not attempt to send the
	// server's gossip to the client.
	for {
		args := *argsPtr
		if args.NodeID != 0 {
			// Decide whether or not we can accept the incoming connection
			// as a permanent peer.
			if s.incoming.hasNode(args.NodeID) {
				// Do nothing.
			} else if s.incoming.hasSpace() {
				s.incoming.addNode(args.NodeID)
				s.nodeMap[args.Addr] = args.NodeID

				defer func(nodeID roachpb.NodeID, addr util.UnresolvedAddr) {
					s.incoming.removeNode(nodeID)
					delete(s.nodeMap, addr)
				}(args.NodeID, args.Addr)
			} else {
				var alternateAddr util.UnresolvedAddr
				var alternateNodeID roachpb.NodeID
				// Choose a random peer for forwarding.
				altIdx := rand.Intn(len(s.nodeMap))
				for addr, id := range s.nodeMap {
					if altIdx == 0 {
						alternateAddr = addr
						alternateNodeID = id
						break
					}
					altIdx--
				}

				log.Infof("refusing gossip from node %d (max %d conns); forwarding to %d (%s)",
					args.NodeID, s.incoming.maxSize, alternateNodeID, alternateAddr)

				*reply = Response{
					NodeID:          s.is.NodeID,
					AlternateAddr:   &alternateAddr,
					AlternateNodeID: alternateNodeID,
				}

				s.mu.Unlock()
				err := senderFn(reply)
				s.mu.Lock()
				return err
			}
		}

		s.received += len(args.Delta)
		freshCount, err := s.is.combine(args.Delta, args.NodeID)
		if err != nil {
			log.Warningf("node %d failed to fully combine gossip delta from node %d: %s", s.is.NodeID, args.NodeID, err)
		}
		if log.V(1) {
			log.Infof("node %d received %s from node %d (%d fresh)", s.is.NodeID, extractKeys(args.Delta), args.NodeID, freshCount)
		}
		s.maybeTighten()

		*reply = Response{
			NodeID:          s.is.NodeID,
			HighWaterStamps: s.is.getHighWaterStamps(),
		}

		s.mu.Unlock()
		err = senderFn(reply)
		s.mu.Lock()
		if err != nil {
			return err
		}

		if cycler := s.simulationCycler; cycler != nil {
			cycler.Wait()
		}

		s.mu.Unlock()
		recvArgs, err := receiverFn()
		s.mu.Lock()
		if err != nil {
			return err
		}

		// *argsPtr holds the remote peer state; we need to update it whenever we
		// receive a new non-nil request. We avoid assigning to *argsPtr directly
		// because the gossip sender above has closed over *argsPtr and will NPE if
		// *argsPtr were set to nil.
		*argsPtr = recvArgs
	}
}

// InfosSent returns the total count of infos sent to clients.
func (s *server) InfosSent() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.sent
}

// InfosReceived returns the total count of infos received from clients.
func (s *server) InfosReceived() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.received
}

// maybeTighten examines the infostore for the most distant node and
// if more distant than MaxHops, sends on the tightenNetwork channel
// to start a new client connection.
func (s *server) maybeTighten() {
	distantNodeID, distantHops := s.is.mostDistant()
	if log.V(1) {
		log.Infof("@%d: distantHops: %d from %d", s.is.NodeID, distantHops, distantNodeID)
	}
	if distantHops > MaxHops {
		select {
		case s.tighten <- distantNodeID:
			if log.V(1) {
				log.Infof("if possible, tightening network to node %d (%d > %d)", distantNodeID, distantHops, MaxHops)
			}
		default:
			// Do nothing.
		}
	}
}

// start initializes the infostore with the rpc server address and
// then begins processing connecting clients in an infinite select
// loop via goroutine. Periodically, clients connected and awaiting
// the next round of gossip are awoken via the conditional variable.
func (s *server) start(grpcServer *grpc.Server, addr net.Addr) {
	s.mu.Lock()
	s.is.NodeAddr = util.MakeUnresolvedAddr(addr.Network(), addr.String())
	s.mu.Unlock()
	RegisterGossipServer(grpcServer, s)

	broadcast := func() {
		ready := make(chan struct{})

		s.mu.Lock()
		close(s.ready)
		s.ready = ready
		s.mu.Unlock()
	}
	unregister := s.is.registerCallback(".*", func(_ string, _ roachpb.Value) {
		broadcast()
	})

	s.stopper.RunWorker(func() {
		<-s.stopper.ShouldDrain()

		s.mu.Lock()
		unregister()
		s.mu.Unlock()

		broadcast()
	})
}
<commit_msg>gossip: do not recommend self as node to forward to<commit_after>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"math/rand"
	"net"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// server maintains an array of connected peers to which it gossips
// newly arrived information on a periodic basis.
type server struct {
	stopper *stop.Stopper

	mu       sync.Mutex                             // Protects the fields below
	is       *infoStore                             // The backing infostore
	incoming nodeSet                                // Incoming client node IDs
	nodeMap  map[util.UnresolvedAddr]roachpb.NodeID // Incoming client's local address -> node ID
	tighten  chan roachpb.NodeID                    // Channel of too-distant node IDs
	sent     int                                    // Count of infos sent from this server to clients
	received int                                    // Count of infos received from clients
	ready    chan struct{}                          // Broadcasts wakeup to waiting gossip requests

	simulationCycler *sync.Cond // Used when simulating the network to signal next cycle
}

// newServer creates and returns a server struct.
func newServer(stopper *stop.Stopper) *server {
	return &server{
		stopper:  stopper,
		is:       newInfoStore(0, util.UnresolvedAddr{}, stopper),
		incoming: makeNodeSet(minPeers),
		nodeMap:  make(map[util.UnresolvedAddr]roachpb.NodeID),
		tighten:  make(chan roachpb.NodeID, 1),
		ready:    make(chan struct{}),
	}
}

// Gossip receives gossiped information from a peer node.
// The received delta is combined with the infostore, and this
// node's own gossip is returned to requesting client.
func (s *server) Gossip(stream Gossip_GossipServer) error {
	args, err := stream.Recv()
	if err != nil {
		return err
	}

	ctx, cancel := context.WithCancel(stream.Context())
	defer cancel()
	syncChan := make(chan struct{}, 1)
	send := func(reply *Response) error {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case syncChan <- struct{}{}:
			defer func() { <-syncChan }()
			return stream.Send(reply)
		}
	}

	defer func() { syncChan <- struct{}{} }()

	// Verify that there aren't multiple incoming connections from the same
	// node. This can happen when bootstrap connections are initiated through
	// a load balancer.
	s.mu.Lock()
	_, ok := s.nodeMap[args.Addr]
	s.mu.Unlock()
	if ok {
		return util.Errorf("duplicate connection from node at %s", args.Addr)
	}

	errCh := make(chan error, 1)

	// Starting workers in a task prevents data races during shutdown.
	s.stopper.RunTask(func() {
		s.stopper.RunWorker(func() {
			errCh <- s.gossipReceiver(&args, send, stream.Recv)
		})
	})

	reply := new(Response)

	for {
		s.mu.Lock()

		delta := s.is.delta(args.HighWaterStamps)

		if infoCount := len(delta); infoCount > 0 {
			if log.V(1) {
				log.Infof("node %d returned %d info(s) to node %d", s.is.NodeID, infoCount, args.NodeID)
			}

			*reply = Response{
				NodeID:          s.is.NodeID,
				HighWaterStamps: s.is.getHighWaterStamps(),
				Delta:           delta,
			}

			s.mu.Unlock()
			if err := send(reply); err != nil {
				return err
			}
			s.mu.Lock()
			s.sent += infoCount
		}

		ready := s.ready
		s.mu.Unlock()

		select {
		case <-s.stopper.ShouldDrain():
			return nil
		case err := <-errCh:
			return err
		case <-ready:
		}
	}
}

func (s *server) gossipReceiver(argsPtr **Request, senderFn func(*Response) error, receiverFn func() (*Request, error)) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	reply := new(Response)

	// This loop receives gossip from the client. It does not attempt to send the
	// server's gossip to the client.
	for {
		args := *argsPtr
		if args.NodeID != 0 {
			// Decide whether or not we can accept the incoming connection
			// as a permanent peer.
			if s.incoming.hasNode(args.NodeID) {
				// Do nothing.
			} else if s.incoming.hasSpace() {
				s.incoming.addNode(args.NodeID)
				s.nodeMap[args.Addr] = args.NodeID

				defer func(nodeID roachpb.NodeID, addr util.UnresolvedAddr) {
					s.incoming.removeNode(nodeID)
					delete(s.nodeMap, addr)
				}(args.NodeID, args.Addr)
			} else {
				var alternateAddr util.UnresolvedAddr
				var alternateNodeID roachpb.NodeID
				// Choose a random peer for forwarding.
				altIdx := rand.Intn(len(s.nodeMap))
				for addr, id := range s.nodeMap {
					if id == s.is.NodeID {
						// Don't forward back to ourselves.
						continue
					}
					// Keep track of a valid forwarding peer in case the randomly
					// selected node is the last node in the map and that node is
					// ourself.
					alternateAddr = addr
					alternateNodeID = id
					if altIdx == 0 {
						break
					}
					altIdx--
				}

				if alternateNodeID == s.is.NodeID {
					panic("cannot recommend self as node to forward to")
				}

				log.Infof("refusing gossip from node %d (max %d conns); forwarding to %d (%s)",
					args.NodeID, s.incoming.maxSize, alternateNodeID, alternateAddr)

				*reply = Response{
					NodeID:          s.is.NodeID,
					AlternateAddr:   &alternateAddr,
					AlternateNodeID: alternateNodeID,
				}

				s.mu.Unlock()
				err := senderFn(reply)
				s.mu.Lock()
				return err
			}
		}

		s.received += len(args.Delta)
		freshCount, err := s.is.combine(args.Delta, args.NodeID)
		if err != nil {
			log.Warningf("node %d failed to fully combine gossip delta from node %d: %s", s.is.NodeID, args.NodeID, err)
		}
		if log.V(1) {
			log.Infof("node %d received %s from node %d (%d fresh)", s.is.NodeID, extractKeys(args.Delta), args.NodeID, freshCount)
		}
		s.maybeTighten()

		*reply = Response{
			NodeID:          s.is.NodeID,
			HighWaterStamps: s.is.getHighWaterStamps(),
		}

		s.mu.Unlock()
		err = senderFn(reply)
		s.mu.Lock()
		if err != nil {
			return err
		}

		if cycler := s.simulationCycler; cycler != nil {
			cycler.Wait()
		}

		s.mu.Unlock()
		recvArgs, err := receiverFn()
		s.mu.Lock()
		if err != nil {
			return err
		}

		// *argsPtr holds the remote peer state; we need to update it whenever we
		// receive a new non-nil request. We avoid assigning to *argsPtr directly
		// because the gossip sender above has closed over *argsPtr and will NPE if
		// *argsPtr were set to nil.
		*argsPtr = recvArgs
	}
}

// InfosSent returns the total count of infos sent to clients.
func (s *server) InfosSent() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.sent
}

// InfosReceived returns the total count of infos received from clients.
func (s *server) InfosReceived() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.received
}

// maybeTighten examines the infostore for the most distant node and
// if more distant than MaxHops, sends on the tightenNetwork channel
// to start a new client connection.
func (s *server) maybeTighten() {
	distantNodeID, distantHops := s.is.mostDistant()
	if log.V(1) {
		log.Infof("@%d: distantHops: %d from %d", s.is.NodeID, distantHops, distantNodeID)
	}
	if distantHops > MaxHops {
		select {
		case s.tighten <- distantNodeID:
			if log.V(1) {
				log.Infof("if possible, tightening network to node %d (%d > %d)", distantNodeID, distantHops, MaxHops)
			}
		default:
			// Do nothing.
		}
	}
}

// start initializes the infostore with the rpc server address and
// then begins processing connecting clients in an infinite select
// loop via goroutine. Periodically, clients connected and awaiting
// the next round of gossip are awoken via the conditional variable.
func (s *server) start(grpcServer *grpc.Server, addr net.Addr) {
	s.mu.Lock()
	s.is.NodeAddr = util.MakeUnresolvedAddr(addr.Network(), addr.String())
	s.mu.Unlock()
	RegisterGossipServer(grpcServer, s)

	broadcast := func() {
		ready := make(chan struct{})

		s.mu.Lock()
		close(s.ready)
		s.ready = ready
		s.mu.Unlock()
	}
	unregister := s.is.registerCallback(".*", func(_ string, _ roachpb.Value) {
		broadcast()
	})

	s.stopper.RunWorker(func() {
		<-s.stopper.ShouldDrain()

		s.mu.Lock()
		unregister()
		s.mu.Unlock()

		broadcast()
	})
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"reflect"
	"testing"
)

func TestIsDir(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	// test
	if !IsDir("tmp") {
		t.Error("IsDir() returned false for directory tmp")
	}

	// cleanUp
	err := os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

func TestIsGoFile(t *testing.T) {
	filename := "test.go"
	if !IsGoFile(filename) {
		t.Error("IsGoFile() returned false for filename test.go")
	}
}

func TestParseFilenames(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	os.Create("tmp/test.txt")
	os.Create("tmp/test2.md")

	test_data := []string{"test.txt", "test2.md"}

	currentDirectory, err := os.Getwd()
	if err != nil {
		t.Error("An error occured trying to get current working directory,", err)
	}

	// test
	filenames := ParseFilenames(currentDirectory + "/tmp")
	if !reflect.DeepEqual(filenames, test_data) {
		t.Error("ParseFilenames() did not return expected value.")
	}

	// cleanUp
	err = os.Remove("tmp/test.txt")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test.txt,", err)
	}

	err = os.Remove("tmp/test2.md")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test2.md,", err)
	}

	err = os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

func TestCheckDirForGo(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	os.Create("tmp/test.go")

	currentDirectory, err := os.Getwd()
	if err != nil {
		t.Error("An error occured trying to get current working directory,", err)
	}

	// test
	if !CheckDirForGo(currentDirectory + "/tmp") {
		t.Error("CheckDirForGo() did not properly identify a directory with a .go file in it.")
	}

	// cleanUp
	err = os.Remove("tmp/test.go")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test.go,", err)
	}

	err = os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

func TestUpdatePackage(t *testing.T) {

	currentDirectory, err := os.Getwd()
	if err != nil {
		t.Error("An error occured trying to get current working directory,", err)
	}

	updated := UpdatePackage(currentDirectory)
	if !updated {
		t.Error("An error occured trying to update this Go package,", err)
	}
}
<commit_msg>Disabled TestUpdatePackage, fails wercker test, wercker not setting up the version control for its test environment<commit_after>package main

import (
	"os"
	"reflect"
	"testing"
)

func TestIsDir(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	// test
	if !IsDir("tmp") {
		t.Error("IsDir() returned false for directory tmp")
	}

	// cleanUp
	err := os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

func TestIsGoFile(t *testing.T) {
	filename := "test.go"
	if !IsGoFile(filename) {
		t.Error("IsGoFile() returned false for filename test.go")
	}
}

func TestParseFilenames(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	os.Create("tmp/test.txt")
	os.Create("tmp/test2.md")

	test_data := []string{"test.txt", "test2.md"}

	currentDirectory, err := os.Getwd()
	if err != nil {
		t.Error("An error occured trying to get current working directory,", err)
	}

	// test
	filenames := ParseFilenames(currentDirectory + "/tmp")
	if !reflect.DeepEqual(filenames, test_data) {
		t.Error("ParseFilenames() did not return expected value.")
	}

	// cleanUp
	err = os.Remove("tmp/test.txt")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test.txt,", err)
	}

	err = os.Remove("tmp/test2.md")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test2.md,", err)
	}

	err = os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

func TestCheckDirForGo(t *testing.T) {
	// setUp
	os.Mkdir("tmp", 0777)

	os.Create("tmp/test.go")

	currentDirectory, err := os.Getwd()
	if err != nil {
		t.Error("An error occured trying to get current working directory,", err)
	}

	// test
	if !CheckDirForGo(currentDirectory + "/tmp") {
		t.Error("CheckDirForGo() did not properly identify a directory with a .go file in it.")
	}

	// cleanUp
	err = os.Remove("tmp/test.go")
	if err != nil {
		t.Error("An error occured trying to remove tmp/test.go,", err)
	}

	err = os.Remove("tmp")
	if err != nil {
		t.Error("An error occured trying to remove tmp/,", err)
	}
}

// wercker doesn't setup a proper clone, must fix before enabling
// func TestUpdatePackage(t *testing.T) {

// 	currentDirectory, err := os.Getwd()
// 	if err != nil {
// 		t.Error("An error occured trying to get current working directory,", err)
// 	}

// 	updated := UpdatePackage(currentDirectory)
// 	if !updated {
// 		t.Error("An error occured trying to update this Go package,", err)
// 	}
// }
<|endoftext|>


<commit_before>package graph

// Bfs does Breadth First Search and return the result in visited order.
// Bfs traverses graphs in an arbitrary order. Time complexity is O(|V| + |E|).
// Bfs uses queue. Dfs uses recursion or stack.
// (http://en.wikipedia.org/wiki/Breadth-first_search)
//
//	1  procedure Bfs(G,v) is
//	2      let Q be a queue
//	3      Q.push(v)
//	4      label v as discovered
//	5      while Q is not empty
//	6         v ← Q.pop()
//	7         for all edges from v to w in G.adjacentEdges(v) do
//	8             if w is not labeled as discovered
//	9                 Q.push(w)
//	10                label w as discovered
//
func (d *Data) Bfs(src *Node) []*Node {

	if src == nil {
		return nil
	}

	result := []*Node{}

	src.Color = "black"
	queue := []*Node{src}

	for len(queue) != 0 {

		front := queue[0]
		queue = queue[1:len(queue):len(queue)]

		for ov := range front.WeightTo {
			if ov.Color == "white" {
				ov.Color = "black"
				queue = append(queue, ov)
			}
		}
		for iv := range front.WeightFrom {
			if iv.Color == "white" {
				iv.Color = "black"
				queue = append(queue, iv)
			}
		}

		front.Color = "black"
		result = append(result, front)
	}

	return result
}

// DfsStack searches a graph with depth-first.
// (http://en.wikipedia.org/wiki/Depth-first_search)
//
//	1  procedure DFS-iterative(G,v):
//	2      let S be a stack
//	3      S.push(v)
//	4      while S is not empty
//	5            v = S.pop()
//	6            if v is not labeled as discovered:
//	7                label v as discovered
//	8                for all edges from v to w in G.adjacentEdges(v) do
//	9                    S.push(w)
//
func (d *Data) DfsStack(src *Node) []*Node {

	if src == nil {
		return nil
	}

	result := []*Node{}
	stack := []*Node{src}

	for len(stack) != 0 {

		back := stack[len(stack)-1]
		stack = stack[:len(stack)-1 : len(stack)-1]

		if back.Color == "white" {

			back.Color = "black"
			result = append(result, back)

			for ov := range back.WeightTo {
				stack = append(stack, ov)
			}
			for iv := range back.WeightFrom {
				stack = append(stack, iv)
			}
		}

	}

	return result
}

// Dfs recursively traverses a graph.
// (http://en.wikipedia.org/wiki/Depth-first_search)
//
//	1  procedure DFS(G,v):
//	2      label v as discovered
//	3      for all edges from v to w in G.adjacentEdges(v) do
//	4          if vertex w is not labeled as discovered then
//	5              recursively call DFS(G,w)
//
func (d *Data) Dfs(src *Node, result *[]*Node) {

	if src == nil {
		return
	}

	if src.Color == "black" {
		return
	}

	src.Color = "black"
	*result = append(*result, src)

	for ov := range src.WeightTo {
		if ov.Color == "white" {
			d.Dfs(ov, result)
		}
	}
	for iv := range src.WeightFrom {
		if iv.Color == "white" {
			d.Dfs(iv, result)
		}
	}
}
<commit_msg>remove redundant coloring<commit_after>package graph

// Bfs does Breadth First Search and return the result in visited order.
// Bfs traverses graphs in an arbitrary order. Time complexity is O(|V| + |E|).
// Bfs uses queue. Dfs uses recursion or stack.
// (http://en.wikipedia.org/wiki/Breadth-first_search)
//
//	1  procedure Bfs(G,v) is
//	2      let Q be a queue
//	3      Q.push(v)
//	4      label v as discovered
//	5      while Q is not empty
//	6         v ← Q.pop()
//	7         for all edges from v to w in G.adjacentEdges(v) do
//	8             if w is not labeled as discovered
//	9                 Q.push(w)
//	10                label w as discovered
//
func (d *Data) Bfs(src *Node) []*Node {

	if src == nil {
		return nil
	}

	result := []*Node{}

	src.Color = "black"
	queue := []*Node{src}

	for len(queue) != 0 {

		front := queue[0]
		queue = queue[1:len(queue):len(queue)]

		for ov := range front.WeightTo {
			if ov.Color == "white" {
				ov.Color = "black"
				queue = append(queue, ov)
			}
		}
		for iv := range front.WeightFrom {
			if iv.Color == "white" {
				iv.Color = "black"
				queue = append(queue, iv)
			}
		}

		// front.Color = "black"
		result = append(result, front)
	}

	return result
}

// DfsStack searches a graph with depth-first.
// (http://en.wikipedia.org/wiki/Depth-first_search)
//
//	1  procedure DFS-iterative(G,v):
//	2      let S be a stack
//	3      S.push(v)
//	4      while S is not empty
//	5            v = S.pop()
//	6            if v is not labeled as discovered:
//	7                label v as discovered
//	8                for all edges from v to w in G.adjacentEdges(v) do
//	9                    S.push(w)
//
func (d *Data) DfsStack(src *Node) []*Node {

	if src == nil {
		return nil
	}

	result := []*Node{}
	stack := []*Node{src}

	for len(stack) != 0 {

		back := stack[len(stack)-1]
		stack = stack[:len(stack)-1 : len(stack)-1]

		if back.Color == "white" {

			back.Color = "black"
			result = append(result, back)

			for ov := range back.WeightTo {
				stack = append(stack, ov)
			}
			for iv := range back.WeightFrom {
				stack = append(stack, iv)
			}
		}

	}

	return result
}

// Dfs recursively traverses a graph.
// (http://en.wikipedia.org/wiki/Depth-first_search)
//
//	1  procedure DFS(G,v):
//	2      label v as discovered
//	3      for all edges from v to w in G.adjacentEdges(v) do
//	4          if vertex w is not labeled as discovered then
//	5              recursively call DFS(G,w)
//
func (d *Data) Dfs(src *Node, result *[]*Node) {

	if src == nil {
		return
	}

	if src.Color == "black" {
		return
	}

	src.Color = "black"
	*result = append(*result, src)

	for ov := range src.WeightTo {
		if ov.Color == "white" {
			d.Dfs(ov, result)
		}
	}
	for iv := range src.WeightFrom {
		if iv.Color == "white" {
			d.Dfs(iv, result)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

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

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

	c, err := Load("./example.toml")
	assert.Nil(err)

	assert.Equal("29300", c.Port)
	assert.Equal("error", c.LogLevel)
	assert.Equal(5, c.Timeout)
	assert.Equal(100, c.MaxIdleConnsPerHost)
	assert.Equal(false, c.DisableCompression)

	eps := c.Endpoints
	assert.Equal(2, len(eps))
}

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

	c, err := Load("./example.toml")
	assert.Nil(err)

	ep, err := FindEp(c, "ep-1")
	assert.Nil(err)
	assert.Equal("ep-1", ep.Name)
	assert.Equal("127.0.0.1:30001", ep.Ep)
	assert.Equal("Host", ep.ProxySetHeaders[0][0])
	assert.Equal("ep1.example.com", ep.ProxySetHeaders[0][1])

	ep, err = FindEp(c, "ep-2")
	assert.Nil(err)
	assert.Equal("ep-2", ep.Name)
	assert.Equal("127.0.0.1:30002", ep.Ep)
	assert.Equal("Host", ep.ProxySetHeaders[0][0])
	assert.Equal("ep2.example.com", ep.ProxySetHeaders[0][1])

	_, err = FindEp(c, "ep-3")
	assert.NotNil(err)
}
<commit_msg>config: added TestLoadGlobalConfig().<commit_after>package config

import (
	"testing"

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

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

	c, err := Load("./example.toml")
	assert.Nil(err)

	assert.Equal("29300", c.Port)
	assert.Equal("error", c.LogLevel)
	assert.Equal(5, c.Timeout)
	assert.Equal(100, c.MaxIdleConnsPerHost)
	assert.Equal(false, c.DisableCompression)

	eps := c.Endpoints
	assert.Equal(2, len(eps))
}

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

	configStr := `
Port = "12345"
LogLevel = "debug"
Timeout = 10
MaxIdleConnsPerHost = 1000
DisableCompression = true
`

	c, err := LoadBytes([]byte(configStr))
	assert.Nil(err)

	assert.Equal("12345", c.Port)
	assert.Equal("debug", c.LogLevel)
	assert.Equal(10, c.Timeout)
	assert.Equal(1000, c.MaxIdleConnsPerHost)
	assert.Equal(true, c.DisableCompression)
}

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

	c, err := Load("./example.toml")
	assert.Nil(err)

	ep, err := FindEp(c, "ep-1")
	assert.Nil(err)
	assert.Equal("ep-1", ep.Name)
	assert.Equal("127.0.0.1:30001", ep.Ep)
	assert.Equal("Host", ep.ProxySetHeaders[0][0])
	assert.Equal("ep1.example.com", ep.ProxySetHeaders[0][1])

	ep, err = FindEp(c, "ep-2")
	assert.Nil(err)
	assert.Equal("ep-2", ep.Name)
	assert.Equal("127.0.0.1:30002", ep.Ep)
	assert.Equal("Host", ep.ProxySetHeaders[0][0])
	assert.Equal("ep2.example.com", ep.ProxySetHeaders[0][1])

	_, err = FindEp(c, "ep-3")
	assert.NotNil(err)
}
<|endoftext|>


<commit_before>package message

import (
	"errors"
)

var (
	// ErrConnectReturnCodeInvalid indicates Connect Return Code to be set is larger than 0x05
	ErrConnectReturnCodeInvalid = errors.New("Connect Return Code should not larger than 0x05")
)

// ConnackMessage is that the CONNACK Packet is the packet sent by the Server in
// response to a CONNECT Packet received from a Client. The first packet sent
// from the Server to the Client MUST be a CONNACK Packet [MQTT-3.2.0-1]
//
// If the Client does not receive a CONNACK Packet from the Server within a reasonable
// amount of time, the Client SHOULD close the Network Connection. A "reasonable"
// amount of time depends on the type of application and the communications infrastructure
type ConnackMessage struct {
	fixedHeader

	// Connect Acknowledge Flags must set
	// bits 7-1 are reserved and MUST be set to 0
	// bit 0 is the Session Present Flag
	//
	// If the Server accepts a connection with CleanSession set to 1, The Server MUST
	// set Session Present to 0 in the CONNACK packet in addition to setting a zero
	// return code in the CONNACK packet [MQTT-3.2.2-1]
	//
	// If the Server accepts a connection with CleanSession set to 0, the value set in
	// Session Present depends on whether the Server already has stored Session state
	// for the supplied client ID. If the Server has stored Session state, it MUST set
	// Session Present to 1 in the CONNACK packet [MQTT-3.2.2-2]. If the Server does
	// not have stored Session state, it MUST set Session Present to 0 in the CONNACK
	// packet. This is in addition to setting a zero return code in the CONNACK packet
	// [MQTT-3.2.2-3].
	//
	// The Session Present flag enables a Client to establish whether the Client and
	// Server have a consistent view about whether there is already stored Session
	// state.
	//
	// Once the initial setup of a Session is complete, a Client with stored Session
	// state will expect the Server to maintain its stored Session state. In the
	// event that the value of Session Present received by the Client from the Server
	// is not as expected, the Client can choose whether to proceed with the Session
	// or to disconnect. The Client can discard the Session state on both Client and
	// Server by disconnecting, connecting with Clean Session set to 1 and then
	// disconnecting again.
	//
	// If a Server sends a CONNACK packet containing a non-zero return code it MUST
	// set Session Present to 0[MQTT-3.2.2-4].
	connectAckFlags byte

	// The values for the one byte unsigned Connect Return code field are listed in
	// Table 3.1 - Connect Return code values. If a well formed CONNECT Packet is
	// received by the Server, but the Server is unable to process it for some
	// reason, then the Server SHOULD attempt to send a CONNACK packet containing
	// the appropriate non-zero Connect return code from this table. If a server
	// sends a CONNACK packet cotaining a non-zero return code it MUST then close
	// the Network Connection [MQTT-3.2.2-5].
	// -----------------------------------------------------------------------------
	// | Value | Return Code Response      |           Description                 |
	// -----------------------------------------------------------------------------
	// |   0   | 0x00 Connection Accepted  | Connection accepted                   |
	// -----------------------------------------------------------------------------
	// |       | 0x01 Connection Refused,  | The Server does not support the level |
	// |   1   | unacceptable protocol     | of the MQTT protocol requested by the |
	// |       | version                   | Client                                |
	// -----------------------------------------------------------------------------
	// |   2   | 0x02 Connection Refused,  | The Client identifier is correct UTF-8|
	// |       | identifier rejected       | but not allowed by the Server         |
	// -----------------------------------------------------------------------------
	// |   3   | 0x03 Connection Refused,  | The Network Connection has been made  |
	// |       | Server unavailable        | but the MQTT service is unavailable   |
	// -----------------------------------------------------------------------------
	// |   4   | 0x04 Connection Refused,  | The data in the user name or password |
	// |       | bad user name or password | is malformed                          |
	// -----------------------------------------------------------------------------
	// |   5   | 0x05 Connection Refused,  | The Client is not authorized to       |
	// |       | not authorized            | connect                               |
	// -----------------------------------------------------------------------------
	// | 6-255 |                           | Reserved for future use               |
	// -----------------------------------------------------------------------------
	// If none of the return codes listed in Table 3.1 - Connect Return code values
	// are deemed applicable, then the Server MUST close the Network Connection
	// without sending a CONNACK [MQTT-3.2.2-6]
	connectReturnCode byte
}

// SetSessionPresent actives Session Present
func (c *ConnackMessage) SetSessionPresent(active bool) {
	if active {
		// 00000001
		c.connectAckFlags |= 0x01
	} else {
		// 11111110
		c.connectAckFlags &= 0xFE
	}
}

// SessionPresent returns Session Present
func (c *ConnackMessage) SessionPresent() byte {
	return c.connectAckFlags & 0x01
}

// SetConnectReturnCode sets Connect Return Code
func (c *ConnackMessage) SetConnectReturnCode(v byte) error {
	if v > 0x05 {
		return ErrConnectReturnCodeInvalid
	}

	c.connectReturnCode = v
	return nil
}

// ConnectReturnCode returns Connect Return Code
func (c *ConnackMessage) ConnectReturnCode() byte {
	return c.connectReturnCode
}
<commit_msg>fix typo<commit_after>package message

import (
	"errors"
)

var (
	// ErrConnectReturnCodeInvalid indicates Connect Return Code to be set is larger than 0x05
	ErrConnectReturnCodeInvalid = errors.New("Connect Return Code should not larger than 0x05")
)

// ConnackMessage is that the CONNACK Packet is the packet sent by the Server in
// response to a CONNECT Packet received from a Client. The first packet sent
// from the Server to the Client MUST be a CONNACK Packet [MQTT-3.2.0-1]
//
// If the Client does not receive a CONNACK Packet from the Server within a reasonable
// amount of time, the Client SHOULD close the Network Connection. A "reasonable"
// amount of time depends on the type of application and the communications infrastructure
type ConnackMessage struct {
	fixedHeader

	// Connect Acknowledge Flags must set
	// bits 7-1 are reserved and MUST be set to 0
	// bit 0 is the Session Present Flag
	//
	// If the Server accepts a connection with CleanSession set to 1, The Server MUST
	// set Session Present to 0 in the CONNACK packet in addition to setting a zero
	// return code in the CONNACK packet [MQTT-3.2.2-1]
	//
	// If the Server accepts a connection with CleanSession set to 0, the value set in
	// Session Present depends on whether the Server already has stored Session state
	// for the supplied client ID. If the Server has stored Session state, it MUST set
	// Session Present to 1 in the CONNACK packet [MQTT-3.2.2-2]. If the Server does
	// not have stored Session state, it MUST set Session Present to 0 in the CONNACK
	// packet. This is in addition to setting a zero return code in the CONNACK packet
	// [MQTT-3.2.2-3].
	//
	// The Session Present flag enables a Client to establish whether the Client and
	// Server have a consistent view about whether there is already stored Session
	// state.
	//
	// Once the initial setup of a Session is complete, a Client with stored Session
	// state will expect the Server to maintain its stored Session state. In the
	// event that the value of Session Present received by the Client from the Server
	// is not as expected, the Client can choose whether to proceed with the Session
	// or to disconnect. The Client can discard the Session state on both Client and
	// Server by disconnecting, connecting with Clean Session set to 1 and then
	// disconnecting again.
	//
	// If a Server sends a CONNACK packet containing a non-zero return code it MUST
	// set Session Present to 0[MQTT-3.2.2-4].
	connectAckFlags byte

	// The values for the one byte unsigned Connect Return code field are listed in
	// Table 3.1 - Connect Return code values. If a well formed CONNECT Packet is
	// received by the Server, but the Server is unable to process it for some
	// reason, then the Server SHOULD attempt to send a CONNACK packet containing
	// the appropriate non-zero Connect return code from this table. If a server
	// sends a CONNACK packet cotaining a non-zero return code it MUST then close
	// the Network Connection [MQTT-3.2.2-5].
	// -----------------------------------------------------------------------------
	// | Value | Return Code Response      |           Description                 |
	// -----------------------------------------------------------------------------
	// |   0   | 0x00 Connection Accepted  | Connection accepted                   |
	// -----------------------------------------------------------------------------
	// |       | 0x01 Connection Refused,  | The Server does not support the level |
	// |   1   | unacceptable protocol     | of the MQTT protocol requested by the |
	// |       | version                   | Client                                |
	// -----------------------------------------------------------------------------
	// |   2   | 0x02 Connection Refused,  | The Client identifier is correct UTF-8|
	// |       | identifier rejected       | but not allowed by the Server         |
	// -----------------------------------------------------------------------------
	// |   3   | 0x03 Connection Refused,  | The Network Connection has been made  |
	// |       | Server unavailable        | but the MQTT service is unavailable   |
	// -----------------------------------------------------------------------------
	// |   4   | 0x04 Connection Refused,  | The data in the user name or password |
	// |       | bad user name or password | is malformed                          |
	// -----------------------------------------------------------------------------
	// |   5   | 0x05 Connection Refused,  | The Client is not authorized to       |
	// |       | not authorized            | connect                               |
	// -----------------------------------------------------------------------------
	// | 6-255 |                           | Reserved for future use               |
	// -----------------------------------------------------------------------------
	// If none of the return codes listed in Table 3.1 - Connect Return code values
	// are deemed applicable, then the Server MUST close the Network Connection
	// without sending a CONNACK [MQTT-3.2.2-6]
	connectReturnCode byte
}

// SetSessionPresent actives Session Present
func (c *ConnackMessage) SetSessionPresent(active bool) {
	if active {
		// 00000001
		c.connectAckFlags |= 0x01
	} else {
		// 11111110
		c.connectAckFlags &= 0xFE
	}
}

// SessionPresent returns Session Present
func (c *ConnackMessage) SessionPresent() byte {
	return c.connectAckFlags & 0x01
}

// SetConnectReturnCode sets Connect Return code
func (c *ConnackMessage) SetConnectReturnCode(v byte) error {
	if v > 0x05 {
		return ErrConnectReturnCodeInvalid
	}

	c.connectReturnCode = v
	return nil
}

// ConnectReturnCode returns Connect Return code
func (c *ConnackMessage) ConnectReturnCode() byte {
	return c.connectReturnCode
}
<|endoftext|>


<commit_before>package meta

import (
	"sort"
)

// Collection describes the period where a sensor and a datalogger are co-located at a site with the associated streams.
type Collection struct {
	Span

	Stream
	Channel
	Component

	InstalledSensor
	DeployedDatalogger

	Gains []Gain

	SensorCalibrations     []Calibration
	DataloggerCalibrations []Calibration
}

// Less compares whether one Collection will sort before another.
func (c Collection) Less(collection Collection) bool {
	switch {
	case c.InstalledSensor.Station < collection.InstalledSensor.Station:
		return true
	case c.InstalledSensor.Station > collection.InstalledSensor.Station:
		return false
	case c.InstalledSensor.Location < collection.InstalledSensor.Location:
		return true
	case c.InstalledSensor.Location > collection.InstalledSensor.Location:
		return false
	case c.Component.Number < collection.Component.Number:
		return true
	case c.Component.Number > collection.Component.Number:
		return false
	case c.Channel.Number < collection.Channel.Number:
		return true
	case c.Channel.Number > collection.Channel.Number:
		return false
	case c.Span.Start.Before(collection.Span.Start):
		return true
	case c.Span.Start.After(collection.Span.Start):
		return false
	case c.Stream.SamplingRate > collection.Stream.SamplingRate:
		return true
	default:
		return false
	}
}

// Collections decodes the stored sensor and datalogger installation
// times and builds a slice of overlapping time spans for the given site.
func (s *Set) Collections(site Site) []Collection {
	var collections []Collection

	for _, recorder := range s.InstalledRecorders() {
		if recorder.Station != site.Station {
			continue
		}
		if recorder.Location != site.Location {
			continue
		}

		for _, stream := range s.Streams() {
			if stream.Station != site.Station {
				continue
			}
			if stream.Location != site.Location {
				continue
			}

			span, ok := recorder.Span.Extent(stream.Span)
			if !ok {
				continue
			}

			for _, component := range s.Components() {
				if recorder.InstalledSensor.Make != component.Make {
					continue
				}
				if recorder.InstalledSensor.Model != component.Model {
					continue
				}

				for _, channel := range s.Channels() {
					if recorder.Make != channel.Make {
						continue
					}

					if recorder.DataloggerModel != channel.Model {
						continue
					}
					if stream.SamplingRate != channel.SamplingRate {
						continue
					}

					var gains []Gain
					for _, g := range s.Gains() {
						if g.Station != stream.Station {
							continue
						}
						if g.Location != stream.Location {
							continue
						}
						if g.Subsource != component.Subsource {
							continue
						}
						if !span.Overlaps(g.Span) {
							continue
						}
						gains = append(gains, g)
					}
					sort.Slice(gains, func(i, j int) bool {
						return gains[i].Span.Start.Before(gains[j].Span.Start)
					})

					var sensors []Calibration
					for _, c := range s.Calibrations() {
						if c.Make != recorder.InstalledSensor.Make {
							continue
						}
						if c.Model != recorder.InstalledSensor.Model {
							continue
						}
						if c.Serial != recorder.InstalledSensor.Serial {
							continue
						}
						if !span.Overlaps(c.Span) {
							continue
						}
						sensors = append(sensors, c)
					}
					sort.Slice(sensors, func(i, j int) bool {
						return sensors[i].Span.Start.Before(sensors[j].Span.Start)
					})

					var dataloggers []Calibration
					for _, c := range s.Calibrations() {
						if c.Make != recorder.InstalledSensor.Make {
							continue
						}
						if c.Model != recorder.DataloggerModel {
							continue
						}
						if c.Serial != recorder.InstalledSensor.Serial {
							continue
						}
						if !span.Overlaps(c.Span) {
							continue
						}
						dataloggers = append(dataloggers, c)
					}
					sort.Slice(dataloggers, func(i, j int) bool {
						return dataloggers[i].Span.Start.Before(dataloggers[j].Span.Start)
					})

					collections = append(collections, Collection{
						InstalledSensor: recorder.InstalledSensor,
						DeployedDatalogger: DeployedDatalogger{
							Install: Install{
								Equipment: Equipment{
									Make:   recorder.InstalledSensor.Make,
									Model:  recorder.DataloggerModel,
									Serial: recorder.InstalledSensor.Serial,
								},
								Span: Span{
									Start: recorder.Start,
									End:   recorder.End,
								},
							},
						},
						Stream:                 stream,
						Gains:                  gains,
						SensorCalibrations:     sensors,
						DataloggerCalibrations: dataloggers,
						Channel:                channel,
						Component:              component,
						Span:                   span,
					})
				}
			}
		}
	}

	for _, connection := range s.Connections() {
		if connection.Station != site.Station {
			continue
		}
		if connection.Location != site.Location {
			continue
		}

		for _, sensor := range s.InstalledSensors() {
			if sensor.Station != site.Station {
				continue
			}
			if sensor.Location != site.Location {
				continue
			}

			for _, component := range s.Components() {
				if sensor.Make != component.Make {
					continue
				}
				if sensor.Model != component.Model {
					continue
				}

				for _, datalogger := range s.DeployedDataloggers() {
					if datalogger.Place != connection.Place {
						continue
					}
					if datalogger.Role != connection.Role {
						continue
					}

					span, ok := connection.Span.Extent(sensor.Span, datalogger.Span)
					if !ok {
						continue
					}

					for _, stream := range s.Streams() {
						if stream.Station != site.Station {
							continue
						}
						if stream.Location != site.Location {
							continue
						}

						span, ok := span.Extent(stream.Span)
						if !ok {
							continue
						}

						for _, channel := range s.Channels() {
							if datalogger.Make != channel.Make {
								continue
							}
							if datalogger.Model != channel.Model {
								continue
							}
							if component.Number+connection.Number < channel.Number {
								continue
							}

							if stream.SamplingRate != channel.SamplingRate {
								continue
							}

							var gains []Gain
							for _, g := range s.Gains() {
								if g.Station != stream.Station {
									continue
								}
								if g.Location != stream.Location {
									continue
								}
								if g.Subsource != component.Subsource {
									continue
								}
								if !span.Overlaps(g.Span) {
									continue
								}
								gains = append(gains, g)
							}
							sort.Slice(gains, func(i, j int) bool {
								return gains[i].Span.Start.Before(gains[j].Span.Start)
							})

							var sensors []Calibration
							for _, c := range s.Calibrations() {
								if c.Make != sensor.Make {
									continue
								}
								if c.Model != sensor.Model {
									continue
								}
								if c.Serial != sensor.Serial {
									continue
								}
								if c.Number != component.Number {
									continue
								}
								if !span.Overlaps(c.Span) {
									continue
								}
								sensors = append(sensors, c)
							}
							sort.Slice(sensors, func(i, j int) bool {
								return sensors[i].Span.Start.Before(sensors[j].Span.Start)
							})

							var dataloggers []Calibration
							for _, c := range s.Calibrations() {
								if c.Make != datalogger.Make {
									continue
								}
								if c.Model != datalogger.Model {
									continue
								}
								if c.Serial != datalogger.Serial {
									continue
								}
								if c.Number != channel.Number {
									continue
								}
								if !span.Overlaps(c.Span) {
									continue
								}
								dataloggers = append(dataloggers, c)
							}
							sort.Slice(dataloggers, func(i, j int) bool {
								return dataloggers[i].Span.Start.Before(dataloggers[j].Span.Start)
							})

							collections = append(collections, Collection{
								InstalledSensor:        sensor,
								DeployedDatalogger:     datalogger,
								Stream:                 stream,
								Gains:                  gains,
								SensorCalibrations:     sensors,
								DataloggerCalibrations: dataloggers,
								Channel:                channel,
								Component:              component,
								Span:                   span,
							})

						}
					}
				}
			}
		}
	}

	sort.Slice(collections, func(i, j int) bool {
		return collections[i].Less(collections[j])
	})

	return collections
}
<commit_msg>add collection Code function<commit_after>package meta

import (
	"sort"
	"strings"
)

// Collection describes the period where a sensor and a datalogger are co-located at a site with the associated streams.
type Collection struct {
	Span

	Stream
	Channel
	Component

	InstalledSensor
	DeployedDatalogger

	Gains []Gain

	SensorCalibrations     []Calibration
	DataloggerCalibrations []Calibration
}

// Less compares whether one Collection will sort before another.
func (c Collection) Less(collection Collection) bool {
	switch {
	case c.InstalledSensor.Station < collection.InstalledSensor.Station:
		return true
	case c.InstalledSensor.Station > collection.InstalledSensor.Station:
		return false
	case c.InstalledSensor.Location < collection.InstalledSensor.Location:
		return true
	case c.InstalledSensor.Location > collection.InstalledSensor.Location:
		return false
	case c.Component.Number < collection.Component.Number:
		return true
	case c.Component.Number > collection.Component.Number:
		return false
	case c.Channel.Number < collection.Channel.Number:
		return true
	case c.Channel.Number > collection.Channel.Number:
		return false
	case c.Span.Start.Before(collection.Span.Start):
		return true
	case c.Span.Start.After(collection.Span.Start):
		return false
	case c.Stream.SamplingRate > collection.Stream.SamplingRate:
		return true
	default:
		return false
	}
}

// Subsource returns the Subsource code based on the Stream and Component values.
func (c Collection) Subsource() string {
	switch strings.ToLower(c.Stream.Axial) {
	case "true", "yes":
		switch strings.ToUpper(c.Component.Subsource) {
		case "N":
			return "1"
		case "E":
			return "2"
		default:
			return c.Component.Subsource
		}
	default:
		return c.Component.Subsource
	}
}

// Code returns the Channel code based on the Stream and Component values.
func (c Collection) Code() string {
	return c.Stream.Band + c.Stream.Source + c.Subsource()
}

// Collections decodes the stored sensor and datalogger installation
// times and builds a slice of overlapping time spans for the given site.
func (s *Set) Collections(site Site) []Collection {
	var collections []Collection

	for _, recorder := range s.InstalledRecorders() {
		if recorder.Station != site.Station {
			continue
		}
		if recorder.Location != site.Location {
			continue
		}

		for _, stream := range s.Streams() {
			if stream.Station != site.Station {
				continue
			}
			if stream.Location != site.Location {
				continue
			}

			span, ok := recorder.Span.Extent(stream.Span)
			if !ok {
				continue
			}

			for _, component := range s.Components() {
				if recorder.InstalledSensor.Make != component.Make {
					continue
				}
				if recorder.InstalledSensor.Model != component.Model {
					continue
				}

				for _, channel := range s.Channels() {
					if recorder.Make != channel.Make {
						continue
					}

					if recorder.DataloggerModel != channel.Model {
						continue
					}
					if stream.SamplingRate != channel.SamplingRate {
						continue
					}

					var gains []Gain
					for _, g := range s.Gains() {
						if g.Station != stream.Station {
							continue
						}
						if g.Location != stream.Location {
							continue
						}
						if g.Subsource != component.Subsource {
							continue
						}
						if !span.Overlaps(g.Span) {
							continue
						}
						gains = append(gains, g)
					}
					sort.Slice(gains, func(i, j int) bool {
						return gains[i].Span.Start.Before(gains[j].Span.Start)
					})

					var sensors []Calibration
					for _, c := range s.Calibrations() {
						if c.Make != recorder.InstalledSensor.Make {
							continue
						}
						if c.Model != recorder.InstalledSensor.Model {
							continue
						}
						if c.Serial != recorder.InstalledSensor.Serial {
							continue
						}
						if !span.Overlaps(c.Span) {
							continue
						}
						sensors = append(sensors, c)
					}
					sort.Slice(sensors, func(i, j int) bool {
						return sensors[i].Span.Start.Before(sensors[j].Span.Start)
					})

					var dataloggers []Calibration
					for _, c := range s.Calibrations() {
						if c.Make != recorder.InstalledSensor.Make {
							continue
						}
						if c.Model != recorder.DataloggerModel {
							continue
						}
						if c.Serial != recorder.InstalledSensor.Serial {
							continue
						}
						if !span.Overlaps(c.Span) {
							continue
						}
						dataloggers = append(dataloggers, c)
					}
					sort.Slice(dataloggers, func(i, j int) bool {
						return dataloggers[i].Span.Start.Before(dataloggers[j].Span.Start)
					})

					collections = append(collections, Collection{
						InstalledSensor: recorder.InstalledSensor,
						DeployedDatalogger: DeployedDatalogger{
							Install: Install{
								Equipment: Equipment{
									Make:   recorder.InstalledSensor.Make,
									Model:  recorder.DataloggerModel,
									Serial: recorder.InstalledSensor.Serial,
								},
								Span: Span{
									Start: recorder.Start,
									End:   recorder.End,
								},
							},
						},
						Stream:                 stream,
						Gains:                  gains,
						SensorCalibrations:     sensors,
						DataloggerCalibrations: dataloggers,
						Channel:                channel,
						Component:              component,
						Span:                   span,
					})
				}
			}
		}
	}

	for _, connection := range s.Connections() {
		if connection.Station != site.Station {
			continue
		}
		if connection.Location != site.Location {
			continue
		}

		for _, sensor := range s.InstalledSensors() {
			if sensor.Station != site.Station {
				continue
			}
			if sensor.Location != site.Location {
				continue
			}

			for _, component := range s.Components() {
				if sensor.Make != component.Make {
					continue
				}
				if sensor.Model != component.Model {
					continue
				}

				for _, datalogger := range s.DeployedDataloggers() {
					if datalogger.Place != connection.Place {
						continue
					}
					if datalogger.Role != connection.Role {
						continue
					}

					span, ok := connection.Span.Extent(sensor.Span, datalogger.Span)
					if !ok {
						continue
					}

					for _, stream := range s.Streams() {
						if stream.Station != site.Station {
							continue
						}
						if stream.Location != site.Location {
							continue
						}

						span, ok := span.Extent(stream.Span)
						if !ok {
							continue
						}

						for _, channel := range s.Channels() {
							if datalogger.Make != channel.Make {
								continue
							}
							if datalogger.Model != channel.Model {
								continue
							}
							if component.Number+connection.Number < channel.Number {
								continue
							}

							if stream.SamplingRate != channel.SamplingRate {
								continue
							}

							var gains []Gain
							for _, g := range s.Gains() {
								if g.Station != stream.Station {
									continue
								}
								if g.Location != stream.Location {
									continue
								}
								if g.Subsource != component.Subsource {
									continue
								}
								if !span.Overlaps(g.Span) {
									continue
								}
								gains = append(gains, g)
							}
							sort.Slice(gains, func(i, j int) bool {
								return gains[i].Span.Start.Before(gains[j].Span.Start)
							})

							var sensors []Calibration
							for _, c := range s.Calibrations() {
								if c.Make != sensor.Make {
									continue
								}
								if c.Model != sensor.Model {
									continue
								}
								if c.Serial != sensor.Serial {
									continue
								}
								if c.Number != component.Number {
									continue
								}
								if !span.Overlaps(c.Span) {
									continue
								}
								sensors = append(sensors, c)
							}
							sort.Slice(sensors, func(i, j int) bool {
								return sensors[i].Span.Start.Before(sensors[j].Span.Start)
							})

							var dataloggers []Calibration
							for _, c := range s.Calibrations() {
								if c.Make != datalogger.Make {
									continue
								}
								if c.Model != datalogger.Model {
									continue
								}
								if c.Serial != datalogger.Serial {
									continue
								}
								if c.Number != channel.Number {
									continue
								}
								if !span.Overlaps(c.Span) {
									continue
								}
								dataloggers = append(dataloggers, c)
							}
							sort.Slice(dataloggers, func(i, j int) bool {
								return dataloggers[i].Span.Start.Before(dataloggers[j].Span.Start)
							})

							collections = append(collections, Collection{
								InstalledSensor:        sensor,
								DeployedDatalogger:     datalogger,
								Stream:                 stream,
								Gains:                  gains,
								SensorCalibrations:     sensors,
								DataloggerCalibrations: dataloggers,
								Channel:                channel,
								Component:              component,
								Span:                   span,
							})

						}
					}
				}
			}
		}
	}

	sort.Slice(collections, func(i, j int) bool {
		return collections[i].Less(collections[j])
	})

	return collections
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"

	"github.com/sacloud/libsacloud/sacloud"
)

// ProductServerAPI サーバープランAPI
type ProductServerAPI struct {
	*baseAPI
}

// NewProductServerAPI サーバープランAPI作成
func NewProductServerAPI(client *Client) *ProductServerAPI {
	return &ProductServerAPI{
		&baseAPI{
			client: client,
			// FuncGetResourceURL
			FuncGetResourceURL: func() string {
				return "product/server"
			},
		},
	}
}

// GetBySpec 指定のコア数/メモリサイズ/世代のプランを取得
func (api *ProductServerAPI) GetBySpec(core, memGB int, gen sacloud.PlanGenerations) (*sacloud.ProductServer, error) {
	return api.GetBySpecCommitment(core, memGB, gen, sacloud.ECommitmentStandard)
}

// GetBySpecCommitment 指定のコア数/メモリサイズ/世代のプランを取得
func (api *ProductServerAPI) GetBySpecCommitment(core, memGB int, gen sacloud.PlanGenerations, commitment sacloud.ECommitment) (*sacloud.ProductServer, error) {
	plans, err := api.Reset().Find()
	if err != nil {
		return nil, err
	}
	var res sacloud.ProductServer
	var found bool
	for _, plan := range plans.ServerPlans {
		if plan.CPU == core && plan.GetMemoryGB() == memGB || plan.Commitment == commitment {
			if gen == sacloud.PlanDefault || gen == plan.Generation {
				// PlanDefaultの場合は複数ヒットしうる。
				// この場合より新しい世代を優先する。
				if found && plan.Generation <= res.Generation {
					continue
				}
				res = plan
				found = true
			}
		}
	}

	if !found {
		return nil, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not found", core, memGB, gen)
	}
	return &res, nil
}

// IsValidPlan 指定のコア数/メモリサイズ/世代のプランが存在し、有効であるか判定
func (api *ProductServerAPI) IsValidPlan(core int, memGB int, gen sacloud.PlanGenerations) (bool, error) {

	productServer, err := api.GetBySpec(core, memGB, gen)

	if err != nil {
		return false, err
	}

	if productServer == nil {
		return false, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not found", core, memGB, gen)
	}

	if productServer.Availability != sacloud.EAAvailable {
		return false, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not available", core, memGB, gen)
	}

	return true, nil
}
<commit_msg>Fix bug - GetBySpecCommitment<commit_after>package api

import (
	"fmt"

	"github.com/sacloud/libsacloud/sacloud"
)

// ProductServerAPI サーバープランAPI
type ProductServerAPI struct {
	*baseAPI
}

// NewProductServerAPI サーバープランAPI作成
func NewProductServerAPI(client *Client) *ProductServerAPI {
	return &ProductServerAPI{
		&baseAPI{
			client: client,
			// FuncGetResourceURL
			FuncGetResourceURL: func() string {
				return "product/server"
			},
		},
	}
}

// GetBySpec 指定のコア数/メモリサイズ/世代のプランを取得
func (api *ProductServerAPI) GetBySpec(core, memGB int, gen sacloud.PlanGenerations) (*sacloud.ProductServer, error) {
	return api.GetBySpecCommitment(core, memGB, gen, sacloud.ECommitmentStandard)
}

// GetBySpecCommitment 指定のコア数/メモリサイズ/世代のプランを取得
func (api *ProductServerAPI) GetBySpecCommitment(core, memGB int, gen sacloud.PlanGenerations, commitment sacloud.ECommitment) (*sacloud.ProductServer, error) {
	plans, err := api.Reset().Find()
	if err != nil {
		return nil, err
	}
	var res sacloud.ProductServer
	var found bool
	for _, plan := range plans.ServerPlans {
		if plan.CPU == core && plan.GetMemoryGB() == memGB && plan.Commitment == commitment {
			if gen == sacloud.PlanDefault || gen == plan.Generation {
				// PlanDefaultの場合は複数ヒットしうる。
				// この場合より新しい世代を優先する。
				if found && plan.Generation <= res.Generation {
					continue
				}
				res = plan
				found = true
			}
		}
	}

	if !found {
		return nil, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not found", core, memGB, gen)
	}
	return &res, nil
}

// IsValidPlan 指定のコア数/メモリサイズ/世代のプランが存在し、有効であるか判定
func (api *ProductServerAPI) IsValidPlan(core int, memGB int, gen sacloud.PlanGenerations) (bool, error) {

	productServer, err := api.GetBySpec(core, memGB, gen)

	if err != nil {
		return false, err
	}

	if productServer == nil {
		return false, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not found", core, memGB, gen)
	}

	if productServer.Availability != sacloud.EAAvailable {
		return false, fmt.Errorf("Server Plan[core:%d, memory:%d, gen:%d] is not available", core, memGB, gen)
	}

	return true, nil
}
<|endoftext|>


<commit_before>package dynago

import (
	"fmt"
)

const (
	STRING_ATTRIBUTE     = "S"
	STRING_SET_ATTRIBUTE = "SS"
	NUMBER_ATTRIBUTE     = "N"
	NUMBER_SET_ATTRIBUTE = "NS"
	BINARY_ATTRIBUTE     = "B"
	BINARY_SET_ATTRIBUTE = "BS"
)

var (
	BOOLEAN_VALUES = map[bool]string{true: "1", false: "0"}
)

// Attribute values are encoded as { "type": "value" }
type AttributeValue map[string]interface{}

// Attributes are encoded as { "name": { "type": "value" } }
type AttributeNameValue map[string]AttributeValue

// Encode a value according to its type
func EncodeValue(value interface{}) AttributeValue {
	switch v := value.(type) {
	case string:
		return AttributeValue{"S": v}

	case []string:
		return AttributeValue{"SS": v}

	case bool:
		return AttributeValue{"N": BOOLEAN_VALUES[v]}

	case uint, uint8, uint32, uint64, int, int8, int32, int64:
		return AttributeValue{"N": fmt.Sprintf("%d", v)}

	case float32:
		return AttributeValue{"N": fmt.Sprintf("%f", v)}

	case []float32:
		vv := make([]string, len(v))
		for i, n := range v {
			vv[i] = fmt.Sprintf("%f", n)
		}
		return AttributeValue{"NN": vv}

	case []float64:
		vv := make([]string, len(v))
		for i, n := range v {
			vv[i] = fmt.Sprintf("%f", n)
		}
		return AttributeValue{"NN": vv}

	default:
		return AttributeValue{}
	}
}

// Encode a value according to the attribute type
func EncodeAttributeValue(attr AttributeDefinition, value interface{}) AttributeValue {
	var v interface{}

	switch attr.AttributeType {
	case STRING_ATTRIBUTE:
		v = fmt.Sprintf("%v", value)

	case STRING_SET_ATTRIBUTE:
		switch value := value.(type) {
		case []string:
			v = value
		}

	case NUMBER_ATTRIBUTE:
		switch value := value.(type) {
		case string:
			v = value

		default:
			v = fmt.Sprintf("%f", value)
		}

	case NUMBER_SET_ATTRIBUTE:
		switch value := value.(type) {
		case []string:
			v = value

		case []int:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%v", n)
			}
			v = av

		case []float32:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%f", n)
			}
			v = av

		case []float64:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%f", n)
			}
			v = av
		}
	}

	return AttributeValue{attr.AttributeType: v}
}

func EncodeAttributeValues(attr AttributeDefinition, values ...interface{}) []AttributeValue {

	result := make([]AttributeValue, len(values))

	for i, v := range values {
		result[i] = EncodeAttributeValue(attr, v)
	}

	return result
}

// Encode an attribute with its value
func EncodeAttribute(attr AttributeDefinition, value interface{}) AttributeNameValue {
	return AttributeNameValue{attr.AttributeName: EncodeAttributeValue(attr, value)}
}
<commit_msg>Added DecodeValue method and encode/decode items.<commit_after>package dynago

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	STRING_ATTRIBUTE     = "S"
	STRING_SET_ATTRIBUTE = "SS"
	NUMBER_ATTRIBUTE     = "N"
	NUMBER_SET_ATTRIBUTE = "NS"
	BINARY_ATTRIBUTE     = "B"
	BINARY_SET_ATTRIBUTE = "BS"
)

var (
	BOOLEAN_VALUES = map[bool]string{true: "1", false: "0"}
)

// Attribute values are encoded as { "type": "value" }
type AttributeValue map[string]interface{}

// Attributes are encoded as { "name": { "type": "value" } }
type AttributeNameValue map[string]AttributeValue

// Items are encoded as maps of "name": { "type": "value" }
type DBItem map[string]AttributeValue

// Encode a value according to its type
func EncodeValue(value interface{}) AttributeValue {
	switch v := value.(type) {
	case string:
		return AttributeValue{STRING_ATTRIBUTE: v}

	case []string:
		return AttributeValue{STRING_SET_ATTRIBUTE: v}

	case bool:
		return AttributeValue{NUMBER_ATTRIBUTE: BOOLEAN_VALUES[v]}

	case uint, uint8, uint32, uint64, int, int8, int32, int64:
		return AttributeValue{NUMBER_ATTRIBUTE: fmt.Sprintf("%d", v)}

	case float32:
		return AttributeValue{NUMBER_ATTRIBUTE: fmt.Sprintf("%f", v)}

	case []float32:
		vv := make([]string, len(v))
		for i, n := range v {
			vv[i] = fmt.Sprintf("%f", n)
		}
		return AttributeValue{NUMBER_SET_ATTRIBUTE: vv}

	case []float64:
		vv := make([]string, len(v))
		for i, n := range v {
			vv[i] = fmt.Sprintf("%f", n)
		}
		return AttributeValue{NUMBER_SET_ATTRIBUTE: vv}

	default:
		return AttributeValue{}
	}
}

func DecodeValue(attrValue AttributeValue) interface{} {
	if len(attrValue) != 1 {
		// panic
	}

	for k, v := range attrValue {
		switch k {
		case STRING_ATTRIBUTE:
			return v.(string)

		case STRING_SET_ATTRIBUTE:
			return v.([]string)

		case NUMBER_ATTRIBUTE:
			s := v.(string)
			if strings.Contains(s, ".") {
				f, _ := strconv.ParseFloat(s, 32)
				return float32(f)
			} else {
				i, _ := strconv.Atoi(s)
				return i
			}

		case NUMBER_SET_ATTRIBUTE:
			ss := v.([]string)
			ff := make([]float32, len(ss))
			for i, n := range ss {
				f, _ := strconv.ParseFloat(n, 32)
				ff[i] = float32(f)
			}
			return ff
		}
	}

	return nil
}

// Encode a value according to the attribute type
func EncodeAttributeValue(attr AttributeDefinition, value interface{}) AttributeValue {
	if value == nil {
		return AttributeValue{attr.AttributeType: nil}
	} else if s, ok := value.(string); ok && s == "" {
		return AttributeValue{attr.AttributeType: nil}
	}

	var v interface{}

	switch attr.AttributeType {
	case STRING_ATTRIBUTE:
		v = fmt.Sprintf("%v", value)

	case STRING_SET_ATTRIBUTE:
		switch value := value.(type) {
		case []string:
			v = value
		}

	case NUMBER_ATTRIBUTE:
		switch value := value.(type) {
		case string:
			v = value

		default:
			v = fmt.Sprintf("%f", value)
		}

	case NUMBER_SET_ATTRIBUTE:
		switch value := value.(type) {
		case []string:
			v = value

		case []int:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%v", n)
			}
			v = av

		case []float32:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%f", n)
			}
			v = av

		case []float64:
			av := make([]string, len(value))
			for i, n := range value {
				av[i] = fmt.Sprintf("%f", n)
			}
			v = av
		}
	}

	return AttributeValue{attr.AttributeType: v}
}

func EncodeAttributeValues(attr AttributeDefinition, values ...interface{}) []AttributeValue {

	result := make([]AttributeValue, len(values))

	for i, v := range values {
		result[i] = EncodeAttributeValue(attr, v)
	}

	return result
}

// Encode an attribute with its value
func EncodeAttribute(attr AttributeDefinition, value interface{}) AttributeNameValue {
	return AttributeNameValue{attr.AttributeName: EncodeAttributeValue(attr, value)}
}

// Encode a user item (map of name/values) into a DynamoDB item
func EncodeItem(item map[string]interface{}) DBItem {
	result := make(DBItem)

	for k, v := range item {
		if v != nil {
			result[k] = EncodeValue(v)
		}
	}

	return result
}

func DecodeItem(item DBItem) map[string]interface{} {
	result := make(map[string]interface{})

	for k, v := range item {
		result[k] = DecodeValue(v)
	}

	return result
}
<|endoftext|>


<commit_before>package metrics

import (
	"github.com/smancke/guble/gubled/config"

	log "github.com/Sirupsen/logrus"

	"expvar"
	"fmt"
	"io"
	"net/http"
)

// IntVar is an interface for the operations defined on expvar.Int
type IntVar interface {
	Add(int64)
	Set(int64)
}

type emptyInt struct{}

// Dummy functions on EmptyInt
func (v *emptyInt) Add(delta int64) {}

func (v *emptyInt) Set(value int64) {}

// NewInt returns an expvar.Int or a dummy emptyInt, depending on the Enabled flag
func NewInt(name string) IntVar {
	//TODO Cosmin Bogdan the condition should be instead on: *config.Metrics.Enabled
	if *config.Metrics.Enabled {
		return expvar.NewInt(name)
	}
	return &emptyInt{}
}

// HttpHandler is a HTTP handler writing the current metrics to the http.ResponseWriter
func HttpHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	writeMetrics(rw)
}

func writeMetrics(w io.Writer) {
	fmt.Fprintf(w, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if !first {
			fmt.Fprintf(w, ",\n")
		}
		first = false
		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
	})
	fmt.Fprintf(w, "\n}\n")
}

// LogOnDebugLevel logs all the current metrics, if logging is on Debug level.
func LogOnDebugLevel() {
	if !*config.Metrics.Enabled {
		log.Debug("metrics: not enabled")
		return
	}
	if log.GetLevel() == log.DebugLevel {
		fields := log.Fields{}
		expvar.Do(func(kv expvar.KeyValue) {
			fields[kv.Key] = kv.Value
		})
		log.WithFields(fields).Debug("metrics: current values")
	}
}
<commit_msg>removed TODO<commit_after>package metrics

import (
	"github.com/smancke/guble/gubled/config"

	log "github.com/Sirupsen/logrus"

	"expvar"
	"fmt"
	"io"
	"net/http"
)

// IntVar is an interface for the operations defined on expvar.Int
type IntVar interface {
	Add(int64)
	Set(int64)
}

type emptyInt struct{}

// Dummy functions on EmptyInt
func (v *emptyInt) Add(delta int64) {}

func (v *emptyInt) Set(value int64) {}

// NewInt returns an expvar.Int or a dummy emptyInt, depending on the Enabled flag
func NewInt(name string) IntVar {
	if *config.Metrics.Enabled {
		return expvar.NewInt(name)
	}
	return &emptyInt{}
}

// HttpHandler is a HTTP handler writing the current metrics to the http.ResponseWriter
func HttpHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	writeMetrics(rw)
}

func writeMetrics(w io.Writer) {
	fmt.Fprintf(w, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if !first {
			fmt.Fprintf(w, ",\n")
		}
		first = false
		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
	})
	fmt.Fprintf(w, "\n}\n")
}

// LogOnDebugLevel logs all the current metrics, if logging is on Debug level.
func LogOnDebugLevel() {
	if !*config.Metrics.Enabled {
		log.Debug("metrics: not enabled")
		return
	}
	if log.GetLevel() == log.DebugLevel {
		fields := log.Fields{}
		expvar.Do(func(kv expvar.KeyValue) {
			fields[kv.Key] = kv.Value
		})
		log.WithFields(fields).Debug("metrics: current values")
	}
}
<|endoftext|>