<commit_before>// +build windows

/*
Copyright 2016 Google Inc.

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

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

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

/*
Package spliced processes domain join requests from the Cloud Datastore.

The core of the Splice joiner runs as a single goroutine which allows it
to function independently of the Windows service which manages it.
Under normal operation, the goroutine for spliced does not exit, unless the
parent Windows service stops.

A channel is used to enable the goroutine to signal an internal failure to
the Windows service, allowing it to shutdown cleanly. All other logging is
sent directly to EventLog.
*/
package main

import (
	"golang.org/x/net/context"
	"fmt"
	"time"

	metric "github.com/google/cabbie/metrics"
	"cloud.google.com/go/datastore"
	"github.com/google/splice/models"
	"github.com/google/splice/shared/certs"
	"github.com/google/splice/shared/crypto"
	"github.com/google/splice/shared/provisioning"
	"github.com/google/splice/spliced/metric/tracker"
	"github.com/google/splice/spliced/pubsub"
)

var (
	conf    appcfg
	metrics *tracker.Tracker

	// MetricRoot sets metric path for all SpliceD metrics
	metricRoot = "/splice/metrics"
	// MetricSvc sets platform source for metrics.
	metricSvc = "splice"
)

// ExitEvt holds an EventLog event explaining why the goroutine had to exit.
type ExitEvt struct {
	Code    uint32
	Message string
}

// Transaction holds an in-flight datastore transaction.
type Transaction struct {
	client *datastore.Client
	keys   []*datastore.Key
	req    models.Request
	tx     *datastore.Transaction
}

// startTransaction opens a datastore transaction and attempts to locate the record with id reqID.
func startTransaction(ctx context.Context, reqID string) (Transaction, error) {
	var trans Transaction
	var err error

	trans.client, err = datastore.NewClient(ctx, conf.ProjectID)
	if err != nil {
		return trans, fmt.Errorf("startTransaction: datastore client creation failed with %v", err)
	}

	trans.tx, err = trans.client.NewTransaction(ctx)
	if err != nil {
		return trans, fmt.Errorf("startTransaction: opening a datastore transaction failed with %v", err)
	}

	var requests []models.Request
	ancestor := datastore.NameKey("RequestID", reqID, nil)
	query := datastore.NewQuery("Request").Ancestor(ancestor).Transaction(trans.tx)

	if trans.keys, err = trans.client.GetAll(ctx, query, &requests); err != nil {
		return trans, fmt.Errorf("startTransaction: obtaining request from the datastore failed with %v", err)
	}

	if len(requests) < 1 {
		return trans, fmt.Errorf("startTransaction: no request received with ID %s", reqID)
	}
	trans.req = requests[0]

	return trans, nil
}

// returnRequest passes the result of the operation to the datastore on its way to the client.
func returnRequest(ctx context.Context, reqID string, success bool, meta *crypto.Metadata) error {
	trans, err := startTransaction(ctx, reqID)
	if err != nil {
		return err
	}
	defer trans.client.Close()

	trans.req.ResponseData = meta.Data
	if success {
		trans.req.Status = models.RequestStatusCompleted
		trans.req.ResponseKey = meta.AESKey
		trans.req.CipherNonce = meta.Nonce
		metrics.Get("join-success").Increment()
	} else {
		trans.req.Status = models.RequestStatusFailed

		metrics.Get("join-fail").Increment()
	}

	trans.req.CompletionTime = time.Now().UTC()

	if _, err := trans.tx.Put(trans.keys[0], &trans.req); err != nil {
		return fmt.Errorf("returnRequest: datastore update failed with %v", err)
	}

	if _, err := trans.tx.Commit(); err != nil {
		return fmt.Errorf("returnRequest: datastore commit failed with %v", err)
	}

	return nil
}

// claimRequest attempts to claim a new join request from the datastore.
func claimRequest(ctx context.Context, reqID string) (models.Request, error) {
	trans, err := startTransaction(ctx, reqID)
	if err != nil {
		return trans.req, err
	}
	defer trans.client.Close()

	if trans.req.Status != models.RequestStatusAccepted || trans.req.ClaimBy != "" {
		return trans.req, fmt.Errorf("claimRequest: request to %s already %s and will be ignored", trans.req.ClaimBy, trans.req.Status)
	}

	trans.req.ClaimBy = conf.Instance
	trans.req.ClaimTime = time.Now().UTC()

	if _, err := trans.tx.Put(trans.keys[0], &trans.req); err != nil {
		return trans.req, fmt.Errorf("claimRequest: datastore update failed with %v", err)
	}

	if _, err := trans.tx.Commit(); err != nil {
		return trans.req, fmt.Errorf("claimRequest: datastore commit failed with %v", err)
	}

	return trans.req, nil
}

func permitReuse(req *models.Request) bool {
	// Always deny reuse if configured locally
	if !conf.PermitReuse {
		return false
	}
	// If allowed locally, do what the server wants
	return req.AttemptReuse
}

// processRequest takes a claimed request, performs any necessary
// checks, processes it and always returns a metadata object
// with the results. Errors in this func are considered non-fatal
// and are logged and returned within the metadata for display to
// the client.
func processRequest(req *models.Request) (crypto.Metadata, error) {
	meta := crypto.Metadata{}
	if err := certs.VerifyCert(req.ClientCert, req.Hostname+"."+conf.Domain, conf.CaURL, conf.CaURLPath, conf.CaOrg, conf.RootsPath, conf.VerifyCert); err != nil {
		elog.Warning(211, fmt.Sprintf("Client verification failed: %v", err))
		metrics.Get("failure-211").Increment()
		meta.Data = []byte(err.Error())
		return meta, err
	}

	var err error
	elog.Info(209, fmt.Sprintf("Attempting to join host %s to domain %s. Hostname reuse is set to %t.", req.Hostname, conf.Domain, permitReuse(req)))
	metrics.Get("join-attempt").Increment()
	meta.Data, err = provisioning.BinData(req.Hostname, conf.Domain, permitReuse(req))
	if err != nil {
		elog.Warning(207, fmt.Sprintf("Failed to join host with: %v", err))
		metrics.Get("failure-207").Increment()
		meta.Data = []byte(err.Error())
		return meta, err
	}

	if conf.EncryptBlob {
		pub, err := certs.PublicKey(req.ClientCert)
		if err != nil {
			elog.Warning(212, fmt.Sprintf("Unable to obtain certificate public key: %v", err))
			metrics.Get("failure-212").Increment()
			meta.Data = []byte(err.Error())
			return meta, err
		}

		if err := meta.Encrypt(pub); err != nil {
			elog.Warning(210, fmt.Sprintf("encryptMeta: %v", err))
			metrics.Get("failure-210").Increment()
			meta.Data = []byte(err.Error())
			return meta, err
		}
	}

	elog.Info(209, fmt.Sprintf("Attempting to create or modify %q computer object in domain %q", req.Hostname, conf.Domain))
	return meta, nil
}

// Run the splice daemon continuously, listening for new requests.
func Run(ctx context.Context) ExitEvt {
	client, err := pubsub.NewClient(ctx, conf.ProjectID)
	if err != nil {
		return ExitEvt{204, fmt.Sprintf("Failed to create client. %v", err)}
	}
	for {
		elog.Info(200, "Awaiting join requests...")
		metrics.Get("waiting").Set(1)
		reqID, err := pubsub.NewJoinRequest(ctx, client, conf.Topic)
		metrics.Get("waiting").Set(0)
		if err != nil {
			metrics.Get("failure-205").Increment()
			elog.Error(205, fmt.Sprintf("%v", err))
			time.Sleep(1 * time.Minute)
			continue
		}

		elog.Info(200, fmt.Sprintf("NewJoinRequest: pulled message for processing, %v", reqID))
		req, err := claimRequest(ctx, reqID)
		if err != nil {
			elog.Error(206, fmt.Sprintf("%v", err))
			metrics.Get("failure-206").Increment()
			continue
		}

		success := true
		meta, err := processRequest(&req)
		if err != nil {
			success = false
		}

		if err = returnRequest(ctx, reqID, success, &meta); err != nil {
			elog.Error(208, fmt.Sprintf("%v", err))
			metrics.Get("failure-208").Increment()
		}
		for i := range meta.Data {
			meta.Data[i] = 0
		}
	}
}

func initMetrics() error {
	metrics = tracker.New()

	// Counters
	for _, name := range []string{
		"failure-205",
		"failure-206",
		"failure-207",
		"failure-208",
		"failure-210",
		"failure-211",
		"failure-212",
		"join-attempt",
		"join-fail",
		"join-success",
	} {
		m, err := metric.NewCounter(fmt.Sprintf("%s/%s", metricRoot, name), metricSvc)
		if err != nil {
			return err
		}
		metrics.Add(name, m)
	}

	// Gauges
	for _, name := range []string{
		"waiting",
	} {
		m, err := metric.NewInt(fmt.Sprintf("%s/%s", metricRoot, name), metricSvc)
		if err != nil {
			return err
		}
		metrics.Add(name, m)
	}
	return nil
}

// Init initializes the internal config and logging. Must call before Run.
func Init() error {
	var err error
	if err := initMetrics(); err != nil {
		return err
	}

	conf, err = getConfig()
	if err != nil {
		return fmt.Errorf("Could not obtain configuration from registry. %v", err)
	}
	elog.Info(201, fmt.Sprintf(
		"Application configured from registry.\n\n"+
			"Domain: %v\n"+
			"Svc name: %v\n"+
			"Project id: %v\n"+
			"Topic name: %v\n"+
			"Encrypt blob: %v\n"+
			"Verify certs: %v\n"+
			"CA URL: %v\n"+
			"CA URL Path: %v\n"+
			"CA Expected Org: %v\n"+
			"Permit reuse: %v",
		conf.Domain,
		conf.Instance,
		conf.ProjectID,
		conf.Topic,
		conf.EncryptBlob,
		conf.VerifyCert,
		conf.CaURL,
		conf.CaURLPath,
		conf.CaOrg,
		conf.PermitReuse))

	return nil
}
<commit_msg>Rename SpliceD metrics to replace - with _.<commit_after>// +build windows

/*
Copyright 2016 Google Inc.

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

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

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

/*
Package spliced processes domain join requests from the Cloud Datastore.

The core of the Splice joiner runs as a single goroutine which allows it
to function independently of the Windows service which manages it.
Under normal operation, the goroutine for spliced does not exit, unless the
parent Windows service stops.

A channel is used to enable the goroutine to signal an internal failure to
the Windows service, allowing it to shutdown cleanly. All other logging is
sent directly to EventLog.
*/
package main

import (
	"golang.org/x/net/context"
	"fmt"
	"time"

	metric "github.com/google/cabbie/metrics"
	"cloud.google.com/go/datastore"
	"github.com/google/splice/models"
	"github.com/google/splice/shared/certs"
	"github.com/google/splice/shared/crypto"
	"github.com/google/splice/shared/provisioning"
	"github.com/google/splice/spliced/metric/tracker"
	"github.com/google/splice/spliced/pubsub"
)

var (
	conf    appcfg
	metrics *tracker.Tracker

	// MetricRoot sets metric path for all SpliceD metrics
	metricRoot = "/splice/metrics"
	// MetricSvc sets platform source for metrics.
	metricSvc = "splice"
)

// ExitEvt holds an EventLog event explaining why the goroutine had to exit.
type ExitEvt struct {
	Code    uint32
	Message string
}

// Transaction holds an in-flight datastore transaction.
type Transaction struct {
	client *datastore.Client
	keys   []*datastore.Key
	req    models.Request
	tx     *datastore.Transaction
}

// startTransaction opens a datastore transaction and attempts to locate the record with id reqID.
func startTransaction(ctx context.Context, reqID string) (Transaction, error) {
	var trans Transaction
	var err error

	trans.client, err = datastore.NewClient(ctx, conf.ProjectID)
	if err != nil {
		return trans, fmt.Errorf("startTransaction: datastore client creation failed with %v", err)
	}

	trans.tx, err = trans.client.NewTransaction(ctx)
	if err != nil {
		return trans, fmt.Errorf("startTransaction: opening a datastore transaction failed with %v", err)
	}

	var requests []models.Request
	ancestor := datastore.NameKey("RequestID", reqID, nil)
	query := datastore.NewQuery("Request").Ancestor(ancestor).Transaction(trans.tx)

	if trans.keys, err = trans.client.GetAll(ctx, query, &requests); err != nil {
		return trans, fmt.Errorf("startTransaction: obtaining request from the datastore failed with %v", err)
	}

	if len(requests) < 1 {
		return trans, fmt.Errorf("startTransaction: no request received with ID %s", reqID)
	}
	trans.req = requests[0]

	return trans, nil
}

// returnRequest passes the result of the operation to the datastore on its way to the client.
func returnRequest(ctx context.Context, reqID string, success bool, meta *crypto.Metadata) error {
	trans, err := startTransaction(ctx, reqID)
	if err != nil {
		return err
	}
	defer trans.client.Close()

	trans.req.ResponseData = meta.Data
	if success {
		trans.req.Status = models.RequestStatusCompleted
		trans.req.ResponseKey = meta.AESKey
		trans.req.CipherNonce = meta.Nonce
		metrics.Get("join_success").Increment()
	} else {
		trans.req.Status = models.RequestStatusFailed

		metrics.Get("join_fail").Increment()
	}

	trans.req.CompletionTime = time.Now().UTC()

	if _, err := trans.tx.Put(trans.keys[0], &trans.req); err != nil {
		return fmt.Errorf("returnRequest: datastore update failed with %v", err)
	}

	if _, err := trans.tx.Commit(); err != nil {
		return fmt.Errorf("returnRequest: datastore commit failed with %v", err)
	}

	return nil
}

// claimRequest attempts to claim a new join request from the datastore.
func claimRequest(ctx context.Context, reqID string) (models.Request, error) {
	trans, err := startTransaction(ctx, reqID)
	if err != nil {
		return trans.req, err
	}
	defer trans.client.Close()

	if trans.req.Status != models.RequestStatusAccepted || trans.req.ClaimBy != "" {
		return trans.req, fmt.Errorf("claimRequest: request to %s already %s and will be ignored", trans.req.ClaimBy, trans.req.Status)
	}

	trans.req.ClaimBy = conf.Instance
	trans.req.ClaimTime = time.Now().UTC()

	if _, err := trans.tx.Put(trans.keys[0], &trans.req); err != nil {
		return trans.req, fmt.Errorf("claimRequest: datastore update failed with %v", err)
	}

	if _, err := trans.tx.Commit(); err != nil {
		return trans.req, fmt.Errorf("claimRequest: datastore commit failed with %v", err)
	}

	return trans.req, nil
}

func permitReuse(req *models.Request) bool {
	// Always deny reuse if configured locally
	if !conf.PermitReuse {
		return false
	}
	// If allowed locally, do what the server wants
	return req.AttemptReuse
}

// processRequest takes a claimed request, performs any necessary
// checks, processes it and always returns a metadata object
// with the results. Errors in this func are considered non-fatal
// and are logged and returned within the metadata for display to
// the client.
func processRequest(req *models.Request) (crypto.Metadata, error) {
	meta := crypto.Metadata{}
	if err := certs.VerifyCert(req.ClientCert, req.Hostname+"."+conf.Domain, conf.CaURL, conf.CaURLPath, conf.CaOrg, conf.RootsPath, conf.VerifyCert); err != nil {
		elog.Warning(211, fmt.Sprintf("Client verification failed: %v", err))
		metrics.Get("failure_211").Increment()
		meta.Data = []byte(err.Error())
		return meta, err
	}

	var err error
	elog.Info(209, fmt.Sprintf("Attempting to join host %s to domain %s. Hostname reuse is set to %t.", req.Hostname, conf.Domain, permitReuse(req)))
	metrics.Get("join_attempt").Increment()
	meta.Data, err = provisioning.BinData(req.Hostname, conf.Domain, permitReuse(req))
	if err != nil {
		elog.Warning(207, fmt.Sprintf("Failed to join host with: %v", err))
		metrics.Get("failure_207").Increment()
		meta.Data = []byte(err.Error())
		return meta, err
	}

	if conf.EncryptBlob {
		pub, err := certs.PublicKey(req.ClientCert)
		if err != nil {
			elog.Warning(212, fmt.Sprintf("Unable to obtain certificate public key: %v", err))
			metrics.Get("failure_212").Increment()
			meta.Data = []byte(err.Error())
			return meta, err
		}

		if err := meta.Encrypt(pub); err != nil {
			elog.Warning(210, fmt.Sprintf("encryptMeta: %v", err))
			metrics.Get("failure_210").Increment()
			meta.Data = []byte(err.Error())
			return meta, err
		}
	}

	elog.Info(209, fmt.Sprintf("Attempting to create or modify %q computer object in domain %q", req.Hostname, conf.Domain))
	return meta, nil
}

// Run the splice daemon continuously, listening for new requests.
func Run(ctx context.Context) ExitEvt {
	client, err := pubsub.NewClient(ctx, conf.ProjectID)
	if err != nil {
		return ExitEvt{204, fmt.Sprintf("Failed to create client. %v", err)}
	}
	for {
		elog.Info(200, "Awaiting join requests...")
		metrics.Get("waiting").Set(1)
		reqID, err := pubsub.NewJoinRequest(ctx, client, conf.Topic)
		metrics.Get("waiting").Set(0)
		if err != nil {
			metrics.Get("failure_205").Increment()
			elog.Error(205, fmt.Sprintf("%v", err))
			time.Sleep(1 * time.Minute)
			continue
		}

		elog.Info(200, fmt.Sprintf("NewJoinRequest: pulled message for processing, %v", reqID))
		req, err := claimRequest(ctx, reqID)
		if err != nil {
			elog.Error(206, fmt.Sprintf("%v", err))
			metrics.Get("failure_206").Increment()
			continue
		}

		success := true
		meta, err := processRequest(&req)
		if err != nil {
			success = false
		}

		if err = returnRequest(ctx, reqID, success, &meta); err != nil {
			elog.Error(208, fmt.Sprintf("%v", err))
			metrics.Get("failure_208").Increment()
		}
		for i := range meta.Data {
			meta.Data[i] = 0
		}
	}
}

func initMetrics() error {
	metrics = tracker.New()

	// Counters
	for _, name := range []string{
		"failure_205",
		"failure_206",
		"failure_207",
		"failure_208",
		"failure_210",
		"failure_211",
		"failure_212",
		"join_attempt",
		"join_fail",
		"join_success",
	} {
		m, err := metric.NewCounter(fmt.Sprintf("%s/%s", metricRoot, name), metricSvc)
		if err != nil {
			return err
		}
		metrics.Add(name, m)
	}

	// Gauges
	for _, name := range []string{
		"waiting",
	} {
		m, err := metric.NewInt(fmt.Sprintf("%s/%s", metricRoot, name), metricSvc)
		if err != nil {
			return err
		}
		metrics.Add(name, m)
	}
	return nil
}

// Init initializes the internal config and logging. Must call before Run.
func Init() error {
	var err error
	if err := initMetrics(); err != nil {
		return err
	}

	conf, err = getConfig()
	if err != nil {
		return fmt.Errorf("Could not obtain configuration from registry. %v", err)
	}
	elog.Info(201, fmt.Sprintf(
		"Application configured from registry.\n\n"+
			"Domain: %v\n"+
			"Svc name: %v\n"+
			"Project id: %v\n"+
			"Topic name: %v\n"+
			"Encrypt blob: %v\n"+
			"Verify certs: %v\n"+
			"CA URL: %v\n"+
			"CA URL Path: %v\n"+
			"CA Expected Org: %v\n"+
			"Permit reuse: %v",
		conf.Domain,
		conf.Instance,
		conf.ProjectID,
		conf.Topic,
		conf.EncryptBlob,
		conf.VerifyCert,
		conf.CaURL,
		conf.CaURLPath,
		conf.CaOrg,
		conf.PermitReuse))

	return nil
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"image/color"
	"image/color/palette"
	"strings"
	"sync"

	"golang.org/x/image/colornames"
)

var (
	// NoColor defines if the output is colorized or not. It's dynamically set to
	// false or true based on the stdout's file descriptor referring to a terminal
	// or not. This is a global option and affects all colors. For more control
	// over each color block use the methods DisableColor() individually.
	// NoColor = os.Getenv("TERM") == "dumb" ||
	// 	(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) //&& Glog.Printer.IsTerminal

	// colorsCache is used to reduce the count of created Color objects and
	// allows to reuse already created objects with required Attribute.
	crgbCache   = make(map[ColorAttribute]*ColorRGB)
	crgbCacheMu sync.Mutex // protects colorsCache

	// ColorsSVG is Colors map. Colors["SVGnames"]=color.RGBA
	ColorsSVG = colornames.Map

	plte color.Palette = palette.Plan9
)

const (
	fgRGB    = "38;2;"
	bgRGB    = "48;2;"
	colorsep = "|"
)

// ColorAttribute define a key for a color
type ColorAttribute struct {
	RGB          color.RGBA
	IsForeground bool
}

// ColorRGB is a alias of "color.Color"
type ColorRGB struct {
	// color.Color
	params  []ColorAttribute
	noColor *bool
}

//---------------------------------------------------------

// NewRGB returns a newly created color object.
func NewRGB(value ...ColorAttribute) *ColorRGB {
	c := &ColorRGB{params: make([]ColorAttribute, 0)}
	c.Add(value...)
	return c
}

// Add is used to chain SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
func (c *ColorRGB) Add(value ...ColorAttribute) *ColorRGB {
	c.params = append(c.params, value...)
	return c
}

func (c *ColorRGB) prepend(value ColorAttribute) {
	c.params = append(c.params, ColorAttribute{})
	copy(c.params[1:], c.params[0:])
	c.params[0] = value
}

//---------------------------------------------------------

// Sprint is just like Print, but returns a string instead of printing it.
func (c *ColorRGB) Sprint(a ...interface{}) string {
	return c.wrap(fmt.Sprint(a...))
}

// Sprintln is just like Println, but returns a string instead of printing it.
func (c *ColorRGB) Sprintln(a ...interface{}) string {
	return c.wrap(fmt.Sprintln(a...))
}

// Sprintf is just like Printf, but returns a string instead of printing it.
func (c *ColorRGB) Sprintf(format string, a ...interface{}) string {
	return c.wrap(fmt.Sprintf(format, a...))
}

// SprintFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprint(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output, example:
//
//	put := New(FgYellow).SprintFunc()
//	fmt.Fprintf(color.Output, "This is a %s", put("warning"))
func (c *ColorRGB) SprintFunc() func(a ...interface{}) string {
	return func(a ...interface{}) string {
		return c.wrap(fmt.Sprint(a...))
	}
}

// SprintfFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *ColorRGB) SprintfFunc() func(format string, a ...interface{}) string {
	return func(format string, a ...interface{}) string {
		return c.wrap(fmt.Sprintf(format, a...))
	}
}

// SprintlnFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *ColorRGB) SprintlnFunc() func(a ...interface{}) string {
	return func(a ...interface{}) string {
		return c.wrap(fmt.Sprintln(a...))
	}
}

// wrap wraps the s string with the colors Attributes. The string is ready to
// be printed.
func (c *ColorRGB) wrap(s string) string {
	if c.isNoColorSet() {
		return s
	}

	return c.format() + s + c.unformat()
}

func getRGBCodeString(c color.RGBA) string {
	r, g, b, _ := c.RGBA()
	return fmt.Sprintf("%v;%v;%vm", r, g, b)
}

// sequence returns a formated SGR sequence to be plugged into a
// ESC[38;2;<r>;<g>;<b>m... Select foreground color
// ESC[48;2;<r>;<g>;<b>m... Select background color
func (c *ColorRGB) sequence() string {
	var lcfmt string
	format := make([]string, len(c.params))
	for i, val := range c.params {
		if val.IsForeground {
			lcfmt = fgRGB
		} else {
			lcfmt = bgRGB
		}
		r, g, b, _ := val.RGB.RGBA()
		// format[i] = fmt.Sprintf("%s[%s%v;%v;%vm", escape, lcfmt, r, g, b)
		format[i] = fmt.Sprintf("\x1b[%s%v;%v;%vm", lcfmt, r, g, b)
	}

	return strings.Join(format, "")
}

func (c *ColorRGB) format() string {
	// return fmt.Sprintf("%s[%sm", escape, c.sequence())
	return c.sequence()
}

func (c *ColorRGB) unformat() string {
	// return fmt.Sprintf("%s[%dm", escape, Reset)
	// return fmt.Sprintf("%s[%dm", escape, 0)
	var unf string
	for i := 0; i < len(c.params); i++ {
		unf += reset // "\x1b[0m"
	}
	return unf
}

// DisableColor disables the color output. Useful to not change any existing
// code and still being able to output. Can be used for flags like
// "--no-color". To enable back use EnableColor() method.
func (c *ColorRGB) DisableColor() {
	c.noColor = boolPtr(true)
}

// EnableColor enables the color output. Use it in conjunction with
// DisableColor(). Otherwise this method has no side effects.
func (c *ColorRGB) EnableColor() {
	c.noColor = boolPtr(false)
}

func (c *ColorRGB) isNoColorSet() bool {
	// check first if we have user setted action
	if c.noColor != nil {
		return *c.noColor
	}

	// if not return the global option, which is disabled by default
	return NoColor
}

func getCachedColorRGB(k ColorAttribute) *ColorRGB {
	crgbCacheMu.Lock()
	defer crgbCacheMu.Unlock()

	c, ok := crgbCache[k]
	if !ok {
		c = NewRGB(k)
		crgbCache[k] = c
	}

	return c
}

// ColorRGBString returns a formatted colorful string with specified "colorname"
func ColorRGBString(format string, rgb color.RGBA, a ...interface{}) string {
	c := getCachedColorRGB(ColorAttribute{RGB: rgb, IsForeground: true})

	if len(a) == 0 {
		return c.SprintFunc()(format)
	}

	return c.SprintfFunc()(format, a...)
}

//---------------------------------------------------------

//---------------------------------------------------------
<commit_msg>correct comment and a little bits of codes of sequence()<commit_after>package util

import (
	"fmt"
	"image/color"
	"image/color/palette"
	"strings"
	"sync"

	"golang.org/x/image/colornames"
)

var (
	// NoColor defines if the output is colorized or not. It's dynamically set to
	// false or true based on the stdout's file descriptor referring to a terminal
	// or not. This is a global option and affects all colors. For more control
	// over each color block use the methods DisableColor() individually.
	// NoColor = os.Getenv("TERM") == "dumb" ||
	// 	(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) //&& Glog.Printer.IsTerminal

	// colorsCache is used to reduce the count of created Color objects and
	// allows to reuse already created objects with required Attribute.
	crgbCache   = make(map[ColorAttribute]*ColorRGB)
	crgbCacheMu sync.Mutex // protects colorsCache

	// ColorsSVG is Colors map. Colors["SVGnames"]=color.RGBA
	ColorsSVG = colornames.Map

	plte color.Palette = palette.Plan9
)

const (
	fgRGB    = "38;2;"
	bgRGB    = "48;2;"
	colorsep = "|"
)

// ColorAttribute define a key for a color
type ColorAttribute struct {
	RGB          color.RGBA
	IsForeground bool
}

// ColorRGB is a alias of "color.Color"
type ColorRGB struct {
	// color.Color
	params  []ColorAttribute
	noColor *bool
}

//---------------------------------------------------------

// NewRGB returns a newly created color object.
func NewRGB(value ...ColorAttribute) *ColorRGB {
	c := &ColorRGB{params: make([]ColorAttribute, 0)}
	c.Add(value...)
	return c
}

// Add is used to chain SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
func (c *ColorRGB) Add(value ...ColorAttribute) *ColorRGB {
	c.params = append(c.params, value...)
	return c
}

func (c *ColorRGB) prepend(value ColorAttribute) {
	c.params = append(c.params, ColorAttribute{})
	copy(c.params[1:], c.params[0:])
	c.params[0] = value
}

//---------------------------------------------------------

// Sprint is just like Print, but returns a string instead of printing it.
func (c *ColorRGB) Sprint(a ...interface{}) string {
	return c.wrap(fmt.Sprint(a...))
}

// Sprintln is just like Println, but returns a string instead of printing it.
func (c *ColorRGB) Sprintln(a ...interface{}) string {
	return c.wrap(fmt.Sprintln(a...))
}

// Sprintf is just like Printf, but returns a string instead of printing it.
func (c *ColorRGB) Sprintf(format string, a ...interface{}) string {
	return c.wrap(fmt.Sprintf(format, a...))
}

// SprintFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprint(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output, example:
//
//	put := New(FgYellow).SprintFunc()
//	fmt.Fprintf(color.Output, "This is a %s", put("warning"))
func (c *ColorRGB) SprintFunc() func(a ...interface{}) string {
	return func(a ...interface{}) string {
		return c.wrap(fmt.Sprint(a...))
	}
}

// SprintfFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *ColorRGB) SprintfFunc() func(format string, a ...interface{}) string {
	return func(format string, a ...interface{}) string {
		return c.wrap(fmt.Sprintf(format, a...))
	}
}

// SprintlnFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *ColorRGB) SprintlnFunc() func(a ...interface{}) string {
	return func(a ...interface{}) string {
		return c.wrap(fmt.Sprintln(a...))
	}
}

// wrap wraps the s string with the colors Attributes. The string is ready to
// be printed.
func (c *ColorRGB) wrap(s string) string {
	if c.isNoColorSet() {
		return s
	}

	return c.format() + s + c.unformat()
}

func getRGBCodeString(c color.RGBA) string {
	r, g, b, _ := c.RGBA()
	return fmt.Sprintf("%v;%v;%vm", r, g, b)
}

// sequence returns a formated SGR sequence to be plugged into a
// ESC[38;2;<r>;<g>;<b>m... Select foreground color
// ESC[48;2;<r>;<g>;<b>m... Select background color
func (c *ColorRGB) sequence() string {
	var lcfmt string
	format := make([]string, len(c.params))
	for i, val := range c.params {
		if val.IsForeground {
			lcfmt = fgRGB
		} else {
			lcfmt = bgRGB
		}
		r, g, b, _ := val.RGB.RGBA()
		// format[i] = fmt.Sprintf("%s[%s%v;%v;%vm", escape, lcfmt, r, g, b)
		format[i] = fmt.Sprintf("%s%v;%v;%vm", lcfmt, r, g, b)
	}

	return strings.Join(format, "")
}

func (c *ColorRGB) format() string {
	return fmt.Sprintf("%s[%sm", escape, c.sequence())
}

func (c *ColorRGB) unformat() string {
	// return fmt.Sprintf("%s[%dm", escape, Reset)
	// return fmt.Sprintf("%s[%dm", escape, 0)
	var unf string
	for i := 0; i < len(c.params); i++ {
		unf += reset // "\x1b[0m"
	}
	return unf
}

// DisableColor disables the color output. Useful to not change any existing
// code and still being able to output. Can be used for flags like
// "--no-color". To enable back use EnableColor() method.
func (c *ColorRGB) DisableColor() {
	c.noColor = boolPtr(true)
}

// EnableColor enables the color output. Use it in conjunction with
// DisableColor(). Otherwise this method has no side effects.
func (c *ColorRGB) EnableColor() {
	c.noColor = boolPtr(false)
}

func (c *ColorRGB) isNoColorSet() bool {
	// check first if we have user setted action
	if c.noColor != nil {
		return *c.noColor
	}

	// if not return the global option, which is disabled by default
	return NoColor
}

func getCachedColorRGB(k ColorAttribute) *ColorRGB {
	crgbCacheMu.Lock()
	defer crgbCacheMu.Unlock()

	c, ok := crgbCache[k]
	if !ok {
		c = NewRGB(k)
		crgbCache[k] = c
	}

	return c
}

// ColorRGBString returns a formatted colorful string with specified "colorname"
func ColorRGBString(format string, rgb color.RGBA, a ...interface{}) string {
	c := getCachedColorRGB(ColorAttribute{RGB: rgb, IsForeground: true})

	if len(a) == 0 {
		return c.SprintFunc()(format)
	}

	return c.SprintfFunc()(format, a...)
}

//---------------------------------------------------------

//---------------------------------------------------------
<|endoftext|>


<commit_before>package utils

import "strconv"

func Atoi32(s string) (int32, error) {
	res, err := strconv.Atoi(s)
	if err != nil {
		return 0, nil
	}
	// TODO: check size ?
	return int32(res), nil
}
<commit_msg>sanity check<commit_after>package utils

import (
	"fmt"
	"math"
	"strconv"
)

func Atoi32(s string) (int32, error) {
	res, err := strconv.Atoi(s)
	if err != nil {
		return 0, nil
	}
	if res > math.MaxInt32 {
		return 0, fmt.Errorf("int32 overflow")
	}
	return int32(res), nil
}
<|endoftext|>


<commit_before>package utils

import (
	"errors"
	"io"

	"golang.org/x/crypto/blake2b"
)

// HMAC will read the given reader and return the MAC for the
// given key
func HMAC(key []byte, data io.Reader) ([]byte, error) {
	// We must hash long keys but short keys are padded
	// by blake2b.
	if len(key) > 64 {
		newKey := blake2b.Sum512(key)
		copy(key, newKey[:])
	}
	hasher, err := blake2b.New512(key)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(hasher, data)
	if err != nil {
		return nil, err
	}
	return hasher.Sum(nil), nil
}

// VerifyHMAC will take an HMAC and a to-be-verified
// data stream plus key. It will generate the HMAC
// for that datastream and then perform a constant-time
// comparison of the two HMACs.
func VerifyHMAC(hmac, key []byte, data io.Reader) (err error) {
	var verifyHMAC = make([]byte, len(hmac))
	verifyHMAC, err = HMAC(key, data)

	lenhmac := len(hmac)
	lenvmac := len(verifyHMAC)
	if lenhmac != lenvmac {
		// if macs are differing in length, verify macs
		// against itself to avoid timing attacks.
		if lenvmac > lenhmac {
			verifyHMAC = verifyHMAC[:lenhmac]
		} else {
			verifyHMAC = append(verifyHMAC,
				make([]byte, lenhmac-lenvmac)...)
		}
	}

	// do a constant-time compare
	var result byte
	for k := range hmac {
		result |= hmac[k] ^ verifyHMAC[k]
	}
	// if any differences are found, return error
	if result != 0 || lenhmac != lenvmac || err != nil {
		return errors.New("HMAC failed")
	}

	return nil
}
<commit_msg>Removed early return on IF<commit_after>package utils

import (
	"errors"
	"io"

	"golang.org/x/crypto/blake2b"
)

// HMAC will read the given reader and return the MAC for the
// given key
func HMAC(key []byte, data io.Reader) ([]byte, error) {
	// We must hash long keys but short keys are padded
	// by blake2b.
	if len(key) > 64 {
		newKey := blake2b.Sum512(key)
		copy(key, newKey[:])
	}
	hasher, err := blake2b.New512(key)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(hasher, data)
	if err != nil {
		return nil, err
	}
	return hasher.Sum(nil), nil
}

// VerifyHMAC will take an HMAC and a to-be-verified
// data stream plus key. It will generate the HMAC
// for that datastream and then perform a constant-time
// comparison of the two HMACs.
func VerifyHMAC(hmac, key []byte, data io.Reader) (err error) {
	var verifyHMAC = make([]byte, len(hmac))
	verifyHMAC, err = HMAC(key, data)

	lenhmac := len(hmac)
	lenvmac := len(verifyHMAC)
	if lenhmac != lenvmac {
		// if macs are differing in length, verify macs
		// against itself to avoid timing attacks.
		if lenvmac > lenhmac {
			verifyHMAC = verifyHMAC[:lenhmac]
		} else {
			verifyHMAC = append(verifyHMAC,
				make([]byte, lenhmac-lenvmac)...)
		}
	}

	// do a constant-time compare
	var result byte
	for k := range hmac {
		result |= hmac[k] ^ verifyHMAC[k]
	}
	// if any differences are found, return error
	var hmacFailed bool
	if result != 0 {
		hmacFailed = true
	}
	if lenhmac != lenvmac {
		hmacFailed = true
	}
	if err != nil {
		hmacFailed = true
	}

	if hmacFailed {
		return errors.New("HMAC failed")
	}

	return nil
}
<|endoftext|>


<commit_before>package longpoll

import (
    "testing"
	"time"
)

func Test_millisecondStringToTime(t *testing.T) {
    inputs := []string {
        "0",
        "1429972200000",
        "1446508745000",
    }
    type tePair struct {
        Time time.Time
        Error error
    }
    expected_outputs := []tePair{
        {time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), nil},
        {time.Date(2015, time.April, 25, 14, 30, 0, 0, time.UTC), nil},
        {time.Date(2015, time.November, 2, 23, 59, 5, 0, time.UTC), nil},
    }
    for index, input := range inputs {
        actualTime, actualError := millisecondStringToTime(input)
        if actualTime != expected_outputs[index].Time || actualError != expected_outputs[index].Error {
            t.Errorf("Expected (%q, %q), got (%q, %q).",
                expected_outputs[index].Time, expected_outputs[index].Error, actualTime, actualError)
        }
    }
}

func Test_millisecondStringToTime_InvalidInput(t *testing.T) {
    inputs := []string {
        "",
        "0a",
        "a0",
        "-adsfjkl",
        "  ",
        "\t\b",
    }
    type tsPair struct {
        Time time.Time
        ErrorString string
    }
    expected_outputs := []tsPair{
        {time.Time{}, "strconv.ParseInt: parsing \"\": invalid syntax"},
        {time.Time{}, "strconv.ParseInt: parsing \"0a\": invalid syntax"},
        {time.Time{}, "strconv.ParseInt: parsing \"a0\": invalid syntax"},
        {time.Time{}, "strconv.ParseInt: parsing \"-adsfjkl\": invalid syntax"},
        {time.Time{}, "strconv.ParseInt: parsing \"  \": invalid syntax"},
        {time.Time{}, "strconv.ParseInt: parsing \"\\t\\b\": invalid syntax"},
    }
    for index, input := range inputs {
        actualTime, actualError := millisecondStringToTime(input)
        if actualTime != expected_outputs[index].Time || actualError.Error() != expected_outputs[index].ErrorString {
            t.Errorf("Expected (%q, %q), got (%q, %q).",
                expected_outputs[index].Time, expected_outputs[index].ErrorString, actualTime, actualError.Error())
        }
    }
}

func Test_timeToEpochMilliseconds(t *testing.T) {
    inputs := []time.Time{
        time.Date(1969, time.December, 31, 23, 59, 59, 0, time.UTC),
        time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC),
        time.Date(2015, time.April, 25, 14, 30, 0, 0, time.UTC),
        time.Date(2015, time.November, 2, 23, 59, 5, 0, time.UTC),
    }
    expected_outputs := []int64 {
        -1000,
        0,
        1429972200000,
        1446508745000,
    }
    for index, input := range inputs {
        actual := timeToEpochMilliseconds(input)
        if actual != expected_outputs[index] {
            t.Errorf("Expected %d, got %d.", expected_outputs[index], actual)
        }
    }
}
<commit_msg>go fmt fixes<commit_after>package longpoll

import (
	"testing"
	"time"
)

func Test_millisecondStringToTime(t *testing.T) {
	inputs := []string{
		"0",
		"1429972200000",
		"1446508745000",
	}
	type tePair struct {
		Time  time.Time
		Error error
	}
	expected_outputs := []tePair{
		{time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), nil},
		{time.Date(2015, time.April, 25, 14, 30, 0, 0, time.UTC), nil},
		{time.Date(2015, time.November, 2, 23, 59, 5, 0, time.UTC), nil},
	}
	for index, input := range inputs {
		actualTime, actualError := millisecondStringToTime(input)
		if actualTime != expected_outputs[index].Time || actualError != expected_outputs[index].Error {
			t.Errorf("Expected (%q, %q), got (%q, %q).",
				expected_outputs[index].Time, expected_outputs[index].Error, actualTime, actualError)
		}
	}
}

func Test_millisecondStringToTime_InvalidInput(t *testing.T) {
	inputs := []string{
		"",
		"0a",
		"a0",
		"-adsfjkl",
		"  ",
		"\t\b",
	}
	type tsPair struct {
		Time        time.Time
		ErrorString string
	}
	expected_outputs := []tsPair{
		{time.Time{}, "strconv.ParseInt: parsing \"\": invalid syntax"},
		{time.Time{}, "strconv.ParseInt: parsing \"0a\": invalid syntax"},
		{time.Time{}, "strconv.ParseInt: parsing \"a0\": invalid syntax"},
		{time.Time{}, "strconv.ParseInt: parsing \"-adsfjkl\": invalid syntax"},
		{time.Time{}, "strconv.ParseInt: parsing \"  \": invalid syntax"},
		{time.Time{}, "strconv.ParseInt: parsing \"\\t\\b\": invalid syntax"},
	}
	for index, input := range inputs {
		actualTime, actualError := millisecondStringToTime(input)
		if actualTime != expected_outputs[index].Time || actualError.Error() != expected_outputs[index].ErrorString {
			t.Errorf("Expected (%q, %q), got (%q, %q).",
				expected_outputs[index].Time, expected_outputs[index].ErrorString, actualTime, actualError.Error())
		}
	}
}

func Test_timeToEpochMilliseconds(t *testing.T) {
	inputs := []time.Time{
		time.Date(1969, time.December, 31, 23, 59, 59, 0, time.UTC),
		time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC),
		time.Date(2015, time.April, 25, 14, 30, 0, 0, time.UTC),
		time.Date(2015, time.November, 2, 23, 59, 5, 0, time.UTC),
	}
	expected_outputs := []int64{
		-1000,
		0,
		1429972200000,
		1446508745000,
	}
	for index, input := range inputs {
		actual := timeToEpochMilliseconds(input)
		if actual != expected_outputs[index] {
			t.Errorf("Expected %d, got %d.", expected_outputs[index], actual)
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"testing"

	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/consul/testutil/retry"
	"github.com/pascaldekloe/goe/verify"
)

func TestAPI_CatalogDatacenters(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		datacenters, err := catalog.Datacenters()
		if err != nil {
			r.Fatal(err)
		}
		if len(datacenters) < 1 {
			r.Fatal("got 0 datacenters want at least one")
		}
	})
}

func TestAPI_CatalogNodes(t *testing.T) {
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.RunWith(retry.ThreeTimes(), t, func(r *retry.R) {
		nodes, meta, err := catalog.Nodes(nil)
		if err != nil {
			r.Fatal(err)
		}
		if meta.LastIndex == 0 {
			r.Fatal("got last index 0 want > 0")
		}
		want := []*Node{
			{
				ID:         s.Config.NodeID,
				Node:       s.Config.NodeName,
				Address:    "127.0.0.1",
				Datacenter: "dc1",
				TaggedAddresses: map[string]string{
					"lan": "127.0.0.1",
					"wan": "127.0.0.1",
				},
				Meta:        map[string]string{},
				CreateIndex: meta.LastIndex,
				ModifyIndex: meta.LastIndex,
			},
		}
		if !verify.Values(r, "", nodes, want) {
			r.FailNow()
		}
	})
}

func TestAPI_CatalogNodes_MetaFilter(t *testing.T) {
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	// Make sure we get the node back when filtering by its metadata
	retry.Run(t, func(r *retry.R) {
		nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(nodes) == 0 {
			r.Fatalf("Bad: %v", nodes)
		}

		if _, ok := nodes[0].TaggedAddresses["wan"]; !ok {
			r.Fatalf("Bad: %v", nodes[0])
		}

		if v, ok := nodes[0].Meta["somekey"]; !ok || v != "somevalue" {
			r.Fatalf("Bad: %v", nodes[0].Meta)
		}

		if nodes[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", nodes[0])
		}
	})

	retry.Run(t, func(r *retry.R) {
		// Get nothing back when we use an invalid filter
		nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(nodes) != 0 {
			r.Fatalf("Bad: %v", nodes)
		}
	})
}

func TestAPI_CatalogServices(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Services(nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}
	})
}

func TestAPI_CatalogServices_NodeMetaFilter(t *testing.T) {
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	// Make sure we get the service back when filtering by the node's metadata
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Services(&QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}
	})

	retry.Run(t, func(r *retry.R) {
		// Get nothing back when using an invalid filter
		services, meta, err := catalog.Services(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) != 0 {
			r.Fatalf("Bad: %v", services)
		}
	})
}

func TestAPI_CatalogService(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Service("consul", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}

		if services[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", services[0])
		}
	})
}

func TestAPI_CatalogService_NodeMetaFilter(t *testing.T) {
	t.Parallel()
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Service("consul", "", &QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}

		if services[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", services[0])
		}
	})
}

func TestAPI_CatalogNode(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	name, _ := c.Agent().NodeName()
	retry.Run(t, func(r *retry.R) {
		info, meta, err := catalog.Node(name, nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(info.Services) == 0 {
			r.Fatalf("Bad: %v", info)
		}

		if _, ok := info.Node.TaggedAddresses["wan"]; !ok {
			r.Fatalf("Bad: %v", info)
		}

		if info.Node.Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", info)
		}
	})
}

func TestAPI_CatalogRegistration(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()

	service := &AgentService{
		ID:      "redis1",
		Service: "redis",
		Tags:    []string{"master", "v1"},
		Port:    8000,
	}

	check := &AgentCheck{
		Node:      "foobar",
		CheckID:   "service:redis1",
		Name:      "Redis health check",
		Notes:     "Script based health check",
		Status:    HealthPassing,
		ServiceID: "redis1",
	}

	reg := &CatalogRegistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		NodeMeta:   map[string]string{"somekey": "somevalue"},
		Service:    service,
		Check:      check,
	}
	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}

		health, _, err := c.Health().Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if health[0].CheckID != "service:redis1" {
			r.Fatal("missing checkid service:redis1")
		}

		if v, ok := node.Node.Meta["somekey"]; !ok || v != "somevalue" {
			r.Fatal("missing node meta pair somekey:somevalue")
		}
	})

	// Test catalog deregistration of the previously registered service
	dereg := &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		ServiceID:  "redis1",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; ok {
			r.Fatal("ServiceID:redis1 is not deregistered")
		}
	})

	// Test deregistration of the previously registered check
	dereg = &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		CheckID:    "service:redis1",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		health, _, err := c.Health().Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(health) != 0 {
			r.Fatal("CheckID:service:redis1 is not deregistered")
		}
	})

	// Test node deregistration of the previously registered node
	dereg = &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if node != nil {
			r.Fatalf("node is not deregistered: %v", node)
		}
	})
}

func TestAPI_CatalogEnableTagOverride(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()

	service := &AgentService{
		ID:      "redis1",
		Service: "redis",
		Tags:    []string{"master", "v1"},
		Port:    8000,
	}

	reg := &CatalogRegistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		Service:    service,
	}

	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}
		if node.Services["redis1"].EnableTagOverride != false {
			r.Fatal("tag override set")
		}

		services, _, err := catalog.Service("redis", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(services) < 1 || services[0].ServiceName != "redis" {
			r.Fatal("missing service: redis")
		}
		if services[0].ServiceEnableTagOverride != false {
			r.Fatal("tag override set")
		}
	})

	service.EnableTagOverride = true

	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}
		if node.Services["redis1"].EnableTagOverride != true {
			r.Fatal("tag override not set")
		}

		services, _, err := catalog.Service("redis", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(services) < 1 || services[0].ServiceName != "redis" {
			r.Fatal("missing service: redis")
		}
		if services[0].ServiceEnableTagOverride != true {
			r.Fatal("tag override not set")
		}
	})
}
<commit_msg>api: fix TestAPI_CatalogNodes test<commit_after>package api

import (
	"testing"

	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/consul/testutil/retry"
	"github.com/pascaldekloe/goe/verify"
)

func TestAPI_CatalogDatacenters(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		datacenters, err := catalog.Datacenters()
		if err != nil {
			r.Fatal(err)
		}
		if len(datacenters) < 1 {
			r.Fatal("got 0 datacenters want at least one")
		}
	})
}

func TestAPI_CatalogNodes(t *testing.T) {
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.RunWith(retry.ThreeTimes(), t, func(r *retry.R) {
		nodes, meta, err := catalog.Nodes(nil)
		if err != nil {
			r.Fatal(err)
		}
		if meta.LastIndex == 0 {
			r.Fatal("got last index 0 want > 0")
		}
		want := []*Node{
			{
				ID:         s.Config.NodeID,
				Node:       s.Config.NodeName,
				Address:    "127.0.0.1",
				Datacenter: "dc1",
				TaggedAddresses: map[string]string{
					"lan": "127.0.0.1",
					"wan": "127.0.0.1",
				},
				Meta:        map[string]string{},
				CreateIndex: meta.LastIndex - 1,
				ModifyIndex: meta.LastIndex,
			},
		}
		if !verify.Values(r, "", nodes, want) {
			r.FailNow()
		}
	})
}

func TestAPI_CatalogNodes_MetaFilter(t *testing.T) {
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	// Make sure we get the node back when filtering by its metadata
	retry.Run(t, func(r *retry.R) {
		nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(nodes) == 0 {
			r.Fatalf("Bad: %v", nodes)
		}

		if _, ok := nodes[0].TaggedAddresses["wan"]; !ok {
			r.Fatalf("Bad: %v", nodes[0])
		}

		if v, ok := nodes[0].Meta["somekey"]; !ok || v != "somevalue" {
			r.Fatalf("Bad: %v", nodes[0].Meta)
		}

		if nodes[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", nodes[0])
		}
	})

	retry.Run(t, func(r *retry.R) {
		// Get nothing back when we use an invalid filter
		nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(nodes) != 0 {
			r.Fatalf("Bad: %v", nodes)
		}
	})
}

func TestAPI_CatalogServices(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Services(nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}
	})
}

func TestAPI_CatalogServices_NodeMetaFilter(t *testing.T) {
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	// Make sure we get the service back when filtering by the node's metadata
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Services(&QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}
	})

	retry.Run(t, func(r *retry.R) {
		// Get nothing back when using an invalid filter
		services, meta, err := catalog.Services(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) != 0 {
			r.Fatalf("Bad: %v", services)
		}
	})
}

func TestAPI_CatalogService(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Service("consul", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}

		if services[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", services[0])
		}
	})
}

func TestAPI_CatalogService_NodeMetaFilter(t *testing.T) {
	t.Parallel()
	meta := map[string]string{"somekey": "somevalue"}
	c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
		conf.NodeMeta = meta
	})
	defer s.Stop()

	catalog := c.Catalog()
	retry.Run(t, func(r *retry.R) {
		services, meta, err := catalog.Service("consul", "", &QueryOptions{NodeMeta: meta})
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(services) == 0 {
			r.Fatalf("Bad: %v", services)
		}

		if services[0].Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", services[0])
		}
	})
}

func TestAPI_CatalogNode(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()
	name, _ := c.Agent().NodeName()
	retry.Run(t, func(r *retry.R) {
		info, meta, err := catalog.Node(name, nil)
		if err != nil {
			r.Fatal(err)
		}

		if meta.LastIndex == 0 {
			r.Fatalf("Bad: %v", meta)
		}

		if len(info.Services) == 0 {
			r.Fatalf("Bad: %v", info)
		}

		if _, ok := info.Node.TaggedAddresses["wan"]; !ok {
			r.Fatalf("Bad: %v", info)
		}

		if info.Node.Datacenter != "dc1" {
			r.Fatalf("Bad datacenter: %v", info)
		}
	})
}

func TestAPI_CatalogRegistration(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()

	service := &AgentService{
		ID:      "redis1",
		Service: "redis",
		Tags:    []string{"master", "v1"},
		Port:    8000,
	}

	check := &AgentCheck{
		Node:      "foobar",
		CheckID:   "service:redis1",
		Name:      "Redis health check",
		Notes:     "Script based health check",
		Status:    HealthPassing,
		ServiceID: "redis1",
	}

	reg := &CatalogRegistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		NodeMeta:   map[string]string{"somekey": "somevalue"},
		Service:    service,
		Check:      check,
	}
	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}

		health, _, err := c.Health().Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if health[0].CheckID != "service:redis1" {
			r.Fatal("missing checkid service:redis1")
		}

		if v, ok := node.Node.Meta["somekey"]; !ok || v != "somevalue" {
			r.Fatal("missing node meta pair somekey:somevalue")
		}
	})

	// Test catalog deregistration of the previously registered service
	dereg := &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		ServiceID:  "redis1",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; ok {
			r.Fatal("ServiceID:redis1 is not deregistered")
		}
	})

	// Test deregistration of the previously registered check
	dereg = &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		CheckID:    "service:redis1",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		health, _, err := c.Health().Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(health) != 0 {
			r.Fatal("CheckID:service:redis1 is not deregistered")
		}
	})

	// Test node deregistration of the previously registered node
	dereg = &CatalogDeregistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
	}

	if _, err := catalog.Deregister(dereg, nil); err != nil {
		t.Fatalf("err: %v", err)
	}

	retry.Run(t, func(r *retry.R) {
		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if node != nil {
			r.Fatalf("node is not deregistered: %v", node)
		}
	})
}

func TestAPI_CatalogEnableTagOverride(t *testing.T) {
	t.Parallel()
	c, s := makeClient(t)
	defer s.Stop()

	catalog := c.Catalog()

	service := &AgentService{
		ID:      "redis1",
		Service: "redis",
		Tags:    []string{"master", "v1"},
		Port:    8000,
	}

	reg := &CatalogRegistration{
		Datacenter: "dc1",
		Node:       "foobar",
		Address:    "192.168.10.10",
		Service:    service,
	}

	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}
		if node.Services["redis1"].EnableTagOverride != false {
			r.Fatal("tag override set")
		}

		services, _, err := catalog.Service("redis", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(services) < 1 || services[0].ServiceName != "redis" {
			r.Fatal("missing service: redis")
		}
		if services[0].ServiceEnableTagOverride != false {
			r.Fatal("tag override set")
		}
	})

	service.EnableTagOverride = true

	retry.Run(t, func(r *retry.R) {
		if _, err := catalog.Register(reg, nil); err != nil {
			r.Fatal(err)
		}

		node, _, err := catalog.Node("foobar", nil)
		if err != nil {
			r.Fatal(err)
		}

		if _, ok := node.Services["redis1"]; !ok {
			r.Fatal("missing service: redis1")
		}
		if node.Services["redis1"].EnableTagOverride != true {
			r.Fatal("tag override not set")
		}

		services, _, err := catalog.Service("redis", "", nil)
		if err != nil {
			r.Fatal(err)
		}

		if len(services) < 1 || services[0].ServiceName != "redis" {
			r.Fatal("missing service: redis")
		}
		if services[0].ServiceEnableTagOverride != true {
			r.Fatal("tag override not set")
		}
	})
}
<|endoftext|>


<commit_before>package client

// import (
// 	"fmt"
// 	"github.com/appcelerator/amp/api/rpc/build"
// 	"golang.org/x/net/context"
// 	"strings"
// )

// func createProjectRequest(repo string) (request *build.ProjectRequest, err error) {
// 	split := strings.Split(repo, "/")
// 	if len(split) != 2 {
// 		return nil, fmt.Errorf("invalid repo %q", split)
// 	}
// 	owner := split[0]
// 	name := split[1]
// 	request = &build.ProjectRequest{
// 		Owner: owner,
// 		Name:  name,
// 	}
// 	return
// }

// func createBuildRequest(buildid string) (request *build.BuildRequest, err error) {
// 	split := strings.Split(buildid, "/")
// 	if len(split) != 3 {
// 		return nil, fmt.Errorf("invalid build %q", split)
// 	}
// 	owner := split[0]
// 	name := split[1]
// 	sha := split[2]
// 	request = &build.BuildRequest{
// 		Owner: owner,
// 		Name:  name,
// 		Sha:   sha,
// 	}
// 	return
// }

// // RegisterProject registers a project through the amplifier proxy to the build service
// func (a *AMP) RegisterProject(repo string) (*build.Project, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	request, err := createProjectRequest(repo)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return client.CreateProject(ctx, request)
// }

// // RemoveProject removes a project through the amplifier proxy to the build service
// func (a *AMP) RemoveProject(repo string) (*build.Project, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	request, err := createProjectRequest(repo)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return client.DeleteProject(ctx, request)
// }

// // ListProjects lists projects through the amplifier proxy to the build service
// func (a *AMP) ListProjects(organization string, latest bool) (*build.ProjectList, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	query := build.ProjectQuery{
// 		Organization: organization,
// 		Latest:       latest,
// 	}
// 	return client.ListProjects(ctx, &query)
// }

// // ListBuilds lists builds through the amplifier proxy to the build service
// func (a *AMP) ListBuilds(repo string, latest bool) (*build.BuildList, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	request, err := createProjectRequest(repo)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return client.ListBuilds(ctx, request)
// }

// // BuildLog returns a log stream through the amplifier proxy to the build service
// func (a *AMP) BuildLog(buildid string) (build.AmpBuild_BuildLogClient, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	request, err := createBuildRequest(buildid)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return client.BuildLog(ctx, request)
// }

// // Rebuild triggers a rebuild through the amplifier proxy to the build service
// func (a *AMP) Rebuild(buildid string) (*build.Build, error) {
// 	client := build.NewAmpBuildClient(a.Conn)
// 	ctx := context.Background()
// 	request, err := createBuildRequest(buildid)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return client.Rebuild(ctx, request)
// }
<commit_msg>remove build<commit_after><|endoftext|>


<commit_before>// Copyright 2021 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package resolvers

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
)

// QueryTimeout is the duration until a Resolver query expires.
var QueryTimeout = 5 * time.Second

// ResolveError contains the Rcode returned during the DNS query.
type ResolveError struct {
	Err   string
	Rcode int
}

func (e *ResolveError) Error() string {
	return e.Err
}

type resolveRequest struct {
	ID        uint16
	Timestamp time.Time
	Name      string
	Qtype     uint16
	Msg       *dns.Msg
	Result    chan *resolveResult
}

type resolveResult struct {
	Msg   *dns.Msg
	Again bool
	Err   error
}

func (r *baseResolver) returnRequest(req *resolveRequest, res *resolveResult) {
	req.Result <- res
}

func makeResolveResult(msg *dns.Msg, again bool, err string, rcode int) *resolveResult {
	return &resolveResult{
		Msg:   msg,
		Again: again,
		Err: &ResolveError{
			Err:   err,
			Rcode: rcode,
		},
	}
}

func checkContext(ctx context.Context) error {
	select {
	case <-ctx.Done():
		return &ResolveError{
			Err:   "The request context was cancelled",
			Rcode: ResolverErrRcode,
		}
	default:
	}
	return nil
}

type xchgManager struct {
	sync.Mutex
	xchgs map[string]*resolveRequest
}

func newXchgManager() *xchgManager {
	return &xchgManager{xchgs: make(map[string]*resolveRequest)}
}

func xchgKey(id uint16, name string) string {
	return fmt.Sprintf("%d:%s", id, strings.ToLower(RemoveLastDot(name)))
}

func (r *xchgManager) add(req *resolveRequest) error {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(req.ID, req.Name)
	if _, found := r.xchgs[key]; found {
		return fmt.Errorf("Key %s is already in use", key)
	}

	r.xchgs[key] = req
	return nil
}

func (r *xchgManager) updateTimestamp(id uint16, name string) error {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(id, name)
	if _, found := r.xchgs[key]; !found {
		return fmt.Errorf("A message for key %s was not found", key)
	}

	r.xchgs[key].Timestamp = time.Now()
	return nil
}

func (r *xchgManager) remove(id uint16, name string) *resolveRequest {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(id, name)
	if _, found := r.xchgs[key]; !found {
		return nil
	}

	reqs := r.delete([]string{key})
	if len(reqs) != 1 {
		return nil
	}

	return reqs[0]
}

func (r *xchgManager) removeExpired() []*resolveRequest {
	r.Lock()
	defer r.Unlock()

	now := time.Now()
	var keys []string
	for key, req := range r.xchgs {
		if !req.Timestamp.IsZero() && now.After(req.Timestamp.Add(QueryTimeout)) {
			keys = append(keys, key)
		}
	}

	return r.delete(keys)
}

func (r *xchgManager) removeAll() []*resolveRequest {
	r.Lock()
	defer r.Unlock()

	var keys []string
	for key := range r.xchgs {
		keys = append(keys, key)
	}

	return r.delete(keys)
}

func (r *xchgManager) delete(keys []string) []*resolveRequest {
	var removed []*resolveRequest

	for _, k := range keys {
		req := r.xchgs[k]

		r.xchgs[k] = nil
		delete(r.xchgs, k)
		removed = append(removed, req)
	}

	return removed
}

const (
	minNumInAverage   int           = 10
	maxNumInAverage   int           = 20
	failurePercentage float64       = 0.8
	expireDuration    time.Duration = 30 * time.Second
)

type slidingWindowEntry struct {
	Timeout   bool
	Timestamp time.Time
}
type slidingWindowTimeouts struct {
	sync.Mutex
	avgs map[string][]*slidingWindowEntry
}

func newSlidingWindowTimeouts() *slidingWindowTimeouts {
	return &slidingWindowTimeouts{avgs: make(map[string][]*slidingWindowEntry)}
}

func (s *slidingWindowTimeouts) updateTimeouts(key string, timeout bool) bool {
	s.Lock()
	defer s.Unlock()

	now := time.Now()
	s.avgs[key] = append(s.avgs[key], &slidingWindowEntry{
		Timeout:   timeout,
		Timestamp: now,
	})

	l := len(s.avgs[key])
	if l > maxNumInAverage {
		s.avgs[key] = s.avgs[key][l-maxNumInAverage:]
	}

	var expired int
	var timeouts float64
	for _, v := range s.avgs[key] {
		if now.After(v.Timestamp.Add(expireDuration)) {
			expired++
			continue
		}
		if v.Timeout {
			timeouts++
		}
	}
	s.avgs[key] = s.avgs[key][expired:]

	l = len(s.avgs[key])
	if l < minNumInAverage {
		return false
	}

	if timeouts/float64(l) >= failurePercentage {
		return true
	}
	return false
}
<commit_msg>Reduced the DNS query timeout to two seconds<commit_after>// Copyright 2021 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package resolvers

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
)

// QueryTimeout is the duration until a Resolver query expires.
var QueryTimeout = 2 * time.Second

// ResolveError contains the Rcode returned during the DNS query.
type ResolveError struct {
	Err   string
	Rcode int
}

func (e *ResolveError) Error() string {
	return e.Err
}

type resolveRequest struct {
	ID        uint16
	Timestamp time.Time
	Name      string
	Qtype     uint16
	Msg       *dns.Msg
	Result    chan *resolveResult
}

type resolveResult struct {
	Msg   *dns.Msg
	Again bool
	Err   error
}

func (r *baseResolver) returnRequest(req *resolveRequest, res *resolveResult) {
	req.Result <- res
}

func makeResolveResult(msg *dns.Msg, again bool, err string, rcode int) *resolveResult {
	return &resolveResult{
		Msg:   msg,
		Again: again,
		Err: &ResolveError{
			Err:   err,
			Rcode: rcode,
		},
	}
}

func checkContext(ctx context.Context) error {
	select {
	case <-ctx.Done():
		return &ResolveError{
			Err:   "The request context was cancelled",
			Rcode: ResolverErrRcode,
		}
	default:
	}
	return nil
}

type xchgManager struct {
	sync.Mutex
	xchgs map[string]*resolveRequest
}

func newXchgManager() *xchgManager {
	return &xchgManager{xchgs: make(map[string]*resolveRequest)}
}

func xchgKey(id uint16, name string) string {
	return fmt.Sprintf("%d:%s", id, strings.ToLower(RemoveLastDot(name)))
}

func (r *xchgManager) add(req *resolveRequest) error {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(req.ID, req.Name)
	if _, found := r.xchgs[key]; found {
		return fmt.Errorf("Key %s is already in use", key)
	}

	r.xchgs[key] = req
	return nil
}

func (r *xchgManager) updateTimestamp(id uint16, name string) error {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(id, name)
	if _, found := r.xchgs[key]; !found {
		return fmt.Errorf("A message for key %s was not found", key)
	}

	r.xchgs[key].Timestamp = time.Now()
	return nil
}

func (r *xchgManager) remove(id uint16, name string) *resolveRequest {
	r.Lock()
	defer r.Unlock()

	key := xchgKey(id, name)
	if _, found := r.xchgs[key]; !found {
		return nil
	}

	reqs := r.delete([]string{key})
	if len(reqs) != 1 {
		return nil
	}

	return reqs[0]
}

func (r *xchgManager) removeExpired() []*resolveRequest {
	r.Lock()
	defer r.Unlock()

	now := time.Now()
	var keys []string
	for key, req := range r.xchgs {
		if !req.Timestamp.IsZero() && now.After(req.Timestamp.Add(QueryTimeout)) {
			keys = append(keys, key)
		}
	}

	return r.delete(keys)
}

func (r *xchgManager) removeAll() []*resolveRequest {
	r.Lock()
	defer r.Unlock()

	var keys []string
	for key := range r.xchgs {
		keys = append(keys, key)
	}

	return r.delete(keys)
}

func (r *xchgManager) delete(keys []string) []*resolveRequest {
	var removed []*resolveRequest

	for _, k := range keys {
		req := r.xchgs[k]

		r.xchgs[k] = nil
		delete(r.xchgs, k)
		removed = append(removed, req)
	}

	return removed
}

const (
	minNumInAverage   int           = 10
	maxNumInAverage   int           = 20
	failurePercentage float64       = 0.8
	expireDuration    time.Duration = 30 * time.Second
)

type slidingWindowEntry struct {
	Timeout   bool
	Timestamp time.Time
}
type slidingWindowTimeouts struct {
	sync.Mutex
	avgs map[string][]*slidingWindowEntry
}

func newSlidingWindowTimeouts() *slidingWindowTimeouts {
	return &slidingWindowTimeouts{avgs: make(map[string][]*slidingWindowEntry)}
}

func (s *slidingWindowTimeouts) updateTimeouts(key string, timeout bool) bool {
	s.Lock()
	defer s.Unlock()

	now := time.Now()
	s.avgs[key] = append(s.avgs[key], &slidingWindowEntry{
		Timeout:   timeout,
		Timestamp: now,
	})

	l := len(s.avgs[key])
	if l > maxNumInAverage {
		s.avgs[key] = s.avgs[key][l-maxNumInAverage:]
	}

	var expired int
	var timeouts float64
	for _, v := range s.avgs[key] {
		if now.After(v.Timestamp.Add(expireDuration)) {
			expired++
			continue
		}
		if v.Timeout {
			timeouts++
		}
	}
	s.avgs[key] = s.avgs[key][expired:]

	l = len(s.avgs[key])
	if l < minNumInAverage {
		return false
	}

	if timeouts/float64(l) >= failurePercentage {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
)

//UserInfo stores information of user
type UserInfo struct {
	First string
	Last  string
}

//Check checks for errors
func check(e error) {
	if e != nil {
		panic(e)
	}
}

//printHelp opens project page (http://hasit.github.io/licensethis/) in the default browser.
func printHelp() {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", "http://hasit.github.io/licensethis/").Start()
		check(err)
	case "windows", "darwin":
		err = exec.Command("open", "http://hasit.github.io/licensethis/").Start()
		check(err)
	default:
		err = fmt.Errorf("unsupported platform")
		check(err)
	}
}

func userConfig() {
	first := os.Getenv("FIRSTNAME")
	last := os.Getenv("LASTNAME")

	//if first != nil; last != nil {
	fmt.Printf("First: %v\n", first)
	fmt.Printf("Last: %v\n", last)
	//}
}

//parseArgs parses command line arguments and calls appropriate functions.
func parseArgs(args []string) {
	if len(args) != 0 {
		switch args[0] {
		case "help":
			printHelp()
		case "config":
			userConfig()
		case "info":
			fmt.Println("info")
		case "list":
			fmt.Println("list")
		case "generate":
			fmt.Println("generate")
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else {
		fmt.Println("Incorrect usage!")
		fmt.Println("Type `licensethis help` for help on proper usage.")
	}
}

func main() {
	args := os.Args[1:]

	parseArgs(args)
}
<commit_msg>userConfig() work<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"runtime"
)

//UserInfo stores information of user
var userinfo struct {
	FirstName string `json:"firstName"`
	LastName  string `json:"lastName"`
}

//Check checks for errors
func check(e error) {
	if e != nil {
		panic(e)
	}
}

//printHelp opens project page (http://hasit.github.io/licensethis/) in the default browser.
func printHelp() {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", "http://hasit.github.io/licensethis/").Start()
		check(err)
	case "windows", "darwin":
		err = exec.Command("open", "http://hasit.github.io/licensethis/").Start()
		check(err)
	default:
		err = fmt.Errorf("unsupported platform")
		check(err)
	}
}

func userConfig() {
	homepath := os.Getenv("HOME")
	configpath := homepath + "/.licensethis.json"

	if _, err := os.Stat(configpath); err == nil {
		fmt.Println("Config file exists.")

		configfile, err := os.Open(configpath)
		check(err)

		jsonparser := json.NewDecoder(configfile)
		err = jsonparser.Decode(&userinfo)
		check(err)

		fmt.Printf("Full name: %v %v\n", userinfo.FirstName, userinfo.LastName)
		configfile.Close()
	} else {
		fmt.Println("Config file does not exist.")

	}
}

//parseArgs parses command line arguments and calls appropriate functions.
func parseArgs(args []string) {
	if len(args) != 0 {
		switch args[0] {
		case "help":
			printHelp()
		case "config":
			userConfig()
		case "info":
			fmt.Println("info")
		case "list":
			fmt.Println("list")
		case "generate":
			fmt.Println("generate")
		default:
			fmt.Println("Incorrect usage!")
			fmt.Println("Type `licensethis help` for help on proper usage.")
		}
	} else {
		fmt.Println("Incorrect usage!")
		fmt.Println("Type `licensethis help` for help on proper usage.")
	}
}

func main() {
	args := os.Args[1:]

	parseArgs(args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"sort"
	"strings"

	cli "gopkg.in/urfave/cli.v2"
	yaml "gopkg.in/yaml.v2"

	"github.com/jgillich/nixbench/modules"
)

// VERSION is set at build time
var VERSION = "master"

func main() {

	moduleNames := []string{}
	for key := range modules.Modules {
		moduleNames = append(moduleNames, key)
	}

	app := &cli.App{
		Name:        "nixbench",
		Usage:       "A better benchmarking tool for servers",
		Description: fmt.Sprintf("Loaded modules: %s", strings.Trim(fmt.Sprintf("%v", moduleNames), "[]")),
		Version:     VERSION,
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  "yaml",
				Usage: "Output as yaml",
			},
			&cli.BoolFlag{
				Name:  "all",
				Usage: "Run all modules",
			},
			&cli.StringSliceFlag{
				Name:    "module",
				Aliases: []string{"m"},
				Usage:   "Modules to enable",
				Value:   cli.NewStringSlice("host", "cpu", "disk", "net", "geekbench"),
			},
		},
		Action: func(c *cli.Context) error {
			if !c.Bool("yaml") {
				fmt.Printf("nixbench %s - https://github.com/jgillich/nixbench", VERSION)
			}

			enabledModules := c.StringSlice("module")

			if c.Bool("all") {
				enabledModules = moduleNames
			}

			sort.Strings(enabledModules)

			for _, name := range enabledModules {
				module, ok := modules.Modules[name]

				if !ok {
					return fmt.Errorf("unknown module '%s'", name)
				}

				if err := module.Run(); err != nil {
					return err
				}

				if c.Bool("yaml") {
					var r map[string]interface{} = map[string]interface{}{}
					r[name] = module
					yml, err := yaml.Marshal(r)
					if err != nil {
						return err
					}
					fmt.Printf(string(yml))
				} else {
					fmt.Printf("\n\n%s\n", name)
					for i := 1; i <= len(name); i++ {
						fmt.Print("-")
					}
					fmt.Print("\n")
					module.Print()
				}
			}

			return nil
		},
	}

	app.Run(os.Args)
}
<commit_msg>print newline after finished module<commit_after>package main

import (
	"fmt"
	"os"
	"sort"
	"strings"

	cli "gopkg.in/urfave/cli.v2"
	yaml "gopkg.in/yaml.v2"

	"github.com/jgillich/nixbench/modules"
)

// VERSION is set at build time
var VERSION = "master"

func main() {

	moduleNames := []string{}
	for key := range modules.Modules {
		moduleNames = append(moduleNames, key)
	}

	app := &cli.App{
		Name:        "nixbench",
		Usage:       "A better benchmarking tool for servers",
		Description: fmt.Sprintf("Loaded modules: %s", strings.Trim(fmt.Sprintf("%v", moduleNames), "[]")),
		Version:     VERSION,
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  "yaml",
				Usage: "Output as yaml",
			},
			&cli.BoolFlag{
				Name:  "all",
				Usage: "Run all modules",
			},
			&cli.StringSliceFlag{
				Name:    "module",
				Aliases: []string{"m"},
				Usage:   "Modules to enable",
				Value:   cli.NewStringSlice("host", "cpu", "disk", "net", "geekbench"),
			},
		},
		Action: func(c *cli.Context) error {
			if !c.Bool("yaml") {
				fmt.Printf("nixbench %s - https://github.com/jgillich/nixbench\n", VERSION)
			}

			enabledModules := c.StringSlice("module")

			if c.Bool("all") {
				enabledModules = moduleNames
			}

			sort.Strings(enabledModules)

			for _, name := range enabledModules {
				module, ok := modules.Modules[name]

				if !ok {
					return fmt.Errorf("unknown module '%s'", name)
				}

				if err := module.Run(); err != nil {
					return err
				}

				if c.Bool("yaml") {
					var r map[string]interface{} = map[string]interface{}{}
					r[name] = module
					yml, err := yaml.Marshal(r)
					if err != nil {
						return err
					}
					fmt.Printf(string(yml))
				} else {
					fmt.Printf("\n%s\n", name)
					for i := 1; i <= len(name); i++ {
						fmt.Print("-")
					}
					fmt.Print("\n")
					module.Print()
					fmt.Print("\n")
				}
			}

			return nil
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Package store provides a simple distributed key-value store. The keys and
// associated values are changed via distributed consensus, meaning that the
// values are changed only when a majority of nodes in the cluster agree on
// the new value.
//
// Distributed consensus is provided via the Raft algorithm, specifically the
// Hashicorp implementation.
package store

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type command struct {
	Op    string `json:"op,omitempty"`
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

// Store is a simple key-value store, where all changes are made via Raft consensus.
type Store struct {
	RaftDir  string
	RaftBind string

	mu sync.Mutex
	m  map[string]string // The key-value store for the system.

	raft *raft.Raft // The consensus mechanism

	logger *log.Logger
}

// New returns a new Store.
func New() *Store {
	return &Store{
		m:      make(map[string]string),
		logger: log.New(os.Stderr, "[store] ", log.LstdFlags),
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomes the first node, and therefore leader, of the cluster.
// localID should be the server identifier for this node.
func (s *Store) Open(enableSingle bool, localID string) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()
	config.LocalID = raft.ServerID(localID)

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", s.RaftBind)
	if err != nil {
		return err
	}
	transport, err := raft.NewTCPTransport(s.RaftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(s.RaftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return fmt.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore, err := raftboltdb.NewBoltStore(filepath.Join(s.RaftDir, "raft.db"))
	if err != nil {
		return fmt.Errorf("new bolt store: %s", err)
	}

	// Instantiate the Raft systems.
	ra, err := raft.NewRaft(config, (*fsm)(s), logStore, logStore, snapshots, transport)
	if err != nil {
		return fmt.Errorf("new raft: %s", err)
	}
	s.raft = ra

	if enableSingle {
		configuration := raft.Configuration{
			Servers: []raft.Server{
				{
					ID:      config.LocalID,
					Address: transport.LocalAddr(),
				},
			},
		}
		ra.BootstrapCluster(configuration)
	}

	return nil
}

// Get returns the value for the given key.
func (s *Store) Get(key string) (string, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.m[key], nil
}

// Set sets the value for the given key.
func (s *Store) Set(key, value string) error {
	if s.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	c := &command{
		Op:    "set",
		Key:   key,
		Value: value,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return err
	}

	f := s.raft.Apply(b, raftTimeout)
	return f.Error()
}

// Delete deletes the given key.
func (s *Store) Delete(key string) error {
	if s.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	c := &command{
		Op:  "delete",
		Key: key,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return err
	}

	f := s.raft.Apply(b, raftTimeout)
	return f.Error()
}

// Join joins a node, identified by nodeID and located at addr, to this store.
// The node must be ready to respond to Raft communications at that address.
func (s *Store) Join(nodeID, addr string) error {
	s.logger.Printf("received join request for remote node %s at %s", nodeID, addr)

	f := s.raft.AddVoter(raft.ServerID(nodeID), raft.ServerAddress(addr), 0, 0)
	if f.Error() != nil {
		return f.Error()
	}
	s.logger.Printf("node %s at %s joined successfully", nodeID, addr)
	return nil
}

type fsm Store

// Apply applies a Raft log entry to the key-value store.
func (f *fsm) Apply(l *raft.Log) interface{} {
	var c command
	if err := json.Unmarshal(l.Data, &c); err != nil {
		panic(fmt.Sprintf("failed to unmarshal command: %s", err.Error()))
	}

	switch c.Op {
	case "set":
		return f.applySet(c.Key, c.Value)
	case "delete":
		return f.applyDelete(c.Key)
	default:
		panic(fmt.Sprintf("unrecognized command op: %s", c.Op))
	}
}

// Snapshot returns a snapshot of the key-value store.
func (f *fsm) Snapshot() (raft.FSMSnapshot, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	// Clone the map.
	o := make(map[string]string)
	for k, v := range f.m {
		o[k] = v
	}
	return &fsmSnapshot{store: o}, nil
}

// Restore stores the key-value store to a previous state.
func (f *fsm) Restore(rc io.ReadCloser) error {
	o := make(map[string]string)
	if err := json.NewDecoder(rc).Decode(&o); err != nil {
		return err
	}

	// Set the state from the snapshot, no lock required according to
	// Hashicorp docs.
	f.m = o
	return nil
}

func (f *fsm) applySet(key, value string) interface{} {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.m[key] = value
	return nil
}

func (f *fsm) applyDelete(key string) interface{} {
	f.mu.Lock()
	defer f.mu.Unlock()
	delete(f.m, key)
	return nil
}

type fsmSnapshot struct {
	store map[string]string
}

func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
	err := func() error {
		// Encode data.
		b, err := json.Marshal(f.store)
		if err != nil {
			return err
		}

		// Write data to sink.
		if _, err := sink.Write(b); err != nil {
			return err
		}

		// Close the sink.
		return sink.Close()
	}()

	if err != nil {
		sink.Cancel()
	}

	return err
}

func (f *fsmSnapshot) Release() {}
<commit_msg>Support nodes rejoining with new IPs<commit_after>// Package store provides a simple distributed key-value store. The keys and
// associated values are changed via distributed consensus, meaning that the
// values are changed only when a majority of nodes in the cluster agree on
// the new value.
//
// Distributed consensus is provided via the Raft algorithm, specifically the
// Hashicorp implementation.
package store

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type command struct {
	Op    string `json:"op,omitempty"`
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

// Store is a simple key-value store, where all changes are made via Raft consensus.
type Store struct {
	RaftDir  string
	RaftBind string

	mu sync.Mutex
	m  map[string]string // The key-value store for the system.

	raft *raft.Raft // The consensus mechanism

	logger *log.Logger
}

// New returns a new Store.
func New() *Store {
	return &Store{
		m:      make(map[string]string),
		logger: log.New(os.Stderr, "[store] ", log.LstdFlags),
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomes the first node, and therefore leader, of the cluster.
// localID should be the server identifier for this node.
func (s *Store) Open(enableSingle bool, localID string) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()
	config.LocalID = raft.ServerID(localID)

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", s.RaftBind)
	if err != nil {
		return err
	}
	transport, err := raft.NewTCPTransport(s.RaftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(s.RaftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return fmt.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore, err := raftboltdb.NewBoltStore(filepath.Join(s.RaftDir, "raft.db"))
	if err != nil {
		return fmt.Errorf("new bolt store: %s", err)
	}

	// Instantiate the Raft systems.
	ra, err := raft.NewRaft(config, (*fsm)(s), logStore, logStore, snapshots, transport)
	if err != nil {
		return fmt.Errorf("new raft: %s", err)
	}
	s.raft = ra

	if enableSingle {
		configuration := raft.Configuration{
			Servers: []raft.Server{
				{
					ID:      config.LocalID,
					Address: transport.LocalAddr(),
				},
			},
		}
		ra.BootstrapCluster(configuration)
	}

	return nil
}

// Get returns the value for the given key.
func (s *Store) Get(key string) (string, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.m[key], nil
}

// Set sets the value for the given key.
func (s *Store) Set(key, value string) error {
	if s.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	c := &command{
		Op:    "set",
		Key:   key,
		Value: value,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return err
	}

	f := s.raft.Apply(b, raftTimeout)
	return f.Error()
}

// Delete deletes the given key.
func (s *Store) Delete(key string) error {
	if s.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	c := &command{
		Op:  "delete",
		Key: key,
	}
	b, err := json.Marshal(c)
	if err != nil {
		return err
	}

	f := s.raft.Apply(b, raftTimeout)
	return f.Error()
}

// Join joins a node, identified by nodeID and located at addr, to this store.
// The node must be ready to respond to Raft communications at that address.
func (s *Store) Join(nodeID, addr string) error {
	s.logger.Printf("received join request for remote node %s at %s", nodeID, addr)

	configFuture := s.raft.GetConfiguration()
	if err := configFuture.Error(); err != nil {
		s.logger.Printf("failed to get raft configuration: %v", err)
		return err
	}

	for _, srv := range configFuture.Configuration().Servers {
		// If a node already exists with either the joining node's ID or address,
		// that node may need to be removed from the config first.
		if srv.ID == raft.ServerID(nodeID) || srv.Address == raft.ServerAddress(addr) {
			// However if *both* the ID and the address are the same, then nothing -- not even
			// a join operation -- is needed.
			if srv.Address == raft.ServerAddress(addr) && srv.ID == raft.ServerID(nodeID) {
				s.logger.Printf("node %s at %s already member of cluster, ignoring join request", nodeID, addr)
				return nil
			}

			future := s.raft.RemoveServer(srv.ID, 0, 0)
			if err := future.Error(); err != nil {
				return fmt.Errorf("error removing existing node %s at %s: %s", nodeID, addr, err)
			}
		}
	}

	f := s.raft.AddVoter(raft.ServerID(nodeID), raft.ServerAddress(addr), 0, 0)
	if f.Error() != nil {
		return f.Error()
	}
	s.logger.Printf("node %s at %s joined successfully", nodeID, addr)
	return nil
}

type fsm Store

// Apply applies a Raft log entry to the key-value store.
func (f *fsm) Apply(l *raft.Log) interface{} {
	var c command
	if err := json.Unmarshal(l.Data, &c); err != nil {
		panic(fmt.Sprintf("failed to unmarshal command: %s", err.Error()))
	}

	switch c.Op {
	case "set":
		return f.applySet(c.Key, c.Value)
	case "delete":
		return f.applyDelete(c.Key)
	default:
		panic(fmt.Sprintf("unrecognized command op: %s", c.Op))
	}
}

// Snapshot returns a snapshot of the key-value store.
func (f *fsm) Snapshot() (raft.FSMSnapshot, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	// Clone the map.
	o := make(map[string]string)
	for k, v := range f.m {
		o[k] = v
	}
	return &fsmSnapshot{store: o}, nil
}

// Restore stores the key-value store to a previous state.
func (f *fsm) Restore(rc io.ReadCloser) error {
	o := make(map[string]string)
	if err := json.NewDecoder(rc).Decode(&o); err != nil {
		return err
	}

	// Set the state from the snapshot, no lock required according to
	// Hashicorp docs.
	f.m = o
	return nil
}

func (f *fsm) applySet(key, value string) interface{} {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.m[key] = value
	return nil
}

func (f *fsm) applyDelete(key string) interface{} {
	f.mu.Lock()
	defer f.mu.Unlock()
	delete(f.m, key)
	return nil
}

type fsmSnapshot struct {
	store map[string]string
}

func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
	err := func() error {
		// Encode data.
		b, err := json.Marshal(f.store)
		if err != nil {
			return err
		}

		// Write data to sink.
		if _, err := sink.Write(b); err != nil {
			return err
		}

		// Close the sink.
		return sink.Close()
	}()

	if err != nil {
		sink.Cancel()
	}

	return err
}

func (f *fsmSnapshot) Release() {}
<|endoftext|>


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

package libsacloud

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

package libsacloud

// Version バージョン
const Version = "2.16.0"
<|endoftext|>