<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsx

import (
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/googlecloudplatform/gcsfuse/internal/storage"
	"github.com/jacobsa/reqtrace"
	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/internal/canned"
	"github.com/googlecloudplatform/gcsfuse/internal/logger"
	"github.com/googlecloudplatform/gcsfuse/internal/monitor"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"github.com/jacobsa/ratelimit"
	"github.com/jacobsa/timeutil"
)

type BucketConfig struct {
	BillingProject                     string
	OnlyDir                            string
	EgressBandwidthLimitBytesPerSecond float64
	OpRateLimitHz                      float64
	StatCacheCapacity                  int
	StatCacheTTL                       time.Duration
	EnableMonitoring                   bool
	EnableStorageClientLibrary         bool
	DebugGCS                           bool

	// Files backed by on object of length at least AppendThreshold that have
	// only been appended to (i.e. none of the object's contents have been
	// dirtied) will be written out by "appending" to the object in GCS with this
	// process:
	//
	// 1. Write out a temporary object containing the appended contents whose
	//    name begins with TmpObjectPrefix.
	//
	// 2. Compose the original object and the temporary object on top of the
	//    original object.
	//
	// 3. Delete the temporary object.
	//
	// Note that if the process fails or is interrupted the temporary object will
	// not be cleaned up, so the user must ensure that TmpObjectPrefix is
	// periodically garbage collected.
	AppendThreshold int64
	TmpObjectPrefix string
}

// BucketManager manages the lifecycle of buckets.
type BucketManager interface {
	SetUpBucket(
		ctx context.Context,
		name string) (b SyncerBucket, err error)

	// Shuts down the bucket manager and its buckets
	ShutDown()
}

type bucketManager struct {
	config        BucketConfig
	conn          *Connection
	storageHandle storage.StorageHandle

	// Garbage collector
	gcCtx                 context.Context
	stopGarbageCollecting func()
}

func NewBucketManager(config BucketConfig, conn *Connection, storageHandle storage.StorageHandle) BucketManager {
	bm := &bucketManager{
		config:        config,
		conn:          conn,
		storageHandle: storageHandle,
	}
	bm.gcCtx, bm.stopGarbageCollecting = context.WithCancel(context.Background())
	return bm
}

func setUpRateLimiting(
	in gcs.Bucket,
	opRateLimitHz float64,
	egressBandwidthLimit float64) (out gcs.Bucket, err error) {
	// If no rate limiting has been requested, just return the bucket.
	if !(opRateLimitHz > 0 || egressBandwidthLimit > 0) {
		out = in
		return
	}

	// Treat a disabled limit as a very large one.
	if !(opRateLimitHz > 0) {
		opRateLimitHz = 1e15
	}

	if !(egressBandwidthLimit > 0) {
		egressBandwidthLimit = 1e15
	}

	// Choose token bucket capacities, targeting only a few percent error in each
	// window of the given size.
	const window = 8 * time.Hour

	opCapacity, err := ratelimit.ChooseTokenBucketCapacity(
		opRateLimitHz,
		window)

	if err != nil {
		err = fmt.Errorf("Choosing operation token bucket capacity: %w", err)
		return
	}

	egressCapacity, err := ratelimit.ChooseTokenBucketCapacity(
		egressBandwidthLimit,
		window)

	if err != nil {
		err = fmt.Errorf("Choosing egress bandwidth token bucket capacity: %w", err)
		return
	}

	// Create the throttles.
	opThrottle := ratelimit.NewThrottle(opRateLimitHz, opCapacity)
	egressThrottle := ratelimit.NewThrottle(egressBandwidthLimit, egressCapacity)

	// And the bucket.
	out = ratelimit.NewThrottledBucket(
		opThrottle,
		egressThrottle,
		in)

	return
}

// Configure a bucket based on the supplied config.
//
// Special case: if the bucket name is canned.FakeBucketName, set up a fake
// bucket as described in that package.
func (bm *bucketManager) SetUpGcsBucket(ctx context.Context, name string) (b gcs.Bucket, err error) {
	if bm.config.EnableStorageClientLibrary {
		b, err = bm.storageHandle.BucketHandle(name)
		if err != nil {
			fmt.Println("Error Occurred while handling bucket ")
		} else {
			if reqtrace.Enabled() {
				b = gcs.GetWrappedWithReqtraceBucket(b)
			}
			if bm.config.DebugGCS {
				b = gcs.NewDebugBucket(b, logger.NewDebug("gcs: "))
			}
		}
	} else {
		logger.Infof("OpenBucket(%q, %q)\n", name, bm.config.BillingProject)
		b, err = bm.conn.OpenBucket(
			ctx,
			&gcs.OpenBucketOptions{
				Name:           name,
				BillingProject: bm.config.BillingProject,
			},
		)
	}
	return
}

func (bm *bucketManager) SetUpBucket(
	ctx context.Context,
	name string) (sb SyncerBucket, err error) {
	var b gcs.Bucket
	// Set up the appropriate backing bucket.
	if name == canned.FakeBucketName {
		b = canned.MakeFakeBucket(ctx)
	} else {
		b, err = bm.SetUpGcsBucket(ctx, name)
		if err != nil {
			err = fmt.Errorf("OpenBucket: %w", err)
			return
		}
	}

	// Limit to a requested prefix of the bucket, if any.
	if bm.config.OnlyDir != "" {
		b, err = NewPrefixBucket(path.Clean(bm.config.OnlyDir)+"/", b)
		if err != nil {
			err = fmt.Errorf("NewPrefixBucket: %w", err)
			return
		}
	}

	// Enable rate limiting, if requested.
	b, err = setUpRateLimiting(
		b,
		bm.config.OpRateLimitHz,
		bm.config.EgressBandwidthLimitBytesPerSecond)

	if err != nil {
		err = fmt.Errorf("setUpRateLimiting: %w", err)
		return
	}

	// Enable cached StatObject results, if appropriate.
	if bm.config.StatCacheTTL != 0 {
		cacheCapacity := bm.config.StatCacheCapacity
		b = gcscaching.NewFastStatBucket(
			bm.config.StatCacheTTL,
			gcscaching.NewStatCache(cacheCapacity),
			timeutil.RealClock(),
			b)
	}

	// Enable content type awareness
	b = NewContentTypeBucket(b)

	// Enable monitoring
	if bm.config.EnableMonitoring {
		b = monitor.NewMonitoringBucket(b)
	}

	// Enable Syncer
	if bm.config.TmpObjectPrefix == "" {
		err = errors.New("You must set TmpObjectPrefix.")
		return
	}
	sb = NewSyncerBucket(
		bm.config.AppendThreshold,
		bm.config.TmpObjectPrefix,
		b)

	// Check whether this bucket works, giving the user a warning early if there
	// is some problem.
	{
		_, err := b.ListObjects(ctx, &gcs.ListObjectsRequest{MaxResults: 1})
		if err != nil {
			fmt.Fprintln(os.Stdout, "WARNING, bucket doesn't appear to work: ", err)
		}
	}

	// Periodically garbage collect temporary objects
	go garbageCollect(bm.gcCtx, bm.config.TmpObjectPrefix, sb)

	return
}

func (bm *bucketManager) ShutDown() {
	bm.stopGarbageCollecting()
}
<commit_msg>updated bucket manager<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsx

import (
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/googlecloudplatform/gcsfuse/internal/storage"
	"github.com/jacobsa/reqtrace"
	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/internal/canned"
	"github.com/googlecloudplatform/gcsfuse/internal/logger"
	"github.com/googlecloudplatform/gcsfuse/internal/monitor"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"github.com/jacobsa/ratelimit"
	"github.com/jacobsa/timeutil"
)

type BucketConfig struct {
	BillingProject                     string
	OnlyDir                            string
	EgressBandwidthLimitBytesPerSecond float64
	OpRateLimitHz                      float64
	StatCacheCapacity                  int
	StatCacheTTL                       time.Duration
	EnableMonitoring                   bool
	EnableStorageClientLibrary         bool
	DebugGCS                           bool

	// Files backed by on object of length at least AppendThreshold that have
	// only been appended to (i.e. none of the object's contents have been
	// dirtied) will be written out by "appending" to the object in GCS with this
	// process:
	//
	// 1. Write out a temporary object containing the appended contents whose
	//    name begins with TmpObjectPrefix.
	//
	// 2. Compose the original object and the temporary object on top of the
	//    original object.
	//
	// 3. Delete the temporary object.
	//
	// Note that if the process fails or is interrupted the temporary object will
	// not be cleaned up, so the user must ensure that TmpObjectPrefix is
	// periodically garbage collected.
	AppendThreshold int64
	TmpObjectPrefix string
}

// BucketManager manages the lifecycle of buckets.
type BucketManager interface {
	SetUpBucket(
		ctx context.Context,
		name string) (b SyncerBucket, err error)

	// Shuts down the bucket manager and its buckets
	ShutDown()
}

type bucketManager struct {
	config        BucketConfig
	conn          *Connection
	storageHandle storage.StorageHandle

	// Garbage collector
	gcCtx                 context.Context
	stopGarbageCollecting func()
}

func NewBucketManager(config BucketConfig, conn *Connection, storageHandle storage.StorageHandle) BucketManager {
	bm := &bucketManager{
		config:        config,
		conn:          conn,
		storageHandle: storageHandle,
	}
	bm.gcCtx, bm.stopGarbageCollecting = context.WithCancel(context.Background())
	return bm
}

func setUpRateLimiting(
	in gcs.Bucket,
	opRateLimitHz float64,
	egressBandwidthLimit float64) (out gcs.Bucket, err error) {
	// If no rate limiting has been requested, just return the bucket.
	if !(opRateLimitHz > 0 || egressBandwidthLimit > 0) {
		out = in
		return
	}

	// Treat a disabled limit as a very large one.
	if !(opRateLimitHz > 0) {
		opRateLimitHz = 1e15
	}

	if !(egressBandwidthLimit > 0) {
		egressBandwidthLimit = 1e15
	}

	// Choose token bucket capacities, targeting only a few percent error in each
	// window of the given size.
	const window = 8 * time.Hour

	opCapacity, err := ratelimit.ChooseTokenBucketCapacity(
		opRateLimitHz,
		window)

	if err != nil {
		err = fmt.Errorf("Choosing operation token bucket capacity: %w", err)
		return
	}

	egressCapacity, err := ratelimit.ChooseTokenBucketCapacity(
		egressBandwidthLimit,
		window)

	if err != nil {
		err = fmt.Errorf("Choosing egress bandwidth token bucket capacity: %w", err)
		return
	}

	// Create the throttles.
	opThrottle := ratelimit.NewThrottle(opRateLimitHz, opCapacity)
	egressThrottle := ratelimit.NewThrottle(egressBandwidthLimit, egressCapacity)

	// And the bucket.
	out = ratelimit.NewThrottledBucket(
		opThrottle,
		egressThrottle,
		in)

	return
}

// Configure a bucket based on the supplied config.
//
// Special case: if the bucket name is canned.FakeBucketName, set up a fake
// bucket as described in that package.
func (bm *bucketManager) SetUpGcsBucket(ctx context.Context, name string) (b gcs.Bucket, err error) {
	if bm.config.EnableStorageClientLibrary {
		b, err = bm.storageHandle.BucketHandle(name)
		if err != nil {
			return
		} else {
			if reqtrace.Enabled() {
				b = gcs.GetWrappedWithReqtraceBucket(b)
			}
			if bm.config.DebugGCS {
				b = gcs.NewDebugBucket(b, logger.NewDebug("gcs: "))
			}
		}
	} else {
		logger.Infof("OpenBucket(%q, %q)\n", name, bm.config.BillingProject)
		b, err = bm.conn.OpenBucket(
			ctx,
			&gcs.OpenBucketOptions{
				Name:           name,
				BillingProject: bm.config.BillingProject,
			},
		)
	}
	return
}

func (bm *bucketManager) SetUpBucket(
	ctx context.Context,
	name string) (sb SyncerBucket, err error) {
	var b gcs.Bucket
	// Set up the appropriate backing bucket.
	if name == canned.FakeBucketName {
		b = canned.MakeFakeBucket(ctx)
	} else {
		b, err = bm.SetUpGcsBucket(ctx, name)
		if err != nil {
			err = fmt.Errorf("OpenBucket: %w", err)
			return
		}
	}

	// Limit to a requested prefix of the bucket, if any.
	if bm.config.OnlyDir != "" {
		b, err = NewPrefixBucket(path.Clean(bm.config.OnlyDir)+"/", b)
		if err != nil {
			err = fmt.Errorf("NewPrefixBucket: %w", err)
			return
		}
	}

	// Enable rate limiting, if requested.
	b, err = setUpRateLimiting(
		b,
		bm.config.OpRateLimitHz,
		bm.config.EgressBandwidthLimitBytesPerSecond)

	if err != nil {
		err = fmt.Errorf("setUpRateLimiting: %w", err)
		return
	}

	// Enable cached StatObject results, if appropriate.
	if bm.config.StatCacheTTL != 0 {
		cacheCapacity := bm.config.StatCacheCapacity
		b = gcscaching.NewFastStatBucket(
			bm.config.StatCacheTTL,
			gcscaching.NewStatCache(cacheCapacity),
			timeutil.RealClock(),
			b)
	}

	// Enable content type awareness
	b = NewContentTypeBucket(b)

	// Enable monitoring
	if bm.config.EnableMonitoring {
		b = monitor.NewMonitoringBucket(b)
	}

	// Enable Syncer
	if bm.config.TmpObjectPrefix == "" {
		err = errors.New("You must set TmpObjectPrefix.")
		return
	}
	sb = NewSyncerBucket(
		bm.config.AppendThreshold,
		bm.config.TmpObjectPrefix,
		b)

	// Check whether this bucket works, giving the user a warning early if there
	// is some problem.
	{
		_, err := b.ListObjects(ctx, &gcs.ListObjectsRequest{MaxResults: 1})
		if err != nil {
			fmt.Fprintln(os.Stdout, "WARNING, bucket doesn't appear to work: ", err)
		}
	}

	// Periodically garbage collect temporary objects
	go garbageCollect(bm.gcCtx, bm.config.TmpObjectPrefix, sb)

	return
}

func (bm *bucketManager) ShutDown() {
	bm.stopGarbageCollecting()
}
<|endoftext|>


<commit_before>package arn

// AnimePopularity shows how many users have that anime in a certain list.
type AnimePopularity struct {
	Watching  int `json:"watching"`
	Completed int `json:"completed"`
	Planned   int `json:"planned"`
	Hold      int `json:"hold"`
	Dropped   int `json:"dropped"`
}
<commit_msg>Added total<commit_after>package arn

// AnimePopularity shows how many users have that anime in a certain list.
type AnimePopularity struct {
	Watching  int `json:"watching"`
	Completed int `json:"completed"`
	Planned   int `json:"planned"`
	Hold      int `json:"hold"`
	Dropped   int `json:"dropped"`
}

// Total returns the total number of users that added this anime to their collection.
func (p *AnimePopularity) Total() int {
	return p.Watching + p.Completed + p.Planned + p.Hold + p.Dropped
}
<|endoftext|>


<commit_before>package Application

import (
	"chukuparser/Application/engparse"
	"chukuparser/Application/morphparse"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"os"

	"log"
	// "net/http"
	"runtime"
	"runtime/pprof"
)

const (
	NUM_CPUS_FLAG = "cpus"
)

var (
	CPUs       int
	CPUProfile string
	allOut     bool = false
)

var AppCommands []*commander.Command = []*commander.Command{
	morphparse.MorphCmd(),
	engparse.EnglishCmd(),
}

func AllCommands() *commander.Commander {
	cmd := &commander.Commander{
		Name:     os.Args[0],
		Commands: AppCommands,
		Flag:     flag.NewFlagSet("app", flag.ExitOnError),
	}
	for _, app := range cmd.Commands {
		app.Run = NewAppWrapCommand(app.Run)
		app.Flag.IntVar(&CPUs, NUM_CPUS_FLAG, 0, "Max CPUS to use (runtime.GOMAXPROCS); 0 = all")
		app.Flag.StringVar(&CPUProfile, "cpuprofile", "", "write cpu profile to file")
	}
	return cmd
}

func InitCommand(cmd *commander.Command, args []string) {
	maxCPUs := runtime.NumCPU()
	if CPUs > maxCPUs {
		log.Printf("Warning: Number of CPUs capped to all available (%d)", maxCPUs)
		CPUs = 0
	}
	if CPUs == 0 {
		CPUs = maxCPUs
	}
	if allOut {
		log.Printf("GOMAXPROCS:\t%d", CPUs)
	}
	runtime.GOMAXPROCS(CPUs)

	// launch net server for profiling
	// log.Println("Profiler interface:", "http://127.0.0.1:6060/debug/pprof")
	// go func() {
	// 	log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
	// }()
}

func NewAppWrapCommand(f func(cmd *commander.Command, args []string)) func(cmd *commander.Command, args []string) {
	log.SetFlags(log.LstdFlags | log.Lmicroseconds)

	wrapped := func(cmd *commander.Command, args []string) {
		InitCommand(cmd, args)
		if CPUProfile != "" {
			f, err := os.Create(CPUProfile)
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Writing profiling info to", CPUProfile)
			pprof.StartCPUProfile(f)
			defer pprof.StopCPUProfile()
		}
		if allOut {
			log.Println()
		}
		f(cmd, args)
	}

	return wrapped
}
<commit_msg>Re-enabled output for global flag parsing<commit_after>package Application

import (
	"chukuparser/Application/engparse"
	"chukuparser/Application/morphparse"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"os"

	"log"
	// "net/http"
	"runtime"
	"runtime/pprof"
)

const (
	NUM_CPUS_FLAG = "cpus"
)

var (
	CPUs       int
	CPUProfile string
	allOut     bool = true
)

var AppCommands []*commander.Command = []*commander.Command{
	morphparse.MorphCmd(),
	engparse.EnglishCmd(),
}

func AllCommands() *commander.Commander {
	cmd := &commander.Commander{
		Name:     os.Args[0],
		Commands: AppCommands,
		Flag:     flag.NewFlagSet("app", flag.ExitOnError),
	}
	for _, app := range cmd.Commands {
		app.Run = NewAppWrapCommand(app.Run)
		app.Flag.IntVar(&CPUs, NUM_CPUS_FLAG, 0, "Max CPUS to use (runtime.GOMAXPROCS); 0 = all")
		app.Flag.StringVar(&CPUProfile, "cpuprofile", "", "write cpu profile to file")
	}
	return cmd
}

func InitCommand(cmd *commander.Command, args []string) {
	maxCPUs := runtime.NumCPU()
	if CPUs > maxCPUs {
		log.Printf("Warning: Number of CPUs capped to all available (%d)", maxCPUs)
		CPUs = 0
	}
	if CPUs == 0 {
		CPUs = maxCPUs
	}
	if allOut {
		log.Printf("GOMAXPROCS:\t%d", CPUs)
	}
	runtime.GOMAXPROCS(CPUs)

	// launch net server for profiling
	// log.Println("Profiler interface:", "http://127.0.0.1:6060/debug/pprof")
	// go func() {
	// 	log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
	// }()
}

func NewAppWrapCommand(f func(cmd *commander.Command, args []string)) func(cmd *commander.Command, args []string) {
	log.SetFlags(log.LstdFlags | log.Lmicroseconds)

	wrapped := func(cmd *commander.Command, args []string) {
		InitCommand(cmd, args)
		if CPUProfile != "" {
			f, err := os.Create(CPUProfile)
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Writing profiling info to", CPUProfile)
			pprof.StartCPUProfile(f)
			defer pprof.StopCPUProfile()
		}
		if allOut {
			log.Println()
		}
		f(cmd, args)
	}

	return wrapped
}
<|endoftext|>


<commit_before>package beaglebone

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hybridgroup/gobot"
	"github.com/hybridgroup/gobot/platforms/gpio"
	"github.com/hybridgroup/gobot/platforms/i2c"
	"github.com/hybridgroup/gobot/sysfs"
)

var _ gobot.Adaptor = (*BeagleboneAdaptor)(nil)

var _ gpio.DigitalReader = (*BeagleboneAdaptor)(nil)
var _ gpio.DigitalWriter = (*BeagleboneAdaptor)(nil)
var _ gpio.AnalogReader = (*BeagleboneAdaptor)(nil)
var _ gpio.PwmWriter = (*BeagleboneAdaptor)(nil)
var _ gpio.ServoWriter = (*BeagleboneAdaptor)(nil)

var _ i2c.I2c = (*BeagleboneAdaptor)(nil)

var slots = "/sys/devices/bone_capemgr.*"
var ocp = "/sys/devices/ocp.*"
var usrLed = "/sys/devices/ocp.3/gpio-leds.8/leds/beaglebone:green:"

var glob = func(pattern string) (matches []string, err error) {
	return filepath.Glob(pattern)
}

var pins = map[string]int{
	"P8_3":  38,
	"P8_4":  39,
	"P8_5":  34,
	"P8_6":  35,
	"P8_7":  66,
	"P8_8":  67,
	"P8_9":  69,
	"P8_10": 68,
	"P8_11": 45,
	"P8_12": 44,
	"P8_13": 23,
	"P8_14": 26,
	"P8_15": 47,
	"P8_16": 46,
	"P8_17": 27,
	"P8_18": 65,
	"P8_19": 22,
	"P8_20": 63,
	"P8_21": 62,
	"P8_22": 37,
	"P8_23": 36,
	"P8_24": 33,
	"P8_25": 32,
	"P8_26": 61,
	"P8_27": 86,
	"P8_28": 88,
	"P8_29": 87,
	"P8_30": 89,
	"P8_31": 10,
	"P8_32": 11,
	"P8_33": 9,
	"P8_34": 81,
	"P8_35": 8,
	"P8_36": 80,
	"P8_37": 78,
	"P8_38": 79,
	"P8_39": 76,
	"P8_40": 77,
	"P8_41": 74,
	"P8_42": 75,
	"P8_43": 72,
	"P8_44": 73,
	"P8_45": 70,
	"P8_46": 71,
	"P9_11": 30,
	"P9_12": 60,
	"P9_13": 31,
	"P9_14": 50,
	"P9_15": 48,
	"P9_16": 51,
	"P9_17": 5,
	"P9_18": 4,
	"P9_19": 13,
	"P9_20": 12,
	"P9_21": 3,
	"P9_22": 2,
	"P9_23": 49,
	"P9_24": 15,
	"P9_25": 117,
	"P9_26": 14,
	"P9_27": 115,
	"P9_28": 113,
	"P9_29": 111,
	"P9_30": 112,
	"P9_31": 110,
}

var pwmPins = map[string]string{
	"P9_14": "P9_14",
	"P9_21": "P9_21",
	"P9_22": "P9_22",
	"P9_29": "P9_29",
	"P9_42": "P9_42",
	"P8_13": "P8_13",
	"P8_34": "P8_34",
	"P8_45": "P8_45",
	"P8_46": "P8_46",
}

var analogPins = map[string]string{
	"P9_39": "AIN0",
	"P9_40": "AIN1",
	"P9_37": "AIN2",
	"P9_38": "AIN3",
	"P9_33": "AIN4",
	"P8_36": "AIN5",
	"P8_35": "AIN6",
}

type BeagleboneAdaptor struct {
	name        string
	digitalPins []sysfs.DigitalPin
	pwmPins     map[string]*pwmPin
	i2cDevice   io.ReadWriteCloser
	ocp         string
	helper      string
	slots       string
}

// NewBeagleboneAdaptor returns a new beaglebone adaptor with specified name
func NewBeagleboneAdaptor(name string) *BeagleboneAdaptor {
	b := &BeagleboneAdaptor{
		name:        name,
		digitalPins: make([]sysfs.DigitalPin, 120),
		pwmPins:     make(map[string]*pwmPin),
	}

	g, _ := glob(ocp)
	b.ocp = g[0]
	g, _ = glob(slots)
	b.slots = fmt.Sprintf("%v/slots", g[0])

	return b
}
func (b *BeagleboneAdaptor) Name() string { return b.name }

// Connect returns true on a succesful connection to beaglebone board.
// It initializes digital, pwm and analog pins
func (b *BeagleboneAdaptor) Connect() (errs []error) {
	if err := ensureSlot(b.slots, "cape-bone-iio"); err != nil {
		return []error{err}
	}

	if err := ensureSlot(b.slots, "am33xx_pwm"); err != nil {
		return []error{err}
	}

	g, err := glob(fmt.Sprintf("%v/helper.*", b.ocp))
	if err != nil {
		return []error{err}
	}
	b.helper = g[0]

	return
}

// Finalize returns true when board connection is finalized correctly.
func (b *BeagleboneAdaptor) Finalize() (errs []error) {
	for _, pin := range b.pwmPins {
		if pin != nil {
			if err := pin.release(); err != nil {
				errs = append(errs, err)
			}
		}
	}
	for _, pin := range b.digitalPins {
		if pin != nil {
			if err := pin.Unexport(); err != nil {
				errs = append(errs, err)
			}
		}
	}
	if b.i2cDevice != nil {
		if err := b.i2cDevice.Close(); err != nil {
			errs = append(errs, err)
		}
	}
	return
}

// PwmWrite writes value in specified pin
func (b *BeagleboneAdaptor) PwmWrite(pin string, val byte) (err error) {
	return b.pwmWrite(pin, val)
}

// ServoWrite writes scaled value to servo in specified pin
func (b *BeagleboneAdaptor) ServoWrite(pin string, val byte) (err error) {
	i, err := b.pwmPin(pin)
	if err != nil {
		return err
	}
	period := 16666666.0
	duty := (gobot.FromScale(float64(val), 0, 180.0) * 0.115) + 0.05
	return b.pwmPins[i].pwmWrite(strconv.Itoa(int(period)), strconv.Itoa(int(period*duty)))
}

// DigitalRead returns a digital value from specified pin
func (b *BeagleboneAdaptor) DigitalRead(pin string) (val int, err error) {
	sysfsPin, err := b.digitalPin(pin, sysfs.IN)
	if err != nil {
		return
	}
	return sysfsPin.Read()
}

// DigitalWrite writes a digital value to specified pin.
// valid usr pin values are usr0, usr1, usr2 and usr3
func (b *BeagleboneAdaptor) DigitalWrite(pin string, val byte) (err error) {
	if strings.Contains(pin, "usr") {
		fi, err := sysfs.OpenFile(usrLed+pin+"/brightness", os.O_WRONLY|os.O_APPEND, 0666)
		defer fi.Close()
		if err != nil {
			return err
		}
		_, err = fi.WriteString(strconv.Itoa(int(val)))
		return err
	}
	sysfsPin, err := b.digitalPin(pin, sysfs.OUT)
	if err != nil {
		return err
	}
	return sysfsPin.Write(int(val))
}

// AnalogRead returns an analog value from specified pin
func (b *BeagleboneAdaptor) AnalogRead(pin string) (val int, err error) {
	analogPin, err := b.translateAnalogPin(pin)
	if err != nil {
		return
	}
	fi, err := sysfs.OpenFile(fmt.Sprintf("%v/%v", b.helper, analogPin), os.O_RDONLY, 0644)
	defer fi.Close()

	if err != nil {
		return
	}

	var buf = make([]byte, 1024)
	_, err = fi.Read(buf)
	if err != nil {
		return
	}

	val, _ = strconv.Atoi(strings.Split(string(buf), "\n")[0])
	return
}

// I2cStart starts a i2c device in specified address
func (b *BeagleboneAdaptor) I2cStart(address byte) (err error) {
	b.i2cDevice, err = sysfs.NewI2cDevice("/dev/i2c-1", address)
	return
}

// I2CWrite writes data to i2c device
func (b *BeagleboneAdaptor) I2cWrite(data []byte) (err error) {
	_, err = b.i2cDevice.Write(data)
	return err
}

// I2cRead returns value from i2c device using specified size
func (b *BeagleboneAdaptor) I2cRead(size uint) (data []byte, err error) {
	data = make([]byte, size)
	_, err = b.i2cDevice.Read(data)
	return
}

// translatePin converts digital pin name to pin position
func (b *BeagleboneAdaptor) translatePin(pin string) (value int, err error) {
	for key, value := range pins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// translatePwmPin converts pwm pin name to pin position
func (b *BeagleboneAdaptor) translatePwmPin(pin string) (value string, err error) {
	for key, value := range pwmPins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// translateAnalogPin converts analog pin name to pin position
func (b *BeagleboneAdaptor) translateAnalogPin(pin string) (value string, err error) {
	for key, value := range analogPins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// digitalPin retrieves digital pin value by name
func (b *BeagleboneAdaptor) digitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPin, err error) {
	i, err := b.translatePin(pin)
	if err != nil {
		return
	}
	if b.digitalPins[i] == nil {
		b.digitalPins[i] = sysfs.NewDigitalPin(i)
		err := b.digitalPins[i].Export()
		if err != nil {
			return nil, err
		}
	}
	if err = b.digitalPins[i].Direction(dir); err != nil {
		return
	}
	return b.digitalPins[i], nil
}

// pwPin retrieves pwm pin value by name
func (b *BeagleboneAdaptor) pwmPin(pin string) (i string, err error) {
	i, err = b.translatePwmPin(pin)
	if err != nil {
		return
	}
	if b.pwmPins[i] == nil {
		err = ensureSlot(b.slots, fmt.Sprintf("bone_pwm_%v", pin))
		if err != nil {
			return
		}
		b.pwmPins[i], err = newPwmPin(i, b.ocp)
		if err != nil {
			return
		}
	}
	return
}

// pwmWrite writes pwm value to specified pin
func (b *BeagleboneAdaptor) pwmWrite(pin string, val byte) (err error) {
	i, err := b.pwmPin(pin)
	if err != nil {
		return
	}
	period := 500000.0
	duty := gobot.FromScale(float64(val), 0, 255.0)
	return b.pwmPins[i].pwmWrite(strconv.Itoa(int(period)), strconv.Itoa(int(period*duty)))
}

func ensureSlot(slots, item string) (err error) {
	fi, err := sysfs.OpenFile(slots, os.O_RDWR|os.O_APPEND, 0666)
	defer fi.Close()
	if err != nil {
		return
	}

	// ensure the slot is not already written into the capemanager
	// (from: https://github.com/mrmorphic/hwio/blob/master/module_bb_pwm.go#L190)
	scanner := bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(line, item) > 0 {
			return
		}
	}

	_, err = fi.WriteString(item)
	if err != nil {
		return err
	}
	fi.Sync()

	scanner = bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(line, item) > 0 {
			return
		}
	}
	return
}
<commit_msg>go lint and documentation tweaks for the beaglebone package<commit_after>package beaglebone

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hybridgroup/gobot"
	"github.com/hybridgroup/gobot/platforms/gpio"
	"github.com/hybridgroup/gobot/platforms/i2c"
	"github.com/hybridgroup/gobot/sysfs"
)

var _ gobot.Adaptor = (*BeagleboneAdaptor)(nil)

var _ gpio.DigitalReader = (*BeagleboneAdaptor)(nil)
var _ gpio.DigitalWriter = (*BeagleboneAdaptor)(nil)
var _ gpio.AnalogReader = (*BeagleboneAdaptor)(nil)
var _ gpio.PwmWriter = (*BeagleboneAdaptor)(nil)
var _ gpio.ServoWriter = (*BeagleboneAdaptor)(nil)

var _ i2c.I2c = (*BeagleboneAdaptor)(nil)

var slots = "/sys/devices/bone_capemgr.*"
var ocp = "/sys/devices/ocp.*"
var usrLed = "/sys/devices/ocp.3/gpio-leds.8/leds/beaglebone:green:"

var glob = func(pattern string) (matches []string, err error) {
	return filepath.Glob(pattern)
}

var pins = map[string]int{
	"P8_3":  38,
	"P8_4":  39,
	"P8_5":  34,
	"P8_6":  35,
	"P8_7":  66,
	"P8_8":  67,
	"P8_9":  69,
	"P8_10": 68,
	"P8_11": 45,
	"P8_12": 44,
	"P8_13": 23,
	"P8_14": 26,
	"P8_15": 47,
	"P8_16": 46,
	"P8_17": 27,
	"P8_18": 65,
	"P8_19": 22,
	"P8_20": 63,
	"P8_21": 62,
	"P8_22": 37,
	"P8_23": 36,
	"P8_24": 33,
	"P8_25": 32,
	"P8_26": 61,
	"P8_27": 86,
	"P8_28": 88,
	"P8_29": 87,
	"P8_30": 89,
	"P8_31": 10,
	"P8_32": 11,
	"P8_33": 9,
	"P8_34": 81,
	"P8_35": 8,
	"P8_36": 80,
	"P8_37": 78,
	"P8_38": 79,
	"P8_39": 76,
	"P8_40": 77,
	"P8_41": 74,
	"P8_42": 75,
	"P8_43": 72,
	"P8_44": 73,
	"P8_45": 70,
	"P8_46": 71,
	"P9_11": 30,
	"P9_12": 60,
	"P9_13": 31,
	"P9_14": 50,
	"P9_15": 48,
	"P9_16": 51,
	"P9_17": 5,
	"P9_18": 4,
	"P9_19": 13,
	"P9_20": 12,
	"P9_21": 3,
	"P9_22": 2,
	"P9_23": 49,
	"P9_24": 15,
	"P9_25": 117,
	"P9_26": 14,
	"P9_27": 115,
	"P9_28": 113,
	"P9_29": 111,
	"P9_30": 112,
	"P9_31": 110,
}

var pwmPins = map[string]string{
	"P9_14": "P9_14",
	"P9_21": "P9_21",
	"P9_22": "P9_22",
	"P9_29": "P9_29",
	"P9_42": "P9_42",
	"P8_13": "P8_13",
	"P8_34": "P8_34",
	"P8_45": "P8_45",
	"P8_46": "P8_46",
}

var analogPins = map[string]string{
	"P9_39": "AIN0",
	"P9_40": "AIN1",
	"P9_37": "AIN2",
	"P9_38": "AIN3",
	"P9_33": "AIN4",
	"P8_36": "AIN5",
	"P8_35": "AIN6",
}

// BeagleboneAdaptor is the gobot.Adaptor representation for the Beaglebone
type BeagleboneAdaptor struct {
	name        string
	digitalPins []sysfs.DigitalPin
	pwmPins     map[string]*pwmPin
	i2cDevice   io.ReadWriteCloser
	ocp         string
	helper      string
	slots       string
}

// NewBeagleboneAdaptor returns a new BeagleboneAdaptor with specified name
func NewBeagleboneAdaptor(name string) *BeagleboneAdaptor {
	b := &BeagleboneAdaptor{
		name:        name,
		digitalPins: make([]sysfs.DigitalPin, 120),
		pwmPins:     make(map[string]*pwmPin),
	}

	g, _ := glob(ocp)
	b.ocp = g[0]
	g, _ = glob(slots)
	b.slots = fmt.Sprintf("%v/slots", g[0])

	return b
}

// Name returns the BeagleboneAdaptors name
func (b *BeagleboneAdaptor) Name() string { return b.name }

// Connect initializes the pwm and analog dts.
func (b *BeagleboneAdaptor) Connect() (errs []error) {
	if err := ensureSlot(b.slots, "cape-bone-iio"); err != nil {
		return []error{err}
	}

	if err := ensureSlot(b.slots, "am33xx_pwm"); err != nil {
		return []error{err}
	}

	g, err := glob(fmt.Sprintf("%v/helper.*", b.ocp))
	if err != nil {
		return []error{err}
	}
	b.helper = g[0]

	return
}

// Finalize releases all i2c devices and exported analog, digital, pwm pins.
func (b *BeagleboneAdaptor) Finalize() (errs []error) {
	for _, pin := range b.pwmPins {
		if pin != nil {
			if err := pin.release(); err != nil {
				errs = append(errs, err)
			}
		}
	}
	for _, pin := range b.digitalPins {
		if pin != nil {
			if err := pin.Unexport(); err != nil {
				errs = append(errs, err)
			}
		}
	}
	if b.i2cDevice != nil {
		if err := b.i2cDevice.Close(); err != nil {
			errs = append(errs, err)
		}
	}
	return
}

// PwmWrite writes the 0-254 value to the specified pin
func (b *BeagleboneAdaptor) PwmWrite(pin string, val byte) (err error) {
	return b.pwmWrite(pin, val)
}

// ServoWrite writes the 0-180 degree val to the specified pin.
func (b *BeagleboneAdaptor) ServoWrite(pin string, val byte) (err error) {
	i, err := b.pwmPin(pin)
	if err != nil {
		return err
	}
	period := 16666666.0
	duty := (gobot.FromScale(float64(val), 0, 180.0) * 0.115) + 0.05
	return b.pwmPins[i].pwmWrite(strconv.Itoa(int(period)), strconv.Itoa(int(period*duty)))
}

// DigitalRead returns a digital value from specified pin
func (b *BeagleboneAdaptor) DigitalRead(pin string) (val int, err error) {
	sysfsPin, err := b.digitalPin(pin, sysfs.IN)
	if err != nil {
		return
	}
	return sysfsPin.Read()
}

// DigitalWrite writes a digital value to specified pin.
// valid usr pin values are usr0, usr1, usr2 and usr3
func (b *BeagleboneAdaptor) DigitalWrite(pin string, val byte) (err error) {
	if strings.Contains(pin, "usr") {
		fi, err := sysfs.OpenFile(usrLed+pin+"/brightness", os.O_WRONLY|os.O_APPEND, 0666)
		defer fi.Close()
		if err != nil {
			return err
		}
		_, err = fi.WriteString(strconv.Itoa(int(val)))
		return err
	}
	sysfsPin, err := b.digitalPin(pin, sysfs.OUT)
	if err != nil {
		return err
	}
	return sysfsPin.Write(int(val))
}

// AnalogRead returns an analog value from specified pin
func (b *BeagleboneAdaptor) AnalogRead(pin string) (val int, err error) {
	analogPin, err := b.translateAnalogPin(pin)
	if err != nil {
		return
	}
	fi, err := sysfs.OpenFile(fmt.Sprintf("%v/%v", b.helper, analogPin), os.O_RDONLY, 0644)
	defer fi.Close()

	if err != nil {
		return
	}

	var buf = make([]byte, 1024)
	_, err = fi.Read(buf)
	if err != nil {
		return
	}

	val, _ = strconv.Atoi(strings.Split(string(buf), "\n")[0])
	return
}

// I2cStart starts a i2c device in specified address on i2c bus /dev/i2c-1
func (b *BeagleboneAdaptor) I2cStart(address byte) (err error) {
	b.i2cDevice, err = sysfs.NewI2cDevice("/dev/i2c-1", address)
	return
}

// I2cWrite writes data to i2c device
func (b *BeagleboneAdaptor) I2cWrite(data []byte) (err error) {
	_, err = b.i2cDevice.Write(data)
	return err
}

// I2cRead returns size bytes from the i2c device
func (b *BeagleboneAdaptor) I2cRead(size uint) (data []byte, err error) {
	data = make([]byte, size)
	_, err = b.i2cDevice.Read(data)
	return
}

// translatePin converts digital pin name to pin position
func (b *BeagleboneAdaptor) translatePin(pin string) (value int, err error) {
	for key, value := range pins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// translatePwmPin converts pwm pin name to pin position
func (b *BeagleboneAdaptor) translatePwmPin(pin string) (value string, err error) {
	for key, value := range pwmPins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// translateAnalogPin converts analog pin name to pin position
func (b *BeagleboneAdaptor) translateAnalogPin(pin string) (value string, err error) {
	for key, value := range analogPins {
		if key == pin {
			return value, nil
		}
	}
	err = errors.New("Not a valid pin")
	return
}

// digitalPin retrieves digital pin value by name
func (b *BeagleboneAdaptor) digitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPin, err error) {
	i, err := b.translatePin(pin)
	if err != nil {
		return
	}
	if b.digitalPins[i] == nil {
		b.digitalPins[i] = sysfs.NewDigitalPin(i)
		err := b.digitalPins[i].Export()
		if err != nil {
			return nil, err
		}
	}
	if err = b.digitalPins[i].Direction(dir); err != nil {
		return
	}
	return b.digitalPins[i], nil
}

// pwPin retrieves pwm pin value by name
func (b *BeagleboneAdaptor) pwmPin(pin string) (i string, err error) {
	i, err = b.translatePwmPin(pin)
	if err != nil {
		return
	}
	if b.pwmPins[i] == nil {
		err = ensureSlot(b.slots, fmt.Sprintf("bone_pwm_%v", pin))
		if err != nil {
			return
		}
		b.pwmPins[i], err = newPwmPin(i, b.ocp)
		if err != nil {
			return
		}
	}
	return
}

// pwmWrite writes pwm value to specified pin
func (b *BeagleboneAdaptor) pwmWrite(pin string, val byte) (err error) {
	i, err := b.pwmPin(pin)
	if err != nil {
		return
	}
	period := 500000.0
	duty := gobot.FromScale(float64(val), 0, 255.0)
	return b.pwmPins[i].pwmWrite(strconv.Itoa(int(period)), strconv.Itoa(int(period*duty)))
}

func ensureSlot(slots, item string) (err error) {
	fi, err := sysfs.OpenFile(slots, os.O_RDWR|os.O_APPEND, 0666)
	defer fi.Close()
	if err != nil {
		return
	}

	// ensure the slot is not already written into the capemanager
	// (from: https://github.com/mrmorphic/hwio/blob/master/module_bb_pwm.go#L190)
	scanner := bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(line, item) > 0 {
			return
		}
	}

	_, err = fi.WriteString(item)
	if err != nil {
		return err
	}
	fi.Sync()

	scanner = bufio.NewScanner(fi)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(line, item) > 0 {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package swarm

import (
	"common"
	"crypto/sha256"
	"log"
	"time"
)

// SwarmInformed - Swarm member shave been told to join swarm
type StateSwarmInformed struct {
	//Map of hosts seen to number of times they have failed to generate a block
	//Used for both host alive tracking & host block generation tracking
	hostsseen map[string]int

	// How many times we have broadcast that we are alive, we use a two stage
	// process where we broadcast, and then broadcast again when we have seen
	// enough nodes up to form a majority
	broadcastcount uint
	stage2         string

	// This state has two phases, the learning phase where it listens for new
	// hosts and then the commit stage where it listens for a block that
	// is correct according to its knowledge and then votes for it.
	learning bool

	heartbeats []*HeartBeatTransaction

	chain    *BlockChain
	blockgen <-chan time.Time

	sendBroadcast chan struct{}
	transaction   chan common.Transaction
	block         chan bwrap
	sync          chan struct{}
}

type bwrap struct {
	block *Block
	ret   chan State
}

func NewStateSwarmInformed(chain *BlockChain) (s *StateSwarmInformed) {
	s = new(StateSwarmInformed)
	s.chain = chain
	s.blockgen = time.Tick(1 * time.Second)

	s.learning = true
	s.hostsseen = make(map[string]int)
	s.sendBroadcast = make(chan struct{})
	s.transaction = make(chan common.Transaction)
	s.block = make(chan bwrap)
	s.sync = make(chan struct{})

	go s.broadcastLife()
	go s.mainloop()
	return
}

func (s *StateSwarmInformed) HandleTransaction(t common.Transaction) {
	log.Print("STATE: Transaction queed to be handled")
	s.transaction <- t
}

func (s *StateSwarmInformed) Sync() {
	<-s.sync
}

func (s *StateSwarmInformed) HandleBlock(b *Block) State {
	c := make(chan State)
	s.block <- bwrap{b, c}
	r := <-c
	return r
}

func (s *StateSwarmInformed) blockCompiler() (compiler string) {

	hosts := make([]string, 0, len(s.hostsseen))

	//Pull all hosts who we haven't seen skipping a block
	for host, skipped := range s.hostsseen {
		if skipped != 0 {
			continue
		}
		hosts = append(hosts, host)
	}

	//Check if we should be the block generator
	compiler = common.RendezvousHash(sha256.New(), hosts, s.chain.Host)
	return
}

func (s *StateSwarmInformed) mainloop() {

	var compiler string

	for {
		select {
		case _ = <-s.sendBroadcast:
			log.Print("STATE: NodeAlive Transaction to be Queed")
			s.broadcastcount += 1
			s.chain.outgoingTransactions <- common.TransactionNetworkObject(
				NewNodeAlive(s.chain.Host, s.chain.Id))
			log.Print("STATE: NodeAlive Transaction Queed")

		case s.sync <- struct{}{}:

		case t := <-s.transaction:
			log.Print("STATE: Transaction Recieved")
			s.handleTransaction(t)

		case o := <-s.block:
			n := s.handleBlock(o.block)
			o.ret <- n

		case _ = <-s.blockgen:
			log.Print("STATE: Blockgen Recieved")

			if s.learning {
				s.learning = false
				log.Print("STATE: Disable Learning")
			} else if len(s.chain.BlockHistory) == 0 {
				if compiler != "" {
					s.hostsseen[compiler] += 1
				}
			}

			//Dont't try to generate a block if we don't have a majority of hosts
			if len(s.hostsseen) < 8 {
				continue
				//Should actually switch to state swarmdied after a while
			}

			compiler = s.blockCompiler()

			if len(s.chain.BlockHistory) == 0 && compiler == s.chain.Host {

				id, err := common.RandomString(8)
				if err != nil {
					panic("checkBlockGenRandom" + err.Error())
				}
				b := &Block{id, s.chain.Id, s.chain.Host, nil, nil, nil}
				b.StorageMapping = make(map[string]interface{})
				for host, _ := range s.hostsseen {
					b.StorageMapping[host] = nil
				}

				s.heartbeats = s.heartbeats[0:0]

				s.chain.outgoingTransactions <- common.BlockNetworkObject(b)
				s.HandleBlock(b)
			}

			if len(s.chain.BlockHistory) == 1 && len(s.heartbeats) > 2 {

				id, err := common.RandomString(8)
				if err != nil {
					panic(err)
				}

				b := &Block{id, s.chain.Id, s.chain.Host, nil, nil, nil}
				b.StorageMapping = s.chain.BlockHistory[0].StorageMapping
				b.EntropyStage1 = make(map[string]string)
				for _, h := range s.heartbeats {
					b.EntropyStage1[h.Host] = h.Stage1
				}

				s.chain.outgoingTransactions <- common.BlockNetworkObject(b)
				s.HandleBlock(b)

			}
		}
		log.Print("STATE: Signal Handling Finished")
	}
}

func (s *StateSwarmInformed) broadcastLife() {
	s.sendBroadcast <- struct{}{}
}

func (s *StateSwarmInformed) handleTransaction(t common.Transaction) {
	switch n := t.(type) {
	case *NodeAlive:
		if !s.learning {
			return
		}

		s.hostsseen[n.Node] = 0
		// Resend hostsseen count once you have seen a majority of hosts
		if len(s.hostsseen) > 2 && s.broadcastcount < 2 {
			s.broadcastLife()
		}

		log.Print("STATE: Node added")

	case *HeartBeatTransaction:

		//If we're learning this is too early
		if s.learning {
			return
		}

		// If we're not trying to compile we don't care
		if s.blockCompiler() != s.chain.Host {
			return
		}

		if n.Prevblock == s.chain.BlockHistory[0].Id {
			s.heartbeats = append(s.heartbeats, n)
		}

	default:
		return
	}
}

func (s *StateSwarmInformed) handleBlock(b *Block) State {

	// If the learning timeout hasn't expired, don't accept blocks
	if s.learning {
		return s
	}

	// All blocks in this state should be generated by the ideal host
	if b.Compiler != s.blockCompiler() {
		return s
	}

	// We are looking for a block to generate a heartbeat for
	if len(s.chain.BlockHistory) == 0 {
		s.chain.AddBlock(b)

		stage1, stage2 := common.HashedRandomData(sha256.New(), 8)
		s.stage2 = stage2

		if _, ok := b.StorageMapping[s.chain.Host]; ok {
			h := NewHeartBeat(s.chain.BlockHistory[0], s.chain.Host, stage1, "")
			s.chain.outgoingTransactions <- common.TransactionNetworkObject(h)
		}
	}

	// We're looking for the block with heartbeats to figure out if we're in
	// it
	if len(s.chain.BlockHistory) == 1 {
		if _, ok := b.StorageMapping[s.chain.Host]; ok {
			//If we're in the block switch to signal mode
			s.chain.state = NewStateSwarmConnected()
		} else {
			//Join the swarm
			s.chain.state = NewStateSwarmJoin(s.chain)

		}
	}

	return s

}
<commit_msg>Stop blocking the stateinformed main loop on outgoing transactions<commit_after>package swarm

import (
	"common"
	"crypto/sha256"
	"log"
	"time"
)

// SwarmInformed - Swarm member shave been told to join swarm
type StateSwarmInformed struct {
	//Map of hosts seen to number of times they have failed to generate a block
	//Used for both host alive tracking & host block generation tracking
	hostsseen map[string]int

	// How many times we have broadcast that we are alive, we use a two stage
	// process where we broadcast, and then broadcast again when we have seen
	// enough nodes up to form a majority
	broadcastcount uint
	stage2         string

	// This state has two phases, the learning phase where it listens for new
	// hosts and then the commit stage where it listens for a block that
	// is correct according to its knowledge and then votes for it.
	learning bool

	heartbeats []*HeartBeatTransaction

	chain    *BlockChain
	blockgen <-chan time.Time

	sendBroadcast chan struct{}
	transaction   chan common.Transaction
	block         chan bwrap
	sync          chan struct{}
}

type bwrap struct {
	block *Block
	ret   chan State
}

func NewStateSwarmInformed(chain *BlockChain) (s *StateSwarmInformed) {
	s = new(StateSwarmInformed)
	s.chain = chain
	s.blockgen = time.Tick(1 * time.Second)

	s.learning = true
	s.hostsseen = make(map[string]int)
	s.sendBroadcast = make(chan struct{})
	s.transaction = make(chan common.Transaction)
	s.block = make(chan bwrap)
	s.sync = make(chan struct{})

	go s.broadcastLife()
	go s.mainloop()
	return
}

func (s *StateSwarmInformed) HandleTransaction(t common.Transaction) {
	log.Print("STATE: Transaction queed to be handled")
	s.transaction <- t
}

func (s *StateSwarmInformed) Sync() {
	<-s.sync
}

func (s *StateSwarmInformed) HandleBlock(b *Block) State {
	c := make(chan State)
	s.block <- bwrap{b, c}
	r := <-c
	return r
}

func (s *StateSwarmInformed) blockCompiler() (compiler string) {

	hosts := make([]string, 0, len(s.hostsseen))

	//Pull all hosts who we haven't seen skipping a block
	for host, skipped := range s.hostsseen {
		if skipped != 0 {
			continue
		}
		hosts = append(hosts, host)
	}

	//Check if we should be the block generator
	compiler = common.RendezvousHash(sha256.New(), hosts, s.chain.Host)
	return
}

func (s *StateSwarmInformed) mainloop() {

	var compiler string

	for {
		select {
		case _ = <-s.sendBroadcast:
			log.Print("STATE: NodeAlive Transaction to be Queed")
			s.broadcastcount += 1
			go func() {
				s.chain.outgoingTransactions <- common.TransactionNetworkObject(
					NewNodeAlive(s.chain.Host, s.chain.Id))
				log.Print("STATE: NodeAlive Transaction Queed")
			}()

		case s.sync <- struct{}{}:

		case t := <-s.transaction:
			log.Print("STATE: Transaction Recieved")
			s.handleTransaction(t)

		case o := <-s.block:
			n := s.handleBlock(o.block)
			o.ret <- n

		case _ = <-s.blockgen:
			log.Print("STATE: Blockgen Recieved")

			if s.learning {
				s.learning = false
				log.Print("STATE: Disable Learning")
			} else if len(s.chain.BlockHistory) == 0 {
				if compiler != "" {
					s.hostsseen[compiler] += 1
				}
			}

			//Dont't try to generate a block if we don't have a majority of hosts
			if len(s.hostsseen) < 3 {
				continue
				//Should actually switch to state swarmdied after a while
			}

			compiler = s.blockCompiler()

			if len(s.chain.BlockHistory) == 0 && compiler == s.chain.Host {

				id, err := common.RandomString(8)
				if err != nil {
					panic("checkBlockGenRandom" + err.Error())
				}
				b := &Block{id, s.chain.Id, s.chain.Host, nil, nil, nil}
				b.StorageMapping = make(map[string]interface{})
				for host, _ := range s.hostsseen {
					b.StorageMapping[host] = nil
				}

				s.heartbeats = s.heartbeats[0:0]

				s.chain.outgoingTransactions <- common.BlockNetworkObject(b)
				s.HandleBlock(b)
			}

			if len(s.chain.BlockHistory) == 1 && len(s.heartbeats) > 2 {

				id, err := common.RandomString(8)
				if err != nil {
					panic(err)
				}

				b := &Block{id, s.chain.Id, s.chain.Host, nil, nil, nil}
				b.StorageMapping = s.chain.BlockHistory[0].StorageMapping
				b.EntropyStage1 = make(map[string]string)
				for _, h := range s.heartbeats {
					b.EntropyStage1[h.Host] = h.Stage1
				}

				s.chain.outgoingTransactions <- common.BlockNetworkObject(b)
				s.HandleBlock(b)

			}
		}
		log.Print("STATE: Signal Handling Finished")
	}
}

func (s *StateSwarmInformed) broadcastLife() {
	s.sendBroadcast <- struct{}{}
}

func (s *StateSwarmInformed) handleTransaction(t common.Transaction) {
	switch n := t.(type) {
	case *NodeAlive:
		if !s.learning {
			return
		}

		s.hostsseen[n.Node] = 0
		// Resend hostsseen count once you have seen a majority of hosts
		if len(s.hostsseen) > 2 && s.broadcastcount < 2 {
			go s.broadcastLife()
		}

		log.Print("STATE: Node added")

	case *HeartBeatTransaction:

		//If we're learning this is too early
		if s.learning {
			return
		}

		// If we're not trying to compile we don't care
		if s.blockCompiler() != s.chain.Host {
			return
		}

		if n.Prevblock == s.chain.BlockHistory[0].Id {
			s.heartbeats = append(s.heartbeats, n)
		}

	default:
		return
	}
}

func (s *StateSwarmInformed) handleBlock(b *Block) State {

	// If the learning timeout hasn't expired, don't accept blocks
	if s.learning {
		return s
	}

	// All blocks in this state should be generated by the ideal host
	if b.Compiler != s.blockCompiler() {
		return s
	}

	// We are looking for a block to generate a heartbeat for
	if len(s.chain.BlockHistory) == 0 {
		s.chain.AddBlock(b)

		stage1, stage2 := common.HashedRandomData(sha256.New(), 8)
		s.stage2 = stage2

		if _, ok := b.StorageMapping[s.chain.Host]; ok {
			h := NewHeartBeat(s.chain.BlockHistory[0], s.chain.Host, stage1, "")
			s.chain.outgoingTransactions <- common.TransactionNetworkObject(h)
		}
	}

	// We're looking for the block with heartbeats to figure out if we're in
	// it
	if len(s.chain.BlockHistory) == 1 {
		if _, ok := b.StorageMapping[s.chain.Host]; ok {
			//If we're in the block switch to signal mode
			s.chain.state = NewStateSwarmConnected()
		} else {
			//Join the swarm
			s.chain.state = NewStateSwarmJoin(s.chain)

		}
	}

	return s

}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 meta

import (
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type fakeCodec struct{}

func (fakeCodec) Encode(runtime.Object) ([]byte, error) {
	return []byte{}, nil
}

func (fakeCodec) Decode([]byte) (runtime.Object, error) {
	return nil, nil
}

func (fakeCodec) DecodeInto([]byte, runtime.Object) error {
	return nil
}

type fakeConvertor struct{}

func (fakeConvertor) Convert(in, out interface{}) error {
	return nil
}

func (fakeConvertor) ConvertToVersion(in runtime.Object, _ string) (runtime.Object, error) {
	return in, nil
}

func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
	return label, value, nil
}

var validCodec = fakeCodec{}
var validAccessor = resourceAccessor{}
var validConvertor = fakeConvertor{}

func fakeInterfaces(version string) (*VersionInterfaces, bool) {
	return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, true
}

func unmatchedVersionInterfaces(version string) (*VersionInterfaces, bool) {
	return nil, false
}

func TestRESTMapperVersionAndKindForResource(t *testing.T) {
	testCases := []struct {
		Resource         string
		Kind, APIVersion string
		MixedCase        bool
		Err              bool
	}{
		{Resource: "internalobjec", Err: true},
		{Resource: "internalObjec", Err: true},

		{Resource: "internalobject", Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalobjects", Kind: "InternalObject", APIVersion: "test"},

		{Resource: "internalobject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalobjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},

		{Resource: "internalObject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
	}
	for i, testCase := range testCases {
		mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
		mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
		v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
			continue
		}
		if v != testCase.APIVersion || k != testCase.Kind {
			t.Errorf("%d: unexpected version and kind: %s %s", i, v, k)
		}
	}
}

func TestKindToResource(t *testing.T) {
	testCases := []struct {
		Kind             string
		MixedCase        bool
		Plural, Singular string
	}{
		{Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"},
		{Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"},
		{Kind: "Pod", MixedCase: false, Plural: "pods", Singular: "pod"},

		{Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
		{Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
		{Kind: "ReplicationController", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"},

		{Kind: "ImageRepository", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"},

		{Kind: "lowercase", MixedCase: false, Plural: "lowercases", Singular: "lowercase"},
		// Don't add extra s if the original object is already plural
		{Kind: "lowercases", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
	}
	for i, testCase := range testCases {
		plural, singular := kindToResource(testCase.Kind, testCase.MixedCase)
		if singular != testCase.Singular || plural != testCase.Plural {
			t.Errorf("%d: unexpected plural and singular: %s %s", i, plural, singular)
		}
	}
}

func TestRESTMapperRESTMapping(t *testing.T) {
	testCases := []struct {
		Kind            string
		APIVersions     []string
		MixedCase       bool
		DefaultVersions []string

		Resource string
		Version  string
		Err      bool
	}{
		{Kind: "Unknown", Err: true},
		{Kind: "InternalObject", Err: true},

		{DefaultVersions: []string{"test"}, Kind: "Unknown", Err: true},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{}, Resource: "internalobjects", Version: "test"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, MixedCase: true, Resource: "internalObjects"},

		// TODO: add test for a resource that exists in one version but not another
	}
	for i, testCase := range testCases {
		mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
		mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
		mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
		}
		if hasErr {
			continue
		}
		if mapping.Resource != testCase.Resource {
			t.Errorf("%d: unexpected resource: %#v", i, mapping)
		}
		version := testCase.Version
		if version == "" {
			version = testCase.APIVersions[0]
		}
		if mapping.APIVersion != version {
			t.Errorf("%d: unexpected version: %#v", i, mapping)
		}
		if mapping.Codec == nil || mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil {
			t.Errorf("%d: missing codec and accessor: %#v", i, mapping)
		}
	}
}

func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces)
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
	mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)

	// pick default matching object kind based on search order
	mapping, err := mapper.RESTMapping("OtherObject")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}

	mapping, err = mapper.RESTMapping("InternalObject")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if mapping.Resource != "internalobjects" || mapping.APIVersion != "test1" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}

	// mismatch of version
	mapping, err = mapper.RESTMapping("InternalObject", "test2")
	if err == nil {
		t.Errorf("unexpected non-error")
	}
	mapping, err = mapper.RESTMapping("OtherObject", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	// not in the search versions
	mapping, err = mapper.RESTMapping("OtherObject", "test3")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	// explicit search order
	mapping, err = mapper.RESTMapping("OtherObject", "test3", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	mapping, err = mapper.RESTMapping("OtherObject", "test3", "test2")
	if err != nil {
		t.Fatalf("unexpected non-error")
	}
	if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}
}

func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces)
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
	_, err := mapper.RESTMapping("InternalObject", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}
}
<commit_msg>Add DecodeIntoWithSpecifiedVersionKind() to Decoder interface. The function validate/default the body with the passed in apiVersion and Kind. It's called in createHandler and UpdateResource<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 meta

import (
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)

type fakeCodec struct{}

func (fakeCodec) Encode(runtime.Object) ([]byte, error) {
	return []byte{}, nil
}

func (fakeCodec) Decode([]byte) (runtime.Object, error) {
	return nil, nil
}

func (fakeCodec) DecodeInto([]byte, runtime.Object) error {
	return nil
}

func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, string, string) error {
	return nil
}

type fakeConvertor struct{}

func (fakeConvertor) Convert(in, out interface{}) error {
	return nil
}

func (fakeConvertor) ConvertToVersion(in runtime.Object, _ string) (runtime.Object, error) {
	return in, nil
}

func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
	return label, value, nil
}

var validCodec = fakeCodec{}
var validAccessor = resourceAccessor{}
var validConvertor = fakeConvertor{}

func fakeInterfaces(version string) (*VersionInterfaces, bool) {
	return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, true
}

func unmatchedVersionInterfaces(version string) (*VersionInterfaces, bool) {
	return nil, false
}

func TestRESTMapperVersionAndKindForResource(t *testing.T) {
	testCases := []struct {
		Resource         string
		Kind, APIVersion string
		MixedCase        bool
		Err              bool
	}{
		{Resource: "internalobjec", Err: true},
		{Resource: "internalObjec", Err: true},

		{Resource: "internalobject", Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalobjects", Kind: "InternalObject", APIVersion: "test"},

		{Resource: "internalobject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalobjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},

		{Resource: "internalObject", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
		{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
	}
	for i, testCase := range testCases {
		mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
		mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
		v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
			continue
		}
		if v != testCase.APIVersion || k != testCase.Kind {
			t.Errorf("%d: unexpected version and kind: %s %s", i, v, k)
		}
	}
}

func TestKindToResource(t *testing.T) {
	testCases := []struct {
		Kind             string
		MixedCase        bool
		Plural, Singular string
	}{
		{Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"},
		{Kind: "Pod", MixedCase: true, Plural: "pods", Singular: "pod"},
		{Kind: "Pod", MixedCase: false, Plural: "pods", Singular: "pod"},

		{Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
		{Kind: "ReplicationController", MixedCase: true, Plural: "replicationControllers", Singular: "replicationController"},
		{Kind: "ReplicationController", MixedCase: false, Plural: "replicationcontrollers", Singular: "replicationcontroller"},

		{Kind: "ImageRepository", MixedCase: true, Plural: "imageRepositories", Singular: "imageRepository"},

		{Kind: "lowercase", MixedCase: false, Plural: "lowercases", Singular: "lowercase"},
		// Don't add extra s if the original object is already plural
		{Kind: "lowercases", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
	}
	for i, testCase := range testCases {
		plural, singular := kindToResource(testCase.Kind, testCase.MixedCase)
		if singular != testCase.Singular || plural != testCase.Plural {
			t.Errorf("%d: unexpected plural and singular: %s %s", i, plural, singular)
		}
	}
}

func TestRESTMapperRESTMapping(t *testing.T) {
	testCases := []struct {
		Kind            string
		APIVersions     []string
		MixedCase       bool
		DefaultVersions []string

		Resource string
		Version  string
		Err      bool
	}{
		{Kind: "Unknown", Err: true},
		{Kind: "InternalObject", Err: true},

		{DefaultVersions: []string{"test"}, Kind: "Unknown", Err: true},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{}, Resource: "internalobjects", Version: "test"},

		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, Resource: "internalobjects"},
		{DefaultVersions: []string{"test"}, Kind: "InternalObject", APIVersions: []string{"test"}, MixedCase: true, Resource: "internalObjects"},

		// TODO: add test for a resource that exists in one version but not another
	}
	for i, testCase := range testCases {
		mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
		mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
		mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
		hasErr := err != nil
		if hasErr != testCase.Err {
			t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
		}
		if hasErr {
			continue
		}
		if mapping.Resource != testCase.Resource {
			t.Errorf("%d: unexpected resource: %#v", i, mapping)
		}
		version := testCase.Version
		if version == "" {
			version = testCase.APIVersions[0]
		}
		if mapping.APIVersion != version {
			t.Errorf("%d: unexpected version: %#v", i, mapping)
		}
		if mapping.Codec == nil || mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil {
			t.Errorf("%d: missing codec and accessor: %#v", i, mapping)
		}
	}
}

func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces)
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
	mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)

	// pick default matching object kind based on search order
	mapping, err := mapper.RESTMapping("OtherObject")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}

	mapping, err = mapper.RESTMapping("InternalObject")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if mapping.Resource != "internalobjects" || mapping.APIVersion != "test1" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}

	// mismatch of version
	mapping, err = mapper.RESTMapping("InternalObject", "test2")
	if err == nil {
		t.Errorf("unexpected non-error")
	}
	mapping, err = mapper.RESTMapping("OtherObject", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	// not in the search versions
	mapping, err = mapper.RESTMapping("OtherObject", "test3")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	// explicit search order
	mapping, err = mapper.RESTMapping("OtherObject", "test3", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}

	mapping, err = mapper.RESTMapping("OtherObject", "test3", "test2")
	if err != nil {
		t.Fatalf("unexpected non-error")
	}
	if mapping.Resource != "otherobjects" || mapping.APIVersion != "test2" {
		t.Errorf("unexpected mapping: %#v", mapping)
	}
}

func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
	mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces)
	mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
	_, err := mapper.RESTMapping("InternalObject", "test1")
	if err == nil {
		t.Errorf("unexpected non-error")
	}
}
<|endoftext|>


<commit_before>package datadog

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/atlassian/gostatsd"

	log "github.com/Sirupsen/logrus"
	"github.com/cenkalti/backoff"
	"github.com/spf13/viper"
	"golang.org/x/net/http2"
)

const (
	apiURL = "https://app.datadoghq.com"
	// BackendName is the name of this backend.
	BackendName                  = "datadog"
	dogstatsdVersion             = "5.6.3"
	dogstatsdUserAgent           = "python-requests/2.6.0 CPython/2.7.10"
	defaultMaxRequestElapsedTime = 15 * time.Second
	defaultClientTimeout         = 9 * time.Second
	// defaultMetricsPerBatch is the default number of metrics to send in a single batch.
	defaultMetricsPerBatch = 1000
	// maxResponseSize is the maximum response size we are willing to read.
	maxResponseSize = 10 * 1024
)

// Client represents a Datadog client.
type Client struct {
	apiKey                string
	apiEndpoint           string
	maxRequestElapsedTime time.Duration
	client                http.Client
	metricsPerBatch       uint
	now                   func() time.Time // Returns current time. Useful for testing.
}

// event represents an event data structure for Datadog.
type event struct {
	Title          string   `json:"title"`
	Text           string   `json:"text"`
	DateHappened   int64    `json:"date_happened,omitempty"`
	Hostname       string   `json:"host,omitempty"`
	AggregationKey string   `json:"aggregation_key,omitempty"`
	SourceTypeName string   `json:"source_type_name,omitempty"`
	Tags           []string `json:"tags,omitempty"`
	Priority       string   `json:"priority,omitempty"`
	AlertType      string   `json:"alert_type,omitempty"`
}

// SendMetricsAsync flushes the metrics to Datadog, preparing payload synchronously but doing the send asynchronously.
func (d *Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.MetricMap, cb gostatsd.SendCallback) {
	if metrics.NumStats == 0 {
		cb(nil)
		return
	}
	counter := 0
	results := make(chan error)
	d.processMetrics(metrics, func(ts *timeSeries) {
		go func() {
			err := d.postMetrics(ctx, ts)
			select {
			case <-ctx.Done():
			case results <- err:
			}
		}()
		counter++
	})
	go func() {
		errs := make([]error, 0, counter)
	loop:
		for c := 0; c < counter; c++ {
			select {
			case <-ctx.Done():
				errs = append(errs, ctx.Err())
				break loop
			case err := <-results:
				errs = append(errs, err)
			}
		}
		cb(errs)
	}()
}

func (d *Client) processMetrics(metrics *gostatsd.MetricMap, cb func(*timeSeries)) {
	fl := flush{
		ts: &timeSeries{
			Series: make([]metric, 0, d.metricsPerBatch),
		},
		timestamp:        float64(d.now().Unix()),
		flushIntervalSec: metrics.FlushInterval.Seconds(),
		metricsPerBatch:  d.metricsPerBatch,
		cb:               cb,
	}

	metrics.Counters.Each(func(key, tagsKey string, counter gostatsd.Counter) {
		fl.addMetric(rate, counter.PerSecond, counter.Hostname, counter.Tags, key)
		fl.addMetricf(gauge, float64(counter.Value), counter.Hostname, counter.Tags, "%s.count", key)
		fl.maybeFlush()
	})

	metrics.Timers.Each(func(key, tagsKey string, timer gostatsd.Timer) {
		fl.addMetricf(gauge, timer.Min, timer.Hostname, timer.Tags, "%s.lower", key)
		fl.addMetricf(gauge, timer.Max, timer.Hostname, timer.Tags, "%s.upper", key)
		fl.addMetricf(gauge, float64(timer.Count), timer.Hostname, timer.Tags, "%s.count", key)
		fl.addMetricf(rate, timer.PerSecond, timer.Hostname, timer.Tags, "%s.count_ps", key)
		fl.addMetricf(gauge, timer.Mean, timer.Hostname, timer.Tags, "%s.mean", key)
		fl.addMetricf(gauge, timer.Median, timer.Hostname, timer.Tags, "%s.median", key)
		fl.addMetricf(gauge, timer.StdDev, timer.Hostname, timer.Tags, "%s.std", key)
		fl.addMetricf(gauge, timer.Sum, timer.Hostname, timer.Tags, "%s.sum", key)
		fl.addMetricf(gauge, timer.SumSquares, timer.Hostname, timer.Tags, "%s.sum_squares", key)
		for _, pct := range timer.Percentiles {
			fl.addMetricf(gauge, pct.Float, timer.Hostname, timer.Tags, "%s.%s", key, pct.Str)
		}
		fl.maybeFlush()
	})

	metrics.Gauges.Each(func(key, tagsKey string, g gostatsd.Gauge) {
		fl.addMetric(gauge, g.Value, g.Hostname, g.Tags, key)
		fl.maybeFlush()
	})

	metrics.Sets.Each(func(key, tagsKey string, set gostatsd.Set) {
		fl.addMetric(gauge, float64(len(set.Values)), set.Hostname, set.Tags, key)
		fl.maybeFlush()
	})

	fl.finish()
}

func (d *Client) postMetrics(ctx context.Context, ts *timeSeries) error {
	return d.post(ctx, "/api/v1/series", "metrics", ts)
}

// SendEvent sends an event to Datadog.
func (d *Client) SendEvent(ctx context.Context, e *gostatsd.Event) error {
	return d.post(ctx, "/api/v1/events", "events", &event{
		Title:          e.Title,
		Text:           e.Text,
		DateHappened:   e.DateHappened,
		Hostname:       e.Hostname,
		AggregationKey: e.AggregationKey,
		SourceTypeName: e.SourceTypeName,
		Tags:           e.Tags,
		Priority:       e.Priority.StringWithEmptyDefault(),
		AlertType:      e.AlertType.StringWithEmptyDefault(),
	})
}

// Name returns the name of the backend.
func (d *Client) Name() string {
	return BackendName
}

func (d *Client) post(ctx context.Context, path, typeOfPost string, data interface{}) error {
	tsBytes, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("[%s] unable to marshal %s: %v", BackendName, typeOfPost, err)
	}
	log.Debugf("[%s] %s json: %s", BackendName, typeOfPost, tsBytes)

	b := backoff.NewExponentialBackOff()
	b.MaxElapsedTime = d.maxRequestElapsedTime
	authenticatedURL := d.authenticatedURL(path)
	post := d.constructPost(ctx, authenticatedURL, tsBytes)
	for {
		if err = post(); err == nil {
			return nil
		}

		next := b.NextBackOff()
		if next == backoff.Stop {
			return fmt.Errorf("[%s] %v", BackendName, err)
		}

		log.Warnf("[%s] failed to send %s, sleeping for %s: %v", BackendName, typeOfPost, next, err)

		timer := time.NewTimer(next)
		select {
		case <-ctx.Done():
			timer.Stop()
			return ctx.Err()
		case <-timer.C:
		}
	}
}

func (d *Client) constructPost(ctx context.Context, authenticatedURL string, body []byte) func() error {
	return func() error {
		req, err := http.NewRequest("POST", authenticatedURL, bytes.NewReader(body))
		if err != nil {
			return fmt.Errorf("unable to create http.Request: %v", err)
		}
		req = req.WithContext(ctx)
		req.Header.Set("Content-Type", "application/json")
		// Mimic dogstatsd code
		req.Header.Set("DD-Dogstatsd-Version", dogstatsdVersion)
		req.Header.Set("User-Agent", dogstatsdUserAgent)
		resp, err := d.client.Do(req)
		if err != nil {
			return fmt.Errorf("error POSTing: %s", strings.Replace(err.Error(), d.apiKey, "*****", -1))
		}
		defer resp.Body.Close()
		body := io.LimitReader(resp.Body, maxResponseSize)
		if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
			b, _ := ioutil.ReadAll(body)
			log.Infof("[%s] failed request status: %d\n%s", BackendName, resp.StatusCode, b)
			return fmt.Errorf("received bad status code %d", resp.StatusCode)
		}
		_, _ = io.Copy(ioutil.Discard, body)
		return nil
	}
}

func (d *Client) authenticatedURL(path string) string {
	q := url.Values{
		"api_key": []string{d.apiKey},
	}
	return fmt.Sprintf("%s%s?%s", d.apiEndpoint, path, q.Encode())
}

// NewClientFromViper returns a new Datadog API client.
func NewClientFromViper(v *viper.Viper) (gostatsd.Backend, error) {
	dd := getSubViper(v, "datadog")
	dd.SetDefault("api_endpoint", apiURL)
	dd.SetDefault("metrics_per_batch", defaultMetricsPerBatch)
	dd.SetDefault("client_timeout", defaultClientTimeout)
	dd.SetDefault("max_request_elapsed_time", defaultMaxRequestElapsedTime)
	return NewClient(
		dd.GetString("api_endpoint"),
		dd.GetString("api_key"),
		uint(dd.GetInt("metrics_per_batch")),
		dd.GetDuration("client_timeout"),
		dd.GetDuration("max_request_elapsed_time"),
	)
}

// NewClient returns a new Datadog API client.
func NewClient(apiEndpoint, apiKey string, metricsPerBatch uint, clientTimeout, maxRequestElapsedTime time.Duration) (*Client, error) {
	if apiEndpoint == "" {
		return nil, fmt.Errorf("[%s] apiEndpoint is required", BackendName)
	}
	if apiKey == "" {
		return nil, fmt.Errorf("[%s] apiKey is required", BackendName)
	}
	if metricsPerBatch <= 0 {
		return nil, fmt.Errorf("[%s] metricsPerBatch must be positive", BackendName)
	}
	if clientTimeout <= 0 {
		return nil, fmt.Errorf("[%s] clientTimeout must be positive", BackendName)
	}
	if maxRequestElapsedTime <= 0 {
		return nil, fmt.Errorf("[%s] maxRequestElapsedTime must be positive", BackendName)
	}
	log.Infof("[%s] maxRequestElapsedTime=%s clientTimeout=%s metricsPerBatch=%d", BackendName, maxRequestElapsedTime, clientTimeout, metricsPerBatch)
	transport := &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		TLSHandshakeTimeout: 3 * time.Second,
		TLSClientConfig: &tls.Config{
			// Can't use SSLv3 because of POODLE and BEAST
			// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
			// Can't use TLSv1.1 because of RC4 cipher usage
			MinVersion: tls.VersionTLS12,
		},
		DialContext: (&net.Dialer{
			Timeout:   5 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:    50,
		IdleConnTimeout: 1 * time.Minute,
	}
	if err := http2.ConfigureTransport(transport); err != nil {
		return nil, err
	}
	return &Client{
		apiKey:                apiKey,
		apiEndpoint:           apiEndpoint,
		maxRequestElapsedTime: maxRequestElapsedTime,
		client: http.Client{
			Transport: transport,
			Timeout:   clientTimeout,
		},
		metricsPerBatch: metricsPerBatch,
		now:             time.Now,
	}, nil
}

func getSubViper(v *viper.Viper, key string) *viper.Viper {
	n := v.Sub(key)
	if n == nil {
		n = viper.New()
	}
	return n
}
<commit_msg>Disable HTTP/2 support in Datadog backend client (#114)<commit_after>package datadog

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/atlassian/gostatsd"

	log "github.com/Sirupsen/logrus"
	"github.com/cenkalti/backoff"
	"github.com/spf13/viper"
)

const (
	apiURL = "https://app.datadoghq.com"
	// BackendName is the name of this backend.
	BackendName                  = "datadog"
	dogstatsdVersion             = "5.6.3"
	dogstatsdUserAgent           = "python-requests/2.6.0 CPython/2.7.10"
	defaultMaxRequestElapsedTime = 15 * time.Second
	defaultClientTimeout         = 9 * time.Second
	// defaultMetricsPerBatch is the default number of metrics to send in a single batch.
	defaultMetricsPerBatch = 1000
	// maxResponseSize is the maximum response size we are willing to read.
	maxResponseSize = 10 * 1024
)

// Client represents a Datadog client.
type Client struct {
	apiKey                string
	apiEndpoint           string
	maxRequestElapsedTime time.Duration
	client                http.Client
	metricsPerBatch       uint
	now                   func() time.Time // Returns current time. Useful for testing.
}

// event represents an event data structure for Datadog.
type event struct {
	Title          string   `json:"title"`
	Text           string   `json:"text"`
	DateHappened   int64    `json:"date_happened,omitempty"`
	Hostname       string   `json:"host,omitempty"`
	AggregationKey string   `json:"aggregation_key,omitempty"`
	SourceTypeName string   `json:"source_type_name,omitempty"`
	Tags           []string `json:"tags,omitempty"`
	Priority       string   `json:"priority,omitempty"`
	AlertType      string   `json:"alert_type,omitempty"`
}

// SendMetricsAsync flushes the metrics to Datadog, preparing payload synchronously but doing the send asynchronously.
func (d *Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.MetricMap, cb gostatsd.SendCallback) {
	if metrics.NumStats == 0 {
		cb(nil)
		return
	}
	counter := 0
	results := make(chan error)
	d.processMetrics(metrics, func(ts *timeSeries) {
		go func() {
			err := d.postMetrics(ctx, ts)
			select {
			case <-ctx.Done():
			case results <- err:
			}
		}()
		counter++
	})
	go func() {
		errs := make([]error, 0, counter)
	loop:
		for c := 0; c < counter; c++ {
			select {
			case <-ctx.Done():
				errs = append(errs, ctx.Err())
				break loop
			case err := <-results:
				errs = append(errs, err)
			}
		}
		cb(errs)
	}()
}

func (d *Client) processMetrics(metrics *gostatsd.MetricMap, cb func(*timeSeries)) {
	fl := flush{
		ts: &timeSeries{
			Series: make([]metric, 0, d.metricsPerBatch),
		},
		timestamp:        float64(d.now().Unix()),
		flushIntervalSec: metrics.FlushInterval.Seconds(),
		metricsPerBatch:  d.metricsPerBatch,
		cb:               cb,
	}

	metrics.Counters.Each(func(key, tagsKey string, counter gostatsd.Counter) {
		fl.addMetric(rate, counter.PerSecond, counter.Hostname, counter.Tags, key)
		fl.addMetricf(gauge, float64(counter.Value), counter.Hostname, counter.Tags, "%s.count", key)
		fl.maybeFlush()
	})

	metrics.Timers.Each(func(key, tagsKey string, timer gostatsd.Timer) {
		fl.addMetricf(gauge, timer.Min, timer.Hostname, timer.Tags, "%s.lower", key)
		fl.addMetricf(gauge, timer.Max, timer.Hostname, timer.Tags, "%s.upper", key)
		fl.addMetricf(gauge, float64(timer.Count), timer.Hostname, timer.Tags, "%s.count", key)
		fl.addMetricf(rate, timer.PerSecond, timer.Hostname, timer.Tags, "%s.count_ps", key)
		fl.addMetricf(gauge, timer.Mean, timer.Hostname, timer.Tags, "%s.mean", key)
		fl.addMetricf(gauge, timer.Median, timer.Hostname, timer.Tags, "%s.median", key)
		fl.addMetricf(gauge, timer.StdDev, timer.Hostname, timer.Tags, "%s.std", key)
		fl.addMetricf(gauge, timer.Sum, timer.Hostname, timer.Tags, "%s.sum", key)
		fl.addMetricf(gauge, timer.SumSquares, timer.Hostname, timer.Tags, "%s.sum_squares", key)
		for _, pct := range timer.Percentiles {
			fl.addMetricf(gauge, pct.Float, timer.Hostname, timer.Tags, "%s.%s", key, pct.Str)
		}
		fl.maybeFlush()
	})

	metrics.Gauges.Each(func(key, tagsKey string, g gostatsd.Gauge) {
		fl.addMetric(gauge, g.Value, g.Hostname, g.Tags, key)
		fl.maybeFlush()
	})

	metrics.Sets.Each(func(key, tagsKey string, set gostatsd.Set) {
		fl.addMetric(gauge, float64(len(set.Values)), set.Hostname, set.Tags, key)
		fl.maybeFlush()
	})

	fl.finish()
}

func (d *Client) postMetrics(ctx context.Context, ts *timeSeries) error {
	return d.post(ctx, "/api/v1/series", "metrics", ts)
}

// SendEvent sends an event to Datadog.
func (d *Client) SendEvent(ctx context.Context, e *gostatsd.Event) error {
	return d.post(ctx, "/api/v1/events", "events", &event{
		Title:          e.Title,
		Text:           e.Text,
		DateHappened:   e.DateHappened,
		Hostname:       e.Hostname,
		AggregationKey: e.AggregationKey,
		SourceTypeName: e.SourceTypeName,
		Tags:           e.Tags,
		Priority:       e.Priority.StringWithEmptyDefault(),
		AlertType:      e.AlertType.StringWithEmptyDefault(),
	})
}

// Name returns the name of the backend.
func (d *Client) Name() string {
	return BackendName
}

func (d *Client) post(ctx context.Context, path, typeOfPost string, data interface{}) error {
	tsBytes, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("[%s] unable to marshal %s: %v", BackendName, typeOfPost, err)
	}
	log.Debugf("[%s] %s json: %s", BackendName, typeOfPost, tsBytes)

	b := backoff.NewExponentialBackOff()
	b.MaxElapsedTime = d.maxRequestElapsedTime
	authenticatedURL := d.authenticatedURL(path)
	post := d.constructPost(ctx, authenticatedURL, tsBytes)
	for {
		if err = post(); err == nil {
			return nil
		}

		next := b.NextBackOff()
		if next == backoff.Stop {
			return fmt.Errorf("[%s] %v", BackendName, err)
		}

		log.Warnf("[%s] failed to send %s, sleeping for %s: %v", BackendName, typeOfPost, next, err)

		timer := time.NewTimer(next)
		select {
		case <-ctx.Done():
			timer.Stop()
			return ctx.Err()
		case <-timer.C:
		}
	}
}

func (d *Client) constructPost(ctx context.Context, authenticatedURL string, body []byte) func() error {
	return func() error {
		req, err := http.NewRequest("POST", authenticatedURL, bytes.NewReader(body))
		if err != nil {
			return fmt.Errorf("unable to create http.Request: %v", err)
		}
		req = req.WithContext(ctx)
		req.Header.Set("Content-Type", "application/json")
		// Mimic dogstatsd code
		req.Header.Set("DD-Dogstatsd-Version", dogstatsdVersion)
		req.Header.Set("User-Agent", dogstatsdUserAgent)
		resp, err := d.client.Do(req)
		if err != nil {
			return fmt.Errorf("error POSTing: %s", strings.Replace(err.Error(), d.apiKey, "*****", -1))
		}
		defer resp.Body.Close()
		body := io.LimitReader(resp.Body, maxResponseSize)
		if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
			b, _ := ioutil.ReadAll(body)
			log.Infof("[%s] failed request status: %d\n%s", BackendName, resp.StatusCode, b)
			return fmt.Errorf("received bad status code %d", resp.StatusCode)
		}
		_, _ = io.Copy(ioutil.Discard, body)
		return nil
	}
}

func (d *Client) authenticatedURL(path string) string {
	q := url.Values{
		"api_key": []string{d.apiKey},
	}
	return fmt.Sprintf("%s%s?%s", d.apiEndpoint, path, q.Encode())
}

// NewClientFromViper returns a new Datadog API client.
func NewClientFromViper(v *viper.Viper) (gostatsd.Backend, error) {
	dd := getSubViper(v, "datadog")
	dd.SetDefault("api_endpoint", apiURL)
	dd.SetDefault("metrics_per_batch", defaultMetricsPerBatch)
	dd.SetDefault("client_timeout", defaultClientTimeout)
	dd.SetDefault("max_request_elapsed_time", defaultMaxRequestElapsedTime)
	return NewClient(
		dd.GetString("api_endpoint"),
		dd.GetString("api_key"),
		uint(dd.GetInt("metrics_per_batch")),
		dd.GetDuration("client_timeout"),
		dd.GetDuration("max_request_elapsed_time"),
	)
}

// NewClient returns a new Datadog API client.
func NewClient(apiEndpoint, apiKey string, metricsPerBatch uint, clientTimeout, maxRequestElapsedTime time.Duration) (*Client, error) {
	if apiEndpoint == "" {
		return nil, fmt.Errorf("[%s] apiEndpoint is required", BackendName)
	}
	if apiKey == "" {
		return nil, fmt.Errorf("[%s] apiKey is required", BackendName)
	}
	if metricsPerBatch <= 0 {
		return nil, fmt.Errorf("[%s] metricsPerBatch must be positive", BackendName)
	}
	if clientTimeout <= 0 {
		return nil, fmt.Errorf("[%s] clientTimeout must be positive", BackendName)
	}
	if maxRequestElapsedTime <= 0 {
		return nil, fmt.Errorf("[%s] maxRequestElapsedTime must be positive", BackendName)
	}
	log.Infof("[%s] maxRequestElapsedTime=%s clientTimeout=%s metricsPerBatch=%d", BackendName, maxRequestElapsedTime, clientTimeout, metricsPerBatch)
	transport := &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		TLSHandshakeTimeout: 3 * time.Second,
		TLSClientConfig: &tls.Config{
			// Can't use SSLv3 because of POODLE and BEAST
			// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
			// Can't use TLSv1.1 because of RC4 cipher usage
			MinVersion: tls.VersionTLS12,
		},
		DialContext: (&net.Dialer{
			Timeout:   5 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:    50,
		IdleConnTimeout: 1 * time.Minute,
		// A non-nil empty map used in TLSNextProto to disable HTTP/2 support in client.
		// https://golang.org/doc/go1.6#http2
		TLSNextProto: map[string](func(string, *tls.Conn) http.RoundTripper){},
	}
	return &Client{
		apiKey:                apiKey,
		apiEndpoint:           apiEndpoint,
		maxRequestElapsedTime: maxRequestElapsedTime,
		client: http.Client{
			Transport: transport,
			Timeout:   clientTimeout,
		},
		metricsPerBatch: metricsPerBatch,
		now:             time.Now,
	}, nil
}

func getSubViper(v *viper.Viper, key string) *viper.Viper {
	n := v.Sub(key)
	if n == nil {
		n = viper.New()
	}
	return n
}
<|endoftext|>