<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"net/http"
	"net/mail"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	"code.google.com/p/google-api-go-client/compute/v1"
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/network"
	"github.com/juju/juju/provider/common"
)

const (
	driverScopes = "https://www.googleapis.com/auth/compute " +
		"https://www.googleapis.com/auth/devstorage.full_control"

	tokenURL = "https://accounts.google.com/o/oauth2/token"

	authURL = "https://accounts.google.com/o/oauth2/auth"

	partialMachineType = "zones/%s/machineTypes/%s"

	diskTypeScratch    = "SCRATCH"
	diskTypePersistent = "PERSISTENT"
	diskModeRW         = "READ_WRITE"
	diskModeRO         = "READ_ONLY"

	statusDone         = "DONE"
	statusDown         = "DOWN"
	statusPending      = "PENDING"
	statusProvisioning = "PROVISIONING"
	statusRunning      = "RUNNING"
	statusStaging      = "STAGING"
	statusStopped      = "STOPPED"
	statusStopping     = "STOPPING"
	statusTerminated   = "TERMINATED"
	statusUp           = "UP"

	operationTimeout = 60 // seconds

	// minDiskSize is the minimum/default size (in megabytes) for GCE
	// disks. GCE does not currently have a minimum disk size.
	minDiskSize int64 = 0
)

var (
	logger = loggo.GetLogger("juju.provider.gce")

	errNotImplemented = errors.NotImplementedf("gce provider")

	operationAttempts = utils.AttemptStrategy{
		Total: operationTimeout * time.Second,
		Delay: 10 * time.Second,
	}

	signedImageDataOnly = false
)

type gceAuth struct {
	clientID    string
	clientEmail string
	privateKey  []byte
}

func (ga gceAuth) validate() error {
	if ga.clientID == "" {
		return &config.InvalidConfigValue{Key: osEnvClientID}
	}
	if ga.clientEmail == "" {
		return &config.InvalidConfigValue{Key: osEnvClientEmail}
	} else if _, err := mail.ParseAddress(ga.clientEmail); err != nil {
		err = errors.Trace(err)
		return &config.InvalidConfigValue{osEnvClientEmail, ga.clientEmail, err}
	}
	if len(ga.privateKey) == 0 {
		return &config.InvalidConfigValue{Key: osEnvPrivateKey}
	}
	return nil
}

func (ga gceAuth) newTransport() (*oauth.Transport, error) {
	token, err := newToken(ga, driverScopes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	transport := oauth.Transport{
		Config: &oauth.Config{
			ClientId: ga.clientID,
			Scope:    driverScopes,
			TokenURL: tokenURL,
			AuthURL:  authURL,
		},
		Token: token,
	}
	return &transport, nil
}

var newToken = func(auth gceAuth, scopes string) (*oauth.Token, error) {
	jtok := jwt.NewToken(auth.clientEmail, scopes, auth.privateKey)
	jtok.ClaimSet.Aud = tokenURL

	token, err := jtok.Assert(&http.Client{})
	if err != nil {
		msg := "retrieving auth token for %s"
		return nil, errors.Annotatef(err, msg, auth.clientEmail)
	}
	return token, nil
}

func (ga *gceAuth) newConnection() (*compute.Service, error) {
	transport, err := ga.newTransport()
	if err != nil {
		return nil, errors.Trace(err)
	}
	service, err := newService(transport)
	return service, errors.Trace(err)
}

var newService = func(transport *oauth.Transport) (*compute.Service, error) {
	return compute.New(transport.Client())
}

type gceConnection struct {
	*compute.Service

	region    string
	projectID string
}

func (gce *gceConnection) validate() error {
	if gce.projectID == "" {
		return &config.InvalidConfigValue{Key: osEnvProjectID}
	}
	return nil
}

func (gce *gceConnection) connect(auth gceAuth) error {
	if gce.Service != nil {
		return errors.New("connect() failed (already connected)")
	}

	service, err := auth.newConnection()
	if err != nil {
		return errors.Trace(err)
	}

	gce.Service = service
	return nil
}

func (gce *gceConnection) verifyCredentials() error {
	call := gce.Projects.Get(gce.projectID)
	if _, err := call.Do(); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) waitOperation(operation *compute.Operation) error {
	opID := operation.ClientOperationId

	logger.Infof("GCE operation %q, waiting...", opID)
	for a := operationAttempts.Start(); a.Next(); {
		var err error
		if operation.Status == statusDone {
			break
		}
		call := gce.GlobalOperations.Get(gce.projectID, opID)
		operation, err = call.Do()
		if err != nil {
			return errors.Annotate(err, "waiting for operation to complete")
		}
	}
	if operation.Status != statusDone {
		msg := "timed out after %d seconds waiting for GCE operation to finish"
		return errors.Errorf(msg, operationTimeout)
	}
	if operation.Error != nil {
		for _, err := range operation.Error.Errors {
			logger.Errorf("GCE operation failed: (%s) %s", err.Code, err.Message)
		}
		return errors.Errorf("GCE operation %q failed", opID)
	}

	logger.Infof("GCE operation %q finished", opID)
	return nil
}

func (gce *gceConnection) instance(zone, id string) (*compute.Instance, error) {
	call := gce.Instances.Get(gce.projectID, zone, id)
	inst, err := call.Do()
	return inst, errors.Trace(err)
}

func (gce *gceConnection) addInstance(inst *compute.Instance, machineType string, zones []string) error {
	for _, zoneName := range zones {
		inst.MachineType = resolveMachineType(zoneName, machineType)
		call := gce.Instances.Insert(
			gce.projectID,
			zoneName,
			inst,
		)
		operation, err := call.Do()
		if err != nil {
			// We are guaranteed the insert failed at the point.
			return errors.Annotate(err, "sending new instance request")
		}
		waitErr := gce.waitOperation(operation)

		// Check if the instance was created.
		realized, err := gce.instance(zoneName, inst.Name)
		if err != nil {
			if waitErr == nil {
				return errors.Trace(err)
			}
			// Try the next zone.
			logger.Errorf("failed to get new instance in zone %q: %v", zoneName, waitErr)
			continue
		}

		// Success!
		*inst = *realized
		return nil
	}
	return errors.Errorf("not able to provision in any zone")
}

func (gce *gceConnection) instances(env environs.Environ) ([]*compute.Instance, error) {
	// env won't be nil.
	prefix := common.MachineFullName(env, "")

	// TODO(ericsnow) MaxResults arg defaults to 500... (call.MaxResults()).
	call := gce.Instances.AggregatedList(gce.projectID)
	call = call.Filter("name eq " + prefix + ".*")
	// TODO(ericsnow) If we can use multiple filters, filter on status here.
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var results []*compute.Instance
	for _, item := range raw.Items {
		for _, inst := range item.Instances {
			results = append(results, inst)
		}
	}
	return results, nil
}

func (gce *gceConnection) availabilityZones(region string) ([]*compute.Zone, error) {
	//TODO(wwtizel3) support paging requests if we receive a truncated result.
	call := gce.Zones.List(gce.projectID)
	if region != "" {
		call = call.Filter("name eq " + region + "-")
	}
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return raw.Items, nil
}

func (gce *gceConnection) removeInstance(id, zone string) error {
	call := gce.Instances.Delete(gce.projectID, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}

	// Clean up the instance's root disk.
	err = gce.removeDisk(id, zone)
	return errors.Trace(err)
}

func (gce *gceConnection) removeInstances(env environs.Environ, ids ...string) error {
	if len(ids) == 0 {
		return nil
	}

	instances, err := gce.instances(env)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", ids)
	}

	var failed []string
	for _, instID := range ids {
		for _, inst := range instances {
			if inst.Name == instID {
				if err := gce.removeInstance(instID, inst.Zone); err != nil {
					failed = append(failed, instID)
					logger.Errorf("while removing instance %q: %v", instID, err)
				}
				break
			}
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func (gce *gceConnection) removeDisk(id, zone string) error {
	call := gce.Disks.Delete(gce.projectId, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	err = gce.waitOperation(operation)
	return errors.Trace(err)
}

func (gce *gceConnection) firewall(name string) (*compute.Firewall, error) {
	call := gce.Firewalls.Get(gce.projectID, name)
	firewall, err := call.Do()
	if err != nil {
		return nil, errors.Annotate(err, "while getting firewall from GCE")
	}
	return firewall, nil
}

func (gce *gceConnection) setFirewall(name string, firewall *compute.Firewall) error {
	var err error
	var operation *compute.Operation
	if firewall == nil {
		call := gce.Firewalls.Delete(gce.projectID, name)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else if name == "" {
		call := gce.Firewalls.Insert(gce.projectID, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else {
		call := gce.Firewalls.Update(gce.projectID, name, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func filterInstances(instances []*compute.Instance, statuses ...string) []*compute.Instance {
	// TODO(ericsnow) Filter in-place?
	// TODO(ericsnow) Also filter metadata (or tags)? While highly
	// unlikely (due to our choice of instance ID), it is possible that
	// the filter in gce.instances() results in a false positive.
	// An additional filter on the metadata would address that
	// possibility.
	var results []*compute.Instance
	for _, inst := range instances {
		if !checkInstStatus(inst, statuses...) {
			continue
		}
		results = append(results, inst)
	}
	return results
}

func checkInstStatus(inst *compute.Instance, statuses ...string) bool {
	for _, status := range statuses {
		if inst.Status == status {
			return true
		}
	}
	return false
}

type diskSpec struct {
	// sizeHint is the requested disk size in Gigabytes.
	sizeHint int64
	imageURL string
	boot     bool
	scratch  bool
	readonly bool
}

func (ds *diskSpec) size() int64 {
	size := minDiskSize
	if ds.sizeHint >= minDiskSize {
		size = ds.sizeHint
	}
	return size
}

func (ds *diskSpec) newAttached() *compute.AttachedDisk {
	diskType := diskTypePersistent // The default.
	if ds.scratch {
		diskType = diskTypeScratch
	}
	mode := diskModeRW // The default.
	if ds.readonly {
		mode = diskModeRO
	}

	disk := compute.AttachedDisk{
		Type: diskType,
		Boot: ds.boot,
		Mode: mode,
		InitializeParams: &compute.AttachedDiskInitializeParams{
			// DiskName (defaults to instance name)
			DiskSizeGb: ds.size(),
			// DiskType (defaults to pd-standard, pd-ssd, local-ssd)
			SourceImage: ds.imageURL,
		},
		// Interface (defaults to SCSI)
		// DeviceName (GCE sets this, persistent disk only)
	}
	return &disk
}

// firewallSpec expands a port range set in to compute.FirewallAllowed
// and returns a compute.Firewall for the provided name.
func firewallSpec(name string, ps network.PortSet) *compute.Firewall {
	firewall := compute.Firewall{
		// Allowed is set below.
		// Description is not set.
		Name: name,
		// Network: (defaults to global)
		// SourceRanges is not set.
		// SourceTags is not set.
		// TargetTags is not set.
	}

	for _, protocol := range ps.Protocols() {
		allowed := compute.FirewallAllowed{
			IPProtocol: protocol,
			Ports:      ps.PortStrings(protocol),
		}
		firewall.Allowed = append(firewall.Allowed, &allowed)
	}
	return &firewall
}
<commit_msg>provider/gce: projectId -> projectID<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"net/http"
	"net/mail"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	"code.google.com/p/google-api-go-client/compute/v1"
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/network"
	"github.com/juju/juju/provider/common"
)

const (
	driverScopes = "https://www.googleapis.com/auth/compute " +
		"https://www.googleapis.com/auth/devstorage.full_control"

	tokenURL = "https://accounts.google.com/o/oauth2/token"

	authURL = "https://accounts.google.com/o/oauth2/auth"

	partialMachineType = "zones/%s/machineTypes/%s"

	diskTypeScratch    = "SCRATCH"
	diskTypePersistent = "PERSISTENT"
	diskModeRW         = "READ_WRITE"
	diskModeRO         = "READ_ONLY"

	statusDone         = "DONE"
	statusDown         = "DOWN"
	statusPending      = "PENDING"
	statusProvisioning = "PROVISIONING"
	statusRunning      = "RUNNING"
	statusStaging      = "STAGING"
	statusStopped      = "STOPPED"
	statusStopping     = "STOPPING"
	statusTerminated   = "TERMINATED"
	statusUp           = "UP"

	operationTimeout = 60 // seconds

	// minDiskSize is the minimum/default size (in megabytes) for GCE
	// disks. GCE does not currently have a minimum disk size.
	minDiskSize int64 = 0
)

var (
	logger = loggo.GetLogger("juju.provider.gce")

	errNotImplemented = errors.NotImplementedf("gce provider")

	operationAttempts = utils.AttemptStrategy{
		Total: operationTimeout * time.Second,
		Delay: 10 * time.Second,
	}

	signedImageDataOnly = false
)

type gceAuth struct {
	clientID    string
	clientEmail string
	privateKey  []byte
}

func (ga gceAuth) validate() error {
	if ga.clientID == "" {
		return &config.InvalidConfigValue{Key: osEnvClientID}
	}
	if ga.clientEmail == "" {
		return &config.InvalidConfigValue{Key: osEnvClientEmail}
	} else if _, err := mail.ParseAddress(ga.clientEmail); err != nil {
		err = errors.Trace(err)
		return &config.InvalidConfigValue{osEnvClientEmail, ga.clientEmail, err}
	}
	if len(ga.privateKey) == 0 {
		return &config.InvalidConfigValue{Key: osEnvPrivateKey}
	}
	return nil
}

func (ga gceAuth) newTransport() (*oauth.Transport, error) {
	token, err := newToken(ga, driverScopes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	transport := oauth.Transport{
		Config: &oauth.Config{
			ClientId: ga.clientID,
			Scope:    driverScopes,
			TokenURL: tokenURL,
			AuthURL:  authURL,
		},
		Token: token,
	}
	return &transport, nil
}

var newToken = func(auth gceAuth, scopes string) (*oauth.Token, error) {
	jtok := jwt.NewToken(auth.clientEmail, scopes, auth.privateKey)
	jtok.ClaimSet.Aud = tokenURL

	token, err := jtok.Assert(&http.Client{})
	if err != nil {
		msg := "retrieving auth token for %s"
		return nil, errors.Annotatef(err, msg, auth.clientEmail)
	}
	return token, nil
}

func (ga *gceAuth) newConnection() (*compute.Service, error) {
	transport, err := ga.newTransport()
	if err != nil {
		return nil, errors.Trace(err)
	}
	service, err := newService(transport)
	return service, errors.Trace(err)
}

var newService = func(transport *oauth.Transport) (*compute.Service, error) {
	return compute.New(transport.Client())
}

type gceConnection struct {
	*compute.Service

	region    string
	projectID string
}

func (gce *gceConnection) validate() error {
	if gce.projectID == "" {
		return &config.InvalidConfigValue{Key: osEnvProjectID}
	}
	return nil
}

func (gce *gceConnection) connect(auth gceAuth) error {
	if gce.Service != nil {
		return errors.New("connect() failed (already connected)")
	}

	service, err := auth.newConnection()
	if err != nil {
		return errors.Trace(err)
	}

	gce.Service = service
	return nil
}

func (gce *gceConnection) verifyCredentials() error {
	call := gce.Projects.Get(gce.projectID)
	if _, err := call.Do(); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) waitOperation(operation *compute.Operation) error {
	opID := operation.ClientOperationId

	logger.Infof("GCE operation %q, waiting...", opID)
	for a := operationAttempts.Start(); a.Next(); {
		var err error
		if operation.Status == statusDone {
			break
		}
		call := gce.GlobalOperations.Get(gce.projectID, opID)
		operation, err = call.Do()
		if err != nil {
			return errors.Annotate(err, "waiting for operation to complete")
		}
	}
	if operation.Status != statusDone {
		msg := "timed out after %d seconds waiting for GCE operation to finish"
		return errors.Errorf(msg, operationTimeout)
	}
	if operation.Error != nil {
		for _, err := range operation.Error.Errors {
			logger.Errorf("GCE operation failed: (%s) %s", err.Code, err.Message)
		}
		return errors.Errorf("GCE operation %q failed", opID)
	}

	logger.Infof("GCE operation %q finished", opID)
	return nil
}

func (gce *gceConnection) instance(zone, id string) (*compute.Instance, error) {
	call := gce.Instances.Get(gce.projectID, zone, id)
	inst, err := call.Do()
	return inst, errors.Trace(err)
}

func (gce *gceConnection) addInstance(inst *compute.Instance, machineType string, zones []string) error {
	for _, zoneName := range zones {
		inst.MachineType = resolveMachineType(zoneName, machineType)
		call := gce.Instances.Insert(
			gce.projectID,
			zoneName,
			inst,
		)
		operation, err := call.Do()
		if err != nil {
			// We are guaranteed the insert failed at the point.
			return errors.Annotate(err, "sending new instance request")
		}
		waitErr := gce.waitOperation(operation)

		// Check if the instance was created.
		realized, err := gce.instance(zoneName, inst.Name)
		if err != nil {
			if waitErr == nil {
				return errors.Trace(err)
			}
			// Try the next zone.
			logger.Errorf("failed to get new instance in zone %q: %v", zoneName, waitErr)
			continue
		}

		// Success!
		*inst = *realized
		return nil
	}
	return errors.Errorf("not able to provision in any zone")
}

func (gce *gceConnection) instances(env environs.Environ) ([]*compute.Instance, error) {
	// env won't be nil.
	prefix := common.MachineFullName(env, "")

	// TODO(ericsnow) MaxResults arg defaults to 500... (call.MaxResults()).
	call := gce.Instances.AggregatedList(gce.projectID)
	call = call.Filter("name eq " + prefix + ".*")
	// TODO(ericsnow) If we can use multiple filters, filter on status here.
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var results []*compute.Instance
	for _, item := range raw.Items {
		for _, inst := range item.Instances {
			results = append(results, inst)
		}
	}
	return results, nil
}

func (gce *gceConnection) availabilityZones(region string) ([]*compute.Zone, error) {
	//TODO(wwtizel3) support paging requests if we receive a truncated result.
	call := gce.Zones.List(gce.projectID)
	if region != "" {
		call = call.Filter("name eq " + region + "-")
	}
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return raw.Items, nil
}

func (gce *gceConnection) removeInstance(id, zone string) error {
	call := gce.Instances.Delete(gce.projectID, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}

	// Clean up the instance's root disk.
	err = gce.removeDisk(id, zone)
	return errors.Trace(err)
}

func (gce *gceConnection) removeInstances(env environs.Environ, ids ...string) error {
	if len(ids) == 0 {
		return nil
	}

	instances, err := gce.instances(env)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", ids)
	}

	var failed []string
	for _, instID := range ids {
		for _, inst := range instances {
			if inst.Name == instID {
				if err := gce.removeInstance(instID, inst.Zone); err != nil {
					failed = append(failed, instID)
					logger.Errorf("while removing instance %q: %v", instID, err)
				}
				break
			}
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func (gce *gceConnection) removeDisk(id, zone string) error {
	call := gce.Disks.Delete(gce.projectID, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	err = gce.waitOperation(operation)
	return errors.Trace(err)
}

func (gce *gceConnection) firewall(name string) (*compute.Firewall, error) {
	call := gce.Firewalls.Get(gce.projectID, name)
	firewall, err := call.Do()
	if err != nil {
		return nil, errors.Annotate(err, "while getting firewall from GCE")
	}
	return firewall, nil
}

func (gce *gceConnection) setFirewall(name string, firewall *compute.Firewall) error {
	var err error
	var operation *compute.Operation
	if firewall == nil {
		call := gce.Firewalls.Delete(gce.projectID, name)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else if name == "" {
		call := gce.Firewalls.Insert(gce.projectID, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else {
		call := gce.Firewalls.Update(gce.projectID, name, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func filterInstances(instances []*compute.Instance, statuses ...string) []*compute.Instance {
	// TODO(ericsnow) Filter in-place?
	// TODO(ericsnow) Also filter metadata (or tags)? While highly
	// unlikely (due to our choice of instance ID), it is possible that
	// the filter in gce.instances() results in a false positive.
	// An additional filter on the metadata would address that
	// possibility.
	var results []*compute.Instance
	for _, inst := range instances {
		if !checkInstStatus(inst, statuses...) {
			continue
		}
		results = append(results, inst)
	}
	return results
}

func checkInstStatus(inst *compute.Instance, statuses ...string) bool {
	for _, status := range statuses {
		if inst.Status == status {
			return true
		}
	}
	return false
}

type diskSpec struct {
	// sizeHint is the requested disk size in Gigabytes.
	sizeHint int64
	imageURL string
	boot     bool
	scratch  bool
	readonly bool
}

func (ds *diskSpec) size() int64 {
	size := minDiskSize
	if ds.sizeHint >= minDiskSize {
		size = ds.sizeHint
	}
	return size
}

func (ds *diskSpec) newAttached() *compute.AttachedDisk {
	diskType := diskTypePersistent // The default.
	if ds.scratch {
		diskType = diskTypeScratch
	}
	mode := diskModeRW // The default.
	if ds.readonly {
		mode = diskModeRO
	}

	disk := compute.AttachedDisk{
		Type: diskType,
		Boot: ds.boot,
		Mode: mode,
		InitializeParams: &compute.AttachedDiskInitializeParams{
			// DiskName (defaults to instance name)
			DiskSizeGb: ds.size(),
			// DiskType (defaults to pd-standard, pd-ssd, local-ssd)
			SourceImage: ds.imageURL,
		},
		// Interface (defaults to SCSI)
		// DeviceName (GCE sets this, persistent disk only)
	}
	return &disk
}

// firewallSpec expands a port range set in to compute.FirewallAllowed
// and returns a compute.Firewall for the provided name.
func firewallSpec(name string, ps network.PortSet) *compute.Firewall {
	firewall := compute.Firewall{
		// Allowed is set below.
		// Description is not set.
		Name: name,
		// Network: (defaults to global)
		// SourceRanges is not set.
		// SourceTags is not set.
		// TargetTags is not set.
	}

	for _, protocol := range ps.Protocols() {
		allowed := compute.FirewallAllowed{
			IPProtocol: protocol,
			Ports:      ps.PortStrings(protocol),
		}
		firewall.Allowed = append(firewall.Allowed, &allowed)
	}
	return &firewall
}
<|endoftext|>


<commit_before>package providers

import (
	"github.com/stretchr/gomniauth/common"
	"github.com/stretchr/gomniauth/oauth2"
	"github.com/stretchr/stew/objects"
	"strings"
)

type GithubProvider struct {
	config objects.Map
}

func (p *GithubProvider) Name() string {
	return "GitHub"
}

func (p *GithubProvider) Config() objects.Map {

	if p.config == nil {
		p.config = objects.NewMap(
			"authURL", "https://github.com/login/oauth/authorize",
			"tokenURL", "https://github.com/login/oauth/access_token",
		)
	}

	return p.config

}

func (p *GithubProvider) AuthType() common.AuthType {
	return common.AuthTypeOAuth2
}

var Github oauth2.ProviderFunc = func(clientId, clientSecret, redirectURL string, scope ...string) common.Provider {
	p := new(GithubProvider)
	p.Config().
		Set("clientId", clientId).
		Set("clientSecret", clientSecret).
		Set("redirectURL", redirectURL).
		Set("scope", strings.Join(scope, ","))
	return p
}
<commit_msg>Fix typo<commit_after>package providers

import (
	"github.com/stretchr/gomniauth/common"
	"github.com/stretchr/gomniauth/oauth2"
	"github.com/stretchr/stew/objects"
	"strings"
)

type GithubProvider struct {
	config objects.Map
}

func (p *GithubProvider) Name() string {
	return "Github"
}

func (p *GithubProvider) Config() objects.Map {

	if p.config == nil {
		p.config = objects.NewMap(
			"authURL", "https://github.com/login/oauth/authorize",
			"tokenURL", "https://github.com/login/oauth/access_token",
		)
	}

	return p.config

}

func (p *GithubProvider) AuthType() common.AuthType {
	return common.AuthTypeOAuth2
}

var Github oauth2.ProviderFunc = func(clientId, clientSecret, redirectURL string, scope ...string) common.Provider {
	p := new(GithubProvider)
	p.Config().
		Set("clientId", clientId).
		Set("clientSecret", clientSecret).
		Set("redirectURL", redirectURL).
		Set("scope", strings.Join(scope, ","))
	return p
}
<|endoftext|>


<commit_before>package termite
import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"syscall"
)
var _ = log.Println
type splicePair struct {
	r, w *os.File
	size int
}
var splicePairs chan *splicePair

var pipeMaxSize = 1 << 20
func init() {
	splicePairs = make(chan *splicePair, 100)
	content, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size")
	if err != nil {
		return
	}
	fmt.Sscan(string(content), &pipeMaxSize)
	log.Println("pipemax", pipeMaxSize)
}

// copy & paste from syscall.
func fcntl(fd int, cmd int, arg int) (val int, errno int) {
        r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
        val = int(r0)
        errno = int(e1)
        return
}

const F_SETPIPE_SZ = 1031
const F_GETPIPE_SZ = 1032

func (me *splicePair) MaxGrow() {
	for me.Grow(2*me.size) {
	}
}

func (me *splicePair) Grow(n int) bool {
	if n > pipeMaxSize {
		return false
	}
	if n <= me.size {
		return true
	}
	newsize := me.size
	for newsize < n {
		newsize *= 2
	}

	newsize, errNo := fcntl(me.r.Fd(), F_SETPIPE_SZ, newsize)
	if errNo != 0 {
		log.Println(os.NewSyscallError("fnct", errNo))
		return false
	}
	me.size = newsize
	return true
}

func (me *splicePair) Close() os.Error {
	err1 := me.r.Close()
	err2 := me.w.Close()
	if err1 != nil {
		return err1
	}
	return err2
}

func newSplicePair() *splicePair {
	me := &splicePair{}
	var err os.Error
	me.r, me.w, err = os.Pipe()
	if err != nil {
		return nil
	}
	errNo := 0
	me.size, errNo = fcntl(me.r.Fd(), F_GETPIPE_SZ, 0)
	if errNo != 0 {
		return nil
	}
	_, errR := fcntl(me.r.Fd(), syscall.F_SETFL, os.O_NONBLOCK)
	_, errW := fcntl(me.w.Fd(), syscall.F_SETFL, os.O_NONBLOCK)
	if errR != 0 || errW != 0 {
		me.Close()
		return nil
	}
	return me
}

func getSplice() (p *splicePair) {
	select {
	case p = <-splicePairs:
		// already done.
        default:
		p = newSplicePair()
        }
	return newSplicePair()
}

func returnSplice(p *splicePair) {
	if p != nil {
		splicePairs <- p
	}
}

func SpliceCopy(dst *os.File, src *os.File, p *splicePair) (int64, os.Error) {
	total := int64(0)

	for {
		n, errNo := syscall.Splice(src.Fd(), nil, p.w.Fd(), nil, p.size, 0)
		if errNo != 0 {
			return total, os.NewSyscallError("Splice", errNo)
		}
		if n == 0 {
			break
		}
		m, errNo := syscall.Splice(p.r.Fd(), nil, dst.Fd(), nil, int(n), 0)
		if errNo != 0 {
			return total, os.NewSyscallError("Splice", errNo)
		}
		if m < n {
			panic("m<n")
		}
		total += m
		if int(n) < p.size {
			break
		}
	}

	return total, nil
}

// Argument ordering follows io.Copy.
func CopyFile(dstName string, srcName string, mode int) os.Error {
	src, err := os.Open(srcName)
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, uint32(mode))
	if err != nil {
		return err
	}
	defer dst.Close()
	p := getSplice()
	if p != nil {
		p.Grow(256*1024)
		_, err := SpliceCopy(dst, src, p)
		returnSplice(p)
		return err
	} else {
		_, err = io.Copy(dst, src)
	}
	if err == os.EOF {
		err = nil
	}
	return err
}
<commit_msg>Drop pipemax print.<commit_after>package termite
import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"syscall"
)
var _ = log.Println
type splicePair struct {
	r, w *os.File
	size int
}
var splicePairs chan *splicePair

var pipeMaxSize = 1 << 20
func init() {
	splicePairs = make(chan *splicePair, 100)
	content, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size")
	if err != nil {
		return
	}
	fmt.Sscan(string(content), &pipeMaxSize)
}

// copy & paste from syscall.
func fcntl(fd int, cmd int, arg int) (val int, errno int) {
        r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
        val = int(r0)
        errno = int(e1)
        return
}

const F_SETPIPE_SZ = 1031
const F_GETPIPE_SZ = 1032

func (me *splicePair) MaxGrow() {
	for me.Grow(2*me.size) {
	}
}

func (me *splicePair) Grow(n int) bool {
	if n > pipeMaxSize {
		return false
	}
	if n <= me.size {
		return true
	}
	newsize := me.size
	for newsize < n {
		newsize *= 2
	}

	newsize, errNo := fcntl(me.r.Fd(), F_SETPIPE_SZ, newsize)
	if errNo != 0 {
		log.Println(os.NewSyscallError("fnct", errNo))
		return false
	}
	me.size = newsize
	return true
}

func (me *splicePair) Close() os.Error {
	err1 := me.r.Close()
	err2 := me.w.Close()
	if err1 != nil {
		return err1
	}
	return err2
}

func newSplicePair() *splicePair {
	me := &splicePair{}
	var err os.Error
	me.r, me.w, err = os.Pipe()
	if err != nil {
		return nil
	}
	errNo := 0
	me.size, errNo = fcntl(me.r.Fd(), F_GETPIPE_SZ, 0)
	if errNo != 0 {
		return nil
	}
	_, errR := fcntl(me.r.Fd(), syscall.F_SETFL, os.O_NONBLOCK)
	_, errW := fcntl(me.w.Fd(), syscall.F_SETFL, os.O_NONBLOCK)
	if errR != 0 || errW != 0 {
		me.Close()
		return nil
	}
	return me
}

func getSplice() (p *splicePair) {
	select {
	case p = <-splicePairs:
		// already done.
        default:
		p = newSplicePair()
        }
	return newSplicePair()
}

func returnSplice(p *splicePair) {
	if p != nil {
		splicePairs <- p
	}
}

func SpliceCopy(dst *os.File, src *os.File, p *splicePair) (int64, os.Error) {
	total := int64(0)

	for {
		n, errNo := syscall.Splice(src.Fd(), nil, p.w.Fd(), nil, p.size, 0)
		if errNo != 0 {
			return total, os.NewSyscallError("Splice", errNo)
		}
		if n == 0 {
			break
		}
		m, errNo := syscall.Splice(p.r.Fd(), nil, dst.Fd(), nil, int(n), 0)
		if errNo != 0 {
			return total, os.NewSyscallError("Splice", errNo)
		}
		if m < n {
			panic("m<n")
		}
		total += m
		if int(n) < p.size {
			break
		}
	}

	return total, nil
}

// Argument ordering follows io.Copy.
func CopyFile(dstName string, srcName string, mode int) os.Error {
	src, err := os.Open(srcName)
	if err != nil {
		return err
	}
	defer src.Close()

	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, uint32(mode))
	if err != nil {
		return err
	}
	defer dst.Close()
	p := getSplice()
	if p != nil {
		p.Grow(256*1024)
		_, err := SpliceCopy(dst, src, p)
		returnSplice(p)
		return err
	} else {
		_, err = io.Copy(dst, src)
	}
	if err == os.EOF {
		err = nil
	}
	return err
}
<|endoftext|>


<commit_before>package delayd2

import (
	"database/sql"
	"errors"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/lib/pq"
	"github.com/nabeken/aws-go-sqs/queue"
	"github.com/nabeken/aws-go-sqs/queue/option"
	"github.com/pborman/uuid"
)

var ErrInvalidAttributes = errors.New("delayd2: invalid attributes")

const (
	sqsMessageDurationKey = "delayd2-delay"
	sqsMessageRelayToKey  = "delayd2-target"
)

type SQSConsumer struct {
	workerID string
	db       *sql.DB
	queue    *queue.Queue
}

func NewSQSConsumer(workerID string, db *sql.DB, svc *sqs.SQS, queueName string) (*SQSConsumer, error) {
	q, err := queue.New(svc, queueName)
	if err != nil {
		return nil, err
	}
	return &SQSConsumer{
		workerID: workerID,
		db:    db,
		queue: q,
	}, nil
}

func (c *SQSConsumer) Enqueue(delay int64, relayTo string, payload []byte) error {
	releaseAt := time.Now().Add(time.Duration(delay) * time.Second)
	queueId := uuid.New()
	_, err := c.db.Exec(`
		INSERT
		INTO
		  queue
		VALUES
			($1, $2, $3, $4, $5)
	;`, queueId, c.workerID, releaseAt, relayTo, payload)
	return err
}

func (c *SQSConsumer) ConsumeMessages() error {
	messages, err := c.queue.ReceiveMessage(
		option.MaxNumberOfMessages(10),
		option.UseAllAttribute(),
	)
	if err != nil {
		return err
	}
	for _, m := range messages {
		duration, relayTo, err := extractDelayd2MessageAttributes(m)
		if err != nil {
			// TODO: log
			continue
		}
		if err := c.Enqueue(duration, relayTo, []byte(*m.Body)); err != nil {
			if perr, ok := err.(*pq.Error); ok && perr.Code.Class() == "23" {
				// delete immediately if duplicated
				c.queue.DeleteMessage(m.ReceiptHandle)
			}
			// TODO: log
			continue
		}
		c.queue.DeleteMessage(m.ReceiptHandle)
	}
	return nil
}

func extractDelayd2MessageAttributes(message *sqs.Message) (int64, string, error) {
	var duration int64
	var relayTo string
	var err error

	durationAttr, found := message.MessageAttributes[sqsMessageDurationKey]
	if !found || *durationAttr.DataType != option.DataTypeNumber {
		return 0, "", ErrInvalidAttributes
	}
	duration, err = strconv.ParseInt(*durationAttr.StringValue, 10, 64)
	if err != nil {
		return 0, "", err
	}

	relayToAttr, found := message.MessageAttributes[sqsMessageRelayToKey]
	if !found || *relayToAttr.DataType != option.DataTypeString {
		return 0, "", ErrInvalidAttributes
	}
	relayTo = *relayToAttr.StringValue

	return duration, relayTo, nil
}
<commit_msg>gofmt<commit_after>package delayd2

import (
	"database/sql"
	"errors"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/lib/pq"
	"github.com/nabeken/aws-go-sqs/queue"
	"github.com/nabeken/aws-go-sqs/queue/option"
	"github.com/pborman/uuid"
)

var ErrInvalidAttributes = errors.New("delayd2: invalid attributes")

const (
	sqsMessageDurationKey = "delayd2-delay"
	sqsMessageRelayToKey  = "delayd2-target"
)

type SQSConsumer struct {
	workerID string
	db       *sql.DB
	queue    *queue.Queue
}

func NewSQSConsumer(workerID string, db *sql.DB, svc *sqs.SQS, queueName string) (*SQSConsumer, error) {
	q, err := queue.New(svc, queueName)
	if err != nil {
		return nil, err
	}
	return &SQSConsumer{
		workerID: workerID,
		db:       db,
		queue:    q,
	}, nil
}

func (c *SQSConsumer) Enqueue(delay int64, relayTo string, payload []byte) error {
	releaseAt := time.Now().Add(time.Duration(delay) * time.Second)
	queueId := uuid.New()
	_, err := c.db.Exec(`
		INSERT
		INTO
		  queue
		VALUES
			($1, $2, $3, $4, $5)
	;`, queueId, c.workerID, releaseAt, relayTo, payload)
	return err
}

func (c *SQSConsumer) ConsumeMessages() error {
	messages, err := c.queue.ReceiveMessage(
		option.MaxNumberOfMessages(10),
		option.UseAllAttribute(),
	)
	if err != nil {
		return err
	}
	for _, m := range messages {
		duration, relayTo, err := extractDelayd2MessageAttributes(m)
		if err != nil {
			// TODO: log
			continue
		}
		if err := c.Enqueue(duration, relayTo, []byte(*m.Body)); err != nil {
			if perr, ok := err.(*pq.Error); ok && perr.Code.Class() == "23" {
				// delete immediately if duplicated
				c.queue.DeleteMessage(m.ReceiptHandle)
			}
			// TODO: log
			continue
		}
		c.queue.DeleteMessage(m.ReceiptHandle)
	}
	return nil
}

func extractDelayd2MessageAttributes(message *sqs.Message) (int64, string, error) {
	var duration int64
	var relayTo string
	var err error

	durationAttr, found := message.MessageAttributes[sqsMessageDurationKey]
	if !found || *durationAttr.DataType != option.DataTypeNumber {
		return 0, "", ErrInvalidAttributes
	}
	duration, err = strconv.ParseInt(*durationAttr.StringValue, 10, 64)
	if err != nil {
		return 0, "", err
	}

	relayToAttr, found := message.MessageAttributes[sqsMessageRelayToKey]
	if !found || *relayToAttr.DataType != option.DataTypeString {
		return 0, "", ErrInvalidAttributes
	}
	relayTo = *relayToAttr.StringValue

	return duration, relayTo, nil
}
<|endoftext|>


<commit_before>// With many thanks to http://www.kfish.org/boids/pseudocode.html

package main

import (
	rtree "github.com/dhconnelly/rtreego"
	"math"
	"math/rand"
)

type Boid struct {
	ID       int         `json:"id"`
	Point    rtree.Point `json:"position"`
	Velocity []float64   `json:"-"`
}

func NewBoid(id, dimensions int) *Boid {
	var point rtree.Point
	var vel []float64

	for i := 0; i < dimensions; i++ {
		point = append(point, 0.0)
		vel = append(vel, 0.0)
	}

	return &Boid{
		ID:       id,
		Point:    point,
		Velocity: vel,
	}
}

func (b *Boid) RandomizePosition(area *Area) {
	var point rtree.Point

	for i := range area.Dimensions {
		p := rand.Int31n(area.Dimensions[i])
		point = append(point, float64(p))
	}

	b.Point = point
}

func (b *Boid) Bounds() *rtree.Rect {
	return b.Point.ToRect(0.01)
}

func (b *Boid) UpdateVelocity(area *Area) {
	v1 := b.Rule1(area)
	v2 := b.Rule2(area)
	v3 := b.Rule3(area)

	b.Velocity = AddFloats(b.Velocity, v1, v2, v3)
}

func (b *Boid) UpdatePosition() {
	b.Point = AddFloats(b.Point, b.Velocity)
}

// Rule1
//
// 	PROCEDURE Rule1(boid bJ)
//
//		Vector pcJ
//
//    FOR EACH BOID b
//    	IF b != bJ THEN
//    		pcJ = pcJ + b.position
//    	END IF
//    END
//
//    pcJ = pcJ / N-1
//
//    RETURN (pcJ - bJ.position) / 100
//
// 	END PROCEDURE
func (b *Boid) Rule1(area *Area) []float64 {
	pcJ := make(rtree.Point, len(b.Point))
	for i := range pcJ {
		pcJ[i] = 0
	}

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Point {
				pcJ[k] = pcJ[k] + v
			}
		}
	}

	for i := range pcJ {
		pcJ[i] = pcJ[i] / float64(len(boids)-1)
	}

	subbed := SubFloats(pcJ, b.Point)
	divved := DivFloat(subbed, 100.0)
	return divved
}

// Rule2
//
// PROCEDURE rule2(boid bJ)
//
// 	Vector c = 0;
//
// 	FOR EACH BOID b
// 		IF b != bJ THEN
// 			IF |b.position - bJ.position| < 100 THEN
// 				c = c - (b.position - bJ.position)
// 			END IF
// 		END IF
// 	END
//
// 	RETURN c
//
// END PROCEDURE
func (b *Boid) Rule2(area *Area) []float64 {
	vector := makeFloats(int32(len(b.Point)))

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Point {
				if math.Abs(v-b.Point[k]) < 10 {
					vector[k] = vector[k] - (v - b.Point[k])
				}
			}
		}
	}

	return vector
}

// Rule3
//
// PROCEDURE rule3(boid bJ)
//
// 	Vector pvJ
//
// 	FOR EACH BOID b
// 		IF b != bJ THEN
// 			pvJ = pvJ + b.velocity
// 		END IF
// 	END
//
// 	pvJ = pvJ / N-1
//
// 	RETURN (pvJ - bJ.velocity) / 8
//
// END PROCEDURE
func (b *Boid) Rule3(area *Area) []float64 {
	pvJ := makeFloats(int32(len(b.Velocity)))

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Velocity {
				pvJ[k] = pvJ[k] + v
			}
		}
	}

	for i := range pvJ {
		pvJ[i] = pvJ[i] / float64(len(boids)-1)
	}

	subbed := SubFloats(pvJ, b.Velocity)
	divved := DivFloat(subbed, 8.0)
	return divved
}

func SpatialsToBoids(spatials []rtree.Spatial) []*Boid {
	boids := make([]*Boid, len(spatials))
	for i, spatial := range spatials {
		boids[i] = spatial.(*Boid)
	}
	return boids
}
<commit_msg>limit velocities to 25 units<commit_after>// With many thanks to http://www.kfish.org/boids/pseudocode.html

package main

import (
	rtree "github.com/dhconnelly/rtreego"
	"math"
	"math/rand"
)

type Boid struct {
	ID       int         `json:"id"`
	Point    rtree.Point `json:"position"`
	Velocity []float64   `json:"-"`
}

func NewBoid(id, dimensions int) *Boid {
	var point rtree.Point
	var vel []float64

	for i := 0; i < dimensions; i++ {
		point = append(point, 0.0)
		vel = append(vel, 0.0)
	}

	return &Boid{
		ID:       id,
		Point:    point,
		Velocity: vel,
	}
}

func (b *Boid) RandomizePosition(area *Area) {
	var point rtree.Point

	for i := range area.Dimensions {
		p := rand.Int31n(area.Dimensions[i])
		point = append(point, float64(p))
	}

	b.Point = point
}

func (b *Boid) Bounds() *rtree.Rect {
	return b.Point.ToRect(0.01)
}

func (b *Boid) UpdateVelocity(area *Area) {
	v1 := b.Rule1(area)
	v2 := b.Rule2(area)
	v3 := b.Rule3(area)

	velocities := AddFloats(b.Velocity, v1, v2, v3)
	b.Velocity = LimitVelocity(velocities)
}

func (b *Boid) UpdatePosition() {
	b.Point = AddFloats(b.Point, b.Velocity)
}

// Rule1
//
// 	PROCEDURE Rule1(boid bJ)
//
//		Vector pcJ
//
//    FOR EACH BOID b
//    	IF b != bJ THEN
//    		pcJ = pcJ + b.position
//    	END IF
//    END
//
//    pcJ = pcJ / N-1
//
//    RETURN (pcJ - bJ.position) / 100
//
// 	END PROCEDURE
func (b *Boid) Rule1(area *Area) []float64 {
	pcJ := make(rtree.Point, len(b.Point))
	for i := range pcJ {
		pcJ[i] = 0
	}

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Point {
				pcJ[k] = pcJ[k] + v
			}
		}
	}

	for i := range pcJ {
		pcJ[i] = pcJ[i] / float64(len(boids)-1)
	}

	subbed := SubFloats(pcJ, b.Point)
	divved := DivFloat(subbed, 100.0)
	return divved
}

// Rule2
//
// PROCEDURE rule2(boid bJ)
//
// 	Vector c = 0;
//
// 	FOR EACH BOID b
// 		IF b != bJ THEN
// 			IF |b.position - bJ.position| < 100 THEN
// 				c = c - (b.position - bJ.position)
// 			END IF
// 		END IF
// 	END
//
// 	RETURN c
//
// END PROCEDURE
func (b *Boid) Rule2(area *Area) []float64 {
	vector := makeFloats(int32(len(b.Point)))

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Point {
				if math.Abs(v-b.Point[k]) < 10 {
					vector[k] = vector[k] - (v - b.Point[k])
				}
			}
		}
	}

	return vector
}

// Rule3
//
// PROCEDURE rule3(boid bJ)
//
// 	Vector pvJ
//
// 	FOR EACH BOID b
// 		IF b != bJ THEN
// 			pvJ = pvJ + b.velocity
// 		END IF
// 	END
//
// 	pvJ = pvJ / N-1
//
// 	RETURN (pvJ - bJ.velocity) / 8
//
// END PROCEDURE
func (b *Boid) Rule3(area *Area) []float64 {
	pvJ := makeFloats(int32(len(b.Velocity)))

	nearest := area.Tree.NearestNeighbors(10, b.Point)
	boids := SpatialsToBoids(nearest)

	for _, boid := range boids {
		if boid.ID != b.ID {
			for k, v := range boid.Velocity {
				pvJ[k] = pvJ[k] + v
			}
		}
	}

	for i := range pvJ {
		pvJ[i] = pvJ[i] / float64(len(boids)-1)
	}

	subbed := SubFloats(pvJ, b.Velocity)
	divved := DivFloat(subbed, 8.0)
	return divved
}

// LimitVelocity
//
//  PROCEDURE limit_velocity(Boid b)
//          Integer vlim
//          Vector v
//
//          IF |b.velocity| > vlim THEN
//                  b.velocity = (b.velocity / |b.velocity|) * vlim
//          END IF
//  END PROCEDURE
func LimitVelocity(velocities []float64) []float64 {
	vlim := 25.0
	var absVel float64

	for i := range velocities {
		absVel = math.Abs(velocities[i])
		if absVel > vlim {
			velocities[i] = (velocities[i] / absVel) * vlim
		}
	}

	return velocities
}

func SpatialsToBoids(spatials []rtree.Spatial) []*Boid {
	boids := make([]*Boid, len(spatials))
	for i, spatial := range spatials {
		boids[i] = spatial.(*Boid)
	}
	return boids
}
<|endoftext|>


<commit_before>// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package topo

import (
	"github.com/gonum/graph"
	"github.com/gonum/graph/traverse"
)

// IsPathIn returns whether path is a path in g.
//
// As special cases, IsPathIn returns true for a zero length path or for
// a path of length 1 when the node in path exists in the graph.
func IsPathIn(g graph.Graph, path []graph.Node) bool {
	var canReach func(u, v graph.Node) bool
	switch g := g.(type) {
	case graph.Directed:
		canReach = g.HasEdgeFromTo
	default:
		canReach = g.HasEdge
	}

	if path == nil || len(path) == 0 {
		return true
	} else if len(path) == 1 {
		return g.Has(path[0])
	}

	for i := 0; i < len(path)-1; i++ {
		if !canReach(path[i], path[i+1]) {
			return false
		}
	}

	return true
}

// ConnectedComponents returns the connected components of the graph g. All
// edges are treated as undirected.
func ConnectedComponents(g graph.Undirected) [][]graph.Node {
	var (
		w  traverse.DepthFirst
		c  []graph.Node
		cc [][]graph.Node
	)
	during := func(n graph.Node) {
		c = append(c, n)
	}
	after := func() {
		cc = append(cc, []graph.Node(nil))
		cc[len(cc)-1] = append(cc[len(cc)-1], c...)
		c = c[:0]
	}
	w.WalkAll(g, nil, after, during)

	return cc
}
<commit_msg>topo: clean up IsPathIn implementation<commit_after>// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package topo

import (
	"github.com/gonum/graph"
	"github.com/gonum/graph/traverse"
)

// IsPathIn returns whether path is a path in g.
//
// As special cases, IsPathIn returns true for a zero length path or for
// a path of length 1 when the node in path exists in the graph.
func IsPathIn(g graph.Graph, path []graph.Node) bool {
	switch len(path) {
	case 0:
		return true
	case 1:
		return g.Has(path[0])
	default:
		var canReach func(u, v graph.Node) bool
		switch g := g.(type) {
		case graph.Directed:
			canReach = g.HasEdgeFromTo
		default:
			canReach = g.HasEdge
		}

		for i, u := range path[:len(path)-1] {
			if !canReach(u, path[i+1]) {
				return false
			}
		}
		return true
	}
}

// ConnectedComponents returns the connected components of the graph g. All
// edges are treated as undirected.
func ConnectedComponents(g graph.Undirected) [][]graph.Node {
	var (
		w  traverse.DepthFirst
		c  []graph.Node
		cc [][]graph.Node
	)
	during := func(n graph.Node) {
		c = append(c, n)
	}
	after := func() {
		cc = append(cc, []graph.Node(nil))
		cc[len(cc)-1] = append(cc[len(cc)-1], c...)
		c = c[:0]
	}
	w.WalkAll(g, nil, after, during)

	return cc
}
<|endoftext|>


<commit_before>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
)

// Update function will UPDATE a record ...
func (o *ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (rowsAff int64, err error) {
	sg := o.sqlGen

	errorString := "Update error"

	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}

	err = o.CallBeforeUpdateHookIfNeeded(ctx, tx, obj)
	if err != nil {
		o.Error("%s %s %s", errorString, "BeforeUpdateHookError", err)
		return 0, err
	}

	sqlStr, bindArgs, bindWhere, err := sg.BindingUpdate(sg, o.s, obj)
	if err != nil {
		o.Error("Update/sqlStr='%s', bindArgs=[%v], bindWhere=[%v], err=%s", sqlStr, bindArgs, bindWhere, err)
		return 0, err
	}
	o.Trace("Update/sqlStr='%s', bindArgs=[%v], bindWhere=[%v]", sqlStr, bindArgs, bindWhere)

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		stmtErr := stmt.Close()
		if stmtErr != nil {
			if err == nil {
				err = stmtErr
			} else {
				err = fmt.Errorf("%s: %s", err.Error(), stmtErr.Error())
			}
		}
	}()

	allBind := append(bindArgs, bindWhere...)

	res, err := stmt.ExecContext(ctx, allBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err = res.RowsAffected()
	if err != nil {
		return 0, err
	}

	if rowsAff == 0 {
		return 0, ErrNoResult
	}

	err = o.CallAfterUpdateHookIfNeeded(ctx, tx, obj)
	if err != nil {
		o.Error("%s %s %s", errorString, "BeforeAfterUpdateHookError", err)
		return 0, err
	}

	obj.MarkDirty(false)      // Note that the object has been recently saved
	obj.ResetChangedColumns() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<commit_msg>UPDATEs that result in zero rows affected are not an error.<commit_after>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
)

// Update function will UPDATE a record ...
func (o *ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (rowsAff int64, err error) {
	sg := o.sqlGen

	errorString := "Update error"

	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}

	err = o.CallBeforeUpdateHookIfNeeded(ctx, tx, obj)
	if err != nil {
		o.Error("%s %s %s", errorString, "BeforeUpdateHookError", err)
		return 0, err
	}

	sqlStr, bindArgs, bindWhere, err := sg.BindingUpdate(sg, o.s, obj)
	if err != nil {
		o.Error("Update/sqlStr='%s', bindArgs=[%v], bindWhere=[%v], err=%s", sqlStr, bindArgs, bindWhere, err)
		return 0, err
	}
	o.Trace("Update/sqlStr='%s', bindArgs=[%v], bindWhere=[%v]", sqlStr, bindArgs, bindWhere)

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		stmtErr := stmt.Close()
		if stmtErr != nil {
			if err == nil {
				err = stmtErr
			} else {
				err = fmt.Errorf("%s: %s", err.Error(), stmtErr.Error())
			}
		}
	}()

	allBind := append(bindArgs, bindWhere...)

	res, err := stmt.ExecContext(ctx, allBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err = res.RowsAffected()
	if err != nil {
		return 0, err
	}

	/*
	 * UPDATEs that result in zero rows affected are no longer an error.
	if rowsAff == 0 {
		return 0, ErrNoResult
	}
	*/

	err = o.CallAfterUpdateHookIfNeeded(ctx, tx, obj)
	if err != nil {
		o.Error("%s %s %s", errorString, "BeforeAfterUpdateHookError", err)
		return 0, err
	}

	obj.MarkDirty(false)      // Note that the object has been recently saved
	obj.ResetChangedColumns() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<|endoftext|>


<commit_before>// Package stringext adds extra power to the strings package with helper
// functions
package stringext

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	specialCases = regexp.MustCompile(`(?m)[-.$/:_{}\s]`)
	acronyms     = regexp.MustCompile(`(Url|Http|Id|Io|Uuid|Api|Uri|Ssl|Cname|Oauth|Otp)$`)
	acronymsi    = regexp.MustCompile(`(URL|HTTP|ID|IO|UUID|API|URI|SSL|CNAME|OAUTH|OTP)`)
)

// ToLowerFirst lowers the first character of any given unicode char
func ToLowerFirst(ident string) string {
	r, n := utf8.DecodeRuneInString(ident)
	return string(unicode.ToLower(r)) + ident[n:]
}

// ToUpperFirst converts the first character of any given unicode char to
// uppercase
func ToUpperFirst(ident string) string {
	r, n := utf8.DecodeRuneInString(ident)
	return string(unicode.ToUpper(r)) + ident[n:]
}

// Pointerize returns the first character of a given string as lowercased, this
// method is intened to use as a function receiver generator
func Pointerize(ident string) string {
	r, _ := utf8.DecodeRuneInString(ident)
	return string(unicode.ToLower(r))
}

// Capitalize uppercases the first char of s and lowercases the rest.
func Capitalize(str string) string {
	buf := &bytes.Buffer{}
	var r0 rune
	var size int

	r0, size = utf8.DecodeRuneInString(str)
	str = str[size:]
	buf.WriteRune(unicode.ToUpper(r0))

	for len(str) > 0 {
		r0, size = utf8.DecodeRuneInString(str)
		str = str[size:]
		buf.WriteRune(unicode.ToLower(r0))
	}

	return buf.String()
}

// JSONTag generates json tag for given string, it is using the javascript
// concepts
//
// eg: ID ->
// 	becomes "id" if it is at the beginning
//  	or
//  becomes "Id" if it is in the middle of the string
func JSONTag(n string, required bool) string {
	modified := ToLowerFirst(Normalize(n))

	tags := []string{modified}
	if !required {
		tags = append(tags, "omitempty")
	}

	return fmt.Sprintf("`json:\"%s\"`", strings.Join(tags, ","))
}

func JSONTagWithIgnored(n string, required bool, ignored bool, fieldType string) string {
	modified := ToLowerFirst(Normalize(n))

	tags := []string{modified}
	if !required {
		tags = append(tags, "omitempty")
	}

	switch fieldType {
	case "int64", "float64", "uint64":
		tags = append(tags, "string")
	}

	// if this field is ingored, override all other tags
	if ignored {
		tags = []string{"-"}
	}

	return fmt.Sprintf("`json:\"%s\"`", strings.Join(tags, ","))
}

// Normalize removes non a-z characters and uppercases the following character,
// all characters followed by it will be lowercased if the word is one the
// acronymsi
func Normalize(s string) string {
	return acronymsi.ReplaceAllStringFunc(
		DepunctWithInitialLower(s), func(c string) string {
			return ToUpperFirst(strings.ToLower(c))
		})
}

// ToFieldName handles field names, if the given string is one of the
// `acronymsi` it is lowercasing it
//
// given "URL" as parameter converted to "url"
// given "ProfileURL" as parameter converted to "profile_url"
// given "Profile" as parameter converted to "profile"
// given "ProfileName" as parameter converted to "profile_name"
//
func ToFieldName(u string) string {
	buf := bytes.NewBufferString("")
	for i, v := range Normalize(u) {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	return strings.ToLower(buf.String())
}

// DepunctWithInitialUpper does special operations to the given string, while
// operating uppercases the special words
func DepunctWithInitialUpper(ident string) string {
	return Depunct(ident, true)
}

// DepunctWithInitialLower does special operations to the given string, while
// operating lowercases the special words
func DepunctWithInitialLower(ident string) string {
	return Depunct(ident, false)
}

// Depunct splits the given string with special chars and operates on them one
// by one
func Depunct(ident string, initialCap bool) string {
	matches := specialCases.Split(ident, -1)
	for i, m := range matches {
		if initialCap || i > 0 {
			m = ToUpperFirst(m)
		}
		matches[i] = acronyms.ReplaceAllStringFunc(m, func(c string) string {
			if len(c) > 4 {
				return strings.ToUpper(c[:2]) + c[2:]
			}
			return strings.ToUpper(c)
		})
	}
	return strings.Join(matches, "")
}

// Equal check if given two strings are same, used in templates
func Equal(a, b string) bool {
	return a == b
}

// AsComment formats the given string as if it is a Go Comment, breaks lines
// every 78 lines
func AsComment(c string) string {
	var buf bytes.Buffer
	const maxLen = 78
	removeNewlines := func(s string) string {
		return strings.Replace(s, "\n", "\n// ", -1)
	}
	for len(c) > 0 {
		line := c
		if len(line) < maxLen {
			fmt.Fprintf(&buf, "// %s\n", removeNewlines(line))
			break
		}
		line = line[:maxLen]
		si := strings.LastIndex(line, " ")
		if si != -1 {
			line = line[:si]
		}
		fmt.Fprintf(&buf, "// %s\n", removeNewlines(line))
		c = c[len(line):]
		if si != -1 {
			c = c[1:]
		}
	}

	return buf.String()
}

// Contains checks if the given string is in given string slice
func Contains(n string, r []string) bool {
	for _, r := range r {
		if r == n {
			return true
		}
	}

	return false
}
<commit_msg>change variable name<commit_after>// Package stringext adds extra power to the strings package with helper
// functions
package stringext

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	specialCases = regexp.MustCompile(`(?m)[-.$/:_{}\s]`)
	acronyms     = regexp.MustCompile(`(Url|Http|Id|Io|Uuid|Api|Uri|Ssl|Cname|Oauth|Otp)$`)
	acronymsi    = regexp.MustCompile(`(URL|HTTP|ID|IO|UUID|API|URI|SSL|CNAME|OAUTH|OTP)`)
)

// ToLowerFirst lowers the first character of any given unicode char
func ToLowerFirst(ident string) string {
	r, n := utf8.DecodeRuneInString(ident)
	return string(unicode.ToLower(r)) + ident[n:]
}

// ToUpperFirst converts the first character of any given unicode char to
// uppercase
func ToUpperFirst(ident string) string {
	r, n := utf8.DecodeRuneInString(ident)
	return string(unicode.ToUpper(r)) + ident[n:]
}

// Pointerize returns the first character of a given string as lowercased, this
// method is intened to use as a function receiver generator
func Pointerize(ident string) string {
	r, _ := utf8.DecodeRuneInString(ident)
	return string(unicode.ToLower(r))
}

// Capitalize uppercases the first char of s and lowercases the rest.
func Capitalize(str string) string {
	buf := &bytes.Buffer{}
	var r0 rune
	var size int

	r0, size = utf8.DecodeRuneInString(str)
	str = str[size:]
	buf.WriteRune(unicode.ToUpper(r0))

	for len(str) > 0 {
		r0, size = utf8.DecodeRuneInString(str)
		str = str[size:]
		buf.WriteRune(unicode.ToLower(r0))
	}

	return buf.String()
}

// JSONTag generates json tag for given string, it is using the javascript
// concepts
//
// eg: ID ->
// 	becomes "id" if it is at the beginning
//  	or
//  becomes "Id" if it is in the middle of the string
func JSONTag(n string, required bool) string {
	modified := ToLowerFirst(Normalize(n))

	tags := []string{modified}
	if !required {
		tags = append(tags, "omitempty")
	}

	return fmt.Sprintf("`json:\"%s\"`", strings.Join(tags, ","))
}

func JSONTagWithIgnored(n string, required bool, ignored bool, fieldType string) string {
	modified := ToLowerFirst(Normalize(n))

	tags := []string{modified}
	if !required {
		tags = append(tags, "omitempty")
	}

	switch fieldType {
	case "int64", "float64", "uint64":
		tags = append(tags, "string")
	}

	// if this field is ingored, override all other tags
	if ignored {
		tags = []string{"-"}
	}

	return fmt.Sprintf("`json:\"%s\"`", strings.Join(tags, ","))
}

// Normalize removes non a-z characters and uppercases the following character,
// all characters followed by it will be lowercased if the word is one the
// acronymsi
func Normalize(s string) string {
	return acronymsi.ReplaceAllStringFunc(
		DepunctWithInitialLower(s), func(c string) string {
			return ToUpperFirst(strings.ToLower(c))
		})
}

// ToFieldName handles field names, if the given string is one of the
// `acronymsi` it is lowercasing it
//
// given "URL" as parameter converted to "url"
// given "ProfileURL" as parameter converted to "profile_url"
// given "Profile" as parameter converted to "profile"
// given "ProfileName" as parameter converted to "profile_name"
//
func ToFieldName(u string) string {
	buf := bytes.NewBufferString("")
	for i, v := range Normalize(u) {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	return strings.ToLower(buf.String())
}

// DepunctWithInitialUpper does special operations to the given string, while
// operating uppercases the special words
func DepunctWithInitialUpper(ident string) string {
	return Depunct(ident, true)
}

// DepunctWithInitialLower does special operations to the given string, while
// operating lowercases the special words
func DepunctWithInitialLower(ident string) string {
	return Depunct(ident, false)
}

// Depunct splits the given string with special chars and operates on them one
// by one
func Depunct(ident string, initialCap bool) string {
	matches := specialCases.Split(ident, -1)
	for i, m := range matches {
		if initialCap || i > 0 {
			m = ToUpperFirst(m)
		}
		matches[i] = acronyms.ReplaceAllStringFunc(m, func(c string) string {
			if len(c) > 4 {
				return strings.ToUpper(c[:2]) + c[2:]
			}
			return strings.ToUpper(c)
		})
	}
	return strings.Join(matches, "")
}

// Equal check if given two strings are same, used in templates
func Equal(a, b string) bool {
	return a == b
}

// AsComment formats the given string as if it is a Go Comment, breaks lines
// every 78 lines
func AsComment(c string) string {
	var buf bytes.Buffer
	const maxLen = 78
	removeNewlines := func(s string) string {
		return strings.Replace(s, "\n", "\n// ", -1)
	}
	for len(c) > 0 {
		line := c
		if len(line) < maxLen {
			fmt.Fprintf(&buf, "// %s\n", removeNewlines(line))
			break
		}
		line = line[:maxLen]
		si := strings.LastIndex(line, " ")
		if si != -1 {
			line = line[:si]
		}
		fmt.Fprintf(&buf, "// %s\n", removeNewlines(line))
		c = c[len(line):]
		if si != -1 {
			c = c[1:]
		}
	}

	return buf.String()
}

// Contains checks if the given string is in given string slice
func Contains(n string, r []string) bool {
	for _, s := range r {
		if s == n {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package installer

// Note: .exe is not in that list because we need to
// read part of its contents to decide what we're going to
// do with it: 1) extract it 2) run it as an installer 3)
// just copy it naked
var installerForExt = map[string]InstallerType{

	///////////////////////////////////////////////////////////
	// Generic archives
	///////////////////////////////////////////////////////////

	".zip": InstallerTypeArchive,
	".gz":  InstallerTypeArchive,
	".bz2": InstallerTypeArchive,
	".7z":  InstallerTypeArchive,
	".tar": InstallerTypeArchive,
	".xz":  InstallerTypeArchive,
	".rar": InstallerTypeArchive,

	///////////////////////////////////////////////////////////
	// Known non-supported
	///////////////////////////////////////////////////////////

	".deb": InstallerTypeUnsupported,
	".rpm": InstallerTypeUnsupported,
	".pkg": InstallerTypeUnsupported,

	///////////////////////////////////////////////////////////
	// Platform-specific packages
	///////////////////////////////////////////////////////////

	// Apple disk images
	".dmg": InstallerTypeArchive,

	// Microsoft packages
	".msi": InstallerTypeMSI,

	///////////////////////////////////////////////////////////
	// Known naked that also sniff as other formats
	///////////////////////////////////////////////////////////

	".jar":          InstallerTypeNaked,
	".love":         InstallerTypeNaked,
	".unitypackage": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Books!
	///////////////////////////////////////////////////////////

	".pdf":    InstallerTypeNaked,
	".ps":     InstallerTypeNaked,
	".djvu":   InstallerTypeNaked,
	".cbr":    InstallerTypeNaked,
	".cbz":    InstallerTypeNaked,
	".cb7":    InstallerTypeNaked,
	".cbt":    InstallerTypeNaked,
	".cba":    InstallerTypeNaked,
	".doc":    InstallerTypeNaked,
	".docx":   InstallerTypeNaked,
	".epub":   InstallerTypeNaked,
	".pdb":    InstallerTypeNaked,
	".fb2":    InstallerTypeNaked,
	".xeb":    InstallerTypeNaked,
	".ceb":    InstallerTypeNaked,
	".ibooks": InstallerTypeNaked,
	".txt":    InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Game-maker assets
	///////////////////////////////////////////////////////////

	".gmez": InstallerTypeNaked,
	".gmz":  InstallerTypeNaked,
	".yyz":  InstallerTypeNaked,
	".yymp": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Miscellaneous other things
	///////////////////////////////////////////////////////////

	// Some html games provide a single .html file
	// Now that's dedication.
	".html": InstallerTypeNaked,
}
<commit_msg>more naked installers<commit_after>package installer

// Note: .exe is not in that list because we need to
// read part of its contents to decide what we're going to
// do with it: 1) extract it 2) run it as an installer 3)
// just copy it naked
var installerForExt = map[string]InstallerType{

	///////////////////////////////////////////////////////////
	// Generic archives
	///////////////////////////////////////////////////////////

	".zip": InstallerTypeArchive,
	".gz":  InstallerTypeArchive,
	".bz2": InstallerTypeArchive,
	".7z":  InstallerTypeArchive,
	".tar": InstallerTypeArchive,
	".xz":  InstallerTypeArchive,
	".rar": InstallerTypeArchive,

	///////////////////////////////////////////////////////////
	// Known non-supported
	///////////////////////////////////////////////////////////

	".deb": InstallerTypeUnsupported,
	".rpm": InstallerTypeUnsupported,
	".pkg": InstallerTypeUnsupported,

	///////////////////////////////////////////////////////////
	// Platform-specific packages
	///////////////////////////////////////////////////////////

	// Apple disk images
	".dmg": InstallerTypeArchive,

	// Microsoft packages
	".msi": InstallerTypeMSI,

	///////////////////////////////////////////////////////////
	// Known naked that also sniff as other formats
	///////////////////////////////////////////////////////////

	".jar":          InstallerTypeNaked,
	".love":         InstallerTypeNaked,
	".unitypackage": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Books!
	///////////////////////////////////////////////////////////

	".pdf":    InstallerTypeNaked,
	".ps":     InstallerTypeNaked,
	".djvu":   InstallerTypeNaked,
	".cbr":    InstallerTypeNaked,
	".cbz":    InstallerTypeNaked,
	".cb7":    InstallerTypeNaked,
	".cbt":    InstallerTypeNaked,
	".cba":    InstallerTypeNaked,
	".doc":    InstallerTypeNaked,
	".docx":   InstallerTypeNaked,
	".epub":   InstallerTypeNaked,
	".pdb":    InstallerTypeNaked,
	".fb2":    InstallerTypeNaked,
	".xeb":    InstallerTypeNaked,
	".ceb":    InstallerTypeNaked,
	".ibooks": InstallerTypeNaked,
	".txt":    InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Media
	///////////////////////////////////////////////////////////

	".ogg": InstallerTypeNaked,
	".mp3": InstallerTypeNaked,
	".wav": InstallerTypeNaked,
	".mp4": InstallerTypeNaked,
	".avi": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Images
	///////////////////////////////////////////////////////////

	".png": InstallerTypeNaked,
	".jpg": InstallerTypeNaked,
	".gif": InstallerTypeNaked,
	".bmp": InstallerTypeNaked,
	".tga": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Game Maker assets
	///////////////////////////////////////////////////////////

	".gmez": InstallerTypeNaked,
	".gmz":  InstallerTypeNaked,
	".yyz":  InstallerTypeNaked,
	".yymp": InstallerTypeNaked,

	///////////////////////////////////////////////////////////
	// Miscellaneous other things
	///////////////////////////////////////////////////////////

	// Some html games provide a single .html file
	// Now that's dedication.
	".html": InstallerTypeNaked,
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package csitranslation

import (
	"errors"
	"fmt"

	"k8s.io/api/core/v1"
	storage "k8s.io/api/storage/v1"
	"k8s.io/csi-translation-lib/plugins"
)

var (
	inTreePlugins = map[string]plugins.InTreePlugin{
		plugins.GCEPDDriverName:  plugins.NewGCEPersistentDiskCSITranslator(),
		plugins.AWSEBSDriverName: plugins.NewAWSElasticBlockStoreCSITranslator(),
		plugins.CinderDriverName: plugins.NewOpenStackCinderCSITranslator(),
	}
)

// TranslateInTreeStorageClassToCSI takes in-tree Storage Class
// and translates it to a set of parameters consumable by CSI plugin
func TranslateInTreeStorageClassToCSI(inTreePluginName string, sc *storage.StorageClass) (*storage.StorageClass, error) {
	newSC := sc.DeepCopy()
	for _, curPlugin := range inTreePlugins {
		if inTreePluginName == curPlugin.GetInTreePluginName() {
			return curPlugin.TranslateInTreeStorageClassToCSI(newSC)
		}
	}
	return nil, fmt.Errorf("could not find in-tree storage class parameter translation logic for %#v", inTreePluginName)
}

// TranslateInTreePVToCSI takes a persistent volume and will translate
// the in-tree source to a CSI Source if the translation logic
// has been implemented. The input persistent volume will not
// be modified
func TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
	if pv == nil {
		return nil, errors.New("persistent volume was nil")
	}
	copiedPV := pv.DeepCopy()
	for _, curPlugin := range inTreePlugins {
		if curPlugin.CanSupport(copiedPV) {
			return curPlugin.TranslateInTreePVToCSI(copiedPV)
		}
	}
	return nil, fmt.Errorf("could not find in-tree plugin translation logic for %#v", copiedPV.Name)
}

// TranslateCSIPVToInTree takes a PV with a CSI PersistentVolume Source and will translate
// it to a in-tree Persistent Volume Source for the specific in-tree volume specified
// by the `Driver` field in the CSI Source. The input PV object will not be modified.
func TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
	if pv == nil || pv.Spec.CSI == nil {
		return nil, errors.New("CSI persistent volume was nil")
	}
	copiedPV := pv.DeepCopy()
	for driverName, curPlugin := range inTreePlugins {
		if copiedPV.Spec.CSI.Driver == driverName {
			return curPlugin.TranslateCSIPVToInTree(copiedPV)
		}
	}
	return nil, fmt.Errorf("could not find in-tree plugin translation logic for %s", copiedPV.Spec.CSI.Driver)
}

// IsMigratableIntreePluginByName tests whether there is migration logic for the in-tree plugin
// whose name matches the given name
func IsMigratableIntreePluginByName(inTreePluginName string) bool {
	for _, curPlugin := range inTreePlugins {
		if curPlugin.GetInTreePluginName() == inTreePluginName {
			return true
		}
	}
	return false
}

// IsMigratedCSIDriverByName tests whether there exists an in-tree plugin with logic
// to migrate to the CSI driver with given name
func IsMigratedCSIDriverByName(csiPluginName string) bool {
	if _, ok := inTreePlugins[csiPluginName]; ok {
		return true
	}
	return false
}

// GetInTreePluginNameFromSpec returns the plugin name
func GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error) {
	if pv != nil {
		for _, curPlugin := range inTreePlugins {
			if curPlugin.CanSupport(pv) {
				return curPlugin.GetInTreePluginName(), nil
			}
		}
		return "", fmt.Errorf("could not find in-tree plugin name from persistent volume %v", pv)
	} else if vol != nil {
		// TODO(dyzz): Implement inline volume migration support
		return "", errors.New("inline volume migration not yet supported")
	} else {
		return "", errors.New("both persistent volume and volume are nil")
	}
}

// GetCSINameFromInTreeName returns the name of a CSI driver that supersedes the
// in-tree plugin with the given name
func GetCSINameFromInTreeName(pluginName string) (string, error) {
	for csiDriverName, curPlugin := range inTreePlugins {
		if curPlugin.GetInTreePluginName() == pluginName {
			return csiDriverName, nil
		}
	}
	return "", fmt.Errorf("could not find CSI Driver name for plugin %v", pluginName)
}

// GetInTreeNameFromCSIName returns the name of the in-tree plugin superseded by
// a CSI driver with the given name
func GetInTreeNameFromCSIName(pluginName string) (string, error) {
	if plugin, ok := inTreePlugins[pluginName]; ok {
		return plugin.GetInTreePluginName(), nil
	}
	return "", fmt.Errorf("Could not find In-Tree driver name for CSI plugin %v", pluginName)
}

// IsPVMigratable tests whether there is migration logic for the given Persistent Volume
func IsPVMigratable(pv *v1.PersistentVolume) bool {
	for _, curPlugin := range inTreePlugins {
		if curPlugin.CanSupport(pv) {
			return true
		}
	}
	return false
}

// IsInlineMigratable tests whether there is Migration logic for the given Inline Volume
func IsInlineMigratable(vol *v1.Volume) bool {
	return false
}
<commit_msg>add feature gate for azure disk<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package csitranslation

import (
	"errors"
	"fmt"

	"k8s.io/api/core/v1"
	storage "k8s.io/api/storage/v1"
	"k8s.io/csi-translation-lib/plugins"
)

var (
	inTreePlugins = map[string]plugins.InTreePlugin{
		plugins.GCEPDDriverName:     plugins.NewGCEPersistentDiskCSITranslator(),
		plugins.AWSEBSDriverName:    plugins.NewAWSElasticBlockStoreCSITranslator(),
		plugins.CinderDriverName:    plugins.NewOpenStackCinderCSITranslator(),
		plugins.AzureDiskDriverName: plugins.NewAzureDiskCSITranslator(),
	}
)

// TranslateInTreeStorageClassToCSI takes in-tree Storage Class
// and translates it to a set of parameters consumable by CSI plugin
func TranslateInTreeStorageClassToCSI(inTreePluginName string, sc *storage.StorageClass) (*storage.StorageClass, error) {
	newSC := sc.DeepCopy()
	for _, curPlugin := range inTreePlugins {
		if inTreePluginName == curPlugin.GetInTreePluginName() {
			return curPlugin.TranslateInTreeStorageClassToCSI(newSC)
		}
	}
	return nil, fmt.Errorf("could not find in-tree storage class parameter translation logic for %#v", inTreePluginName)
}

// TranslateInTreePVToCSI takes a persistent volume and will translate
// the in-tree source to a CSI Source if the translation logic
// has been implemented. The input persistent volume will not
// be modified
func TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
	if pv == nil {
		return nil, errors.New("persistent volume was nil")
	}
	copiedPV := pv.DeepCopy()
	for _, curPlugin := range inTreePlugins {
		if curPlugin.CanSupport(copiedPV) {
			return curPlugin.TranslateInTreePVToCSI(copiedPV)
		}
	}
	return nil, fmt.Errorf("could not find in-tree plugin translation logic for %#v", copiedPV.Name)
}

// TranslateCSIPVToInTree takes a PV with a CSI PersistentVolume Source and will translate
// it to a in-tree Persistent Volume Source for the specific in-tree volume specified
// by the `Driver` field in the CSI Source. The input PV object will not be modified.
func TranslateCSIPVToInTree(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
	if pv == nil || pv.Spec.CSI == nil {
		return nil, errors.New("CSI persistent volume was nil")
	}
	copiedPV := pv.DeepCopy()
	for driverName, curPlugin := range inTreePlugins {
		if copiedPV.Spec.CSI.Driver == driverName {
			return curPlugin.TranslateCSIPVToInTree(copiedPV)
		}
	}
	return nil, fmt.Errorf("could not find in-tree plugin translation logic for %s", copiedPV.Spec.CSI.Driver)
}

// IsMigratableIntreePluginByName tests whether there is migration logic for the in-tree plugin
// whose name matches the given name
func IsMigratableIntreePluginByName(inTreePluginName string) bool {
	for _, curPlugin := range inTreePlugins {
		if curPlugin.GetInTreePluginName() == inTreePluginName {
			return true
		}
	}
	return false
}

// IsMigratedCSIDriverByName tests whether there exists an in-tree plugin with logic
// to migrate to the CSI driver with given name
func IsMigratedCSIDriverByName(csiPluginName string) bool {
	if _, ok := inTreePlugins[csiPluginName]; ok {
		return true
	}
	return false
}

// GetInTreePluginNameFromSpec returns the plugin name
func GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error) {
	if pv != nil {
		for _, curPlugin := range inTreePlugins {
			if curPlugin.CanSupport(pv) {
				return curPlugin.GetInTreePluginName(), nil
			}
		}
		return "", fmt.Errorf("could not find in-tree plugin name from persistent volume %v", pv)
	} else if vol != nil {
		// TODO(dyzz): Implement inline volume migration support
		return "", errors.New("inline volume migration not yet supported")
	} else {
		return "", errors.New("both persistent volume and volume are nil")
	}
}

// GetCSINameFromInTreeName returns the name of a CSI driver that supersedes the
// in-tree plugin with the given name
func GetCSINameFromInTreeName(pluginName string) (string, error) {
	for csiDriverName, curPlugin := range inTreePlugins {
		if curPlugin.GetInTreePluginName() == pluginName {
			return csiDriverName, nil
		}
	}
	return "", fmt.Errorf("could not find CSI Driver name for plugin %v", pluginName)
}

// GetInTreeNameFromCSIName returns the name of the in-tree plugin superseded by
// a CSI driver with the given name
func GetInTreeNameFromCSIName(pluginName string) (string, error) {
	if plugin, ok := inTreePlugins[pluginName]; ok {
		return plugin.GetInTreePluginName(), nil
	}
	return "", fmt.Errorf("Could not find In-Tree driver name for CSI plugin %v", pluginName)
}

// IsPVMigratable tests whether there is migration logic for the given Persistent Volume
func IsPVMigratable(pv *v1.PersistentVolume) bool {
	for _, curPlugin := range inTreePlugins {
		if curPlugin.CanSupport(pv) {
			return true
		}
	}
	return false
}

// IsInlineMigratable tests whether there is Migration logic for the given Inline Volume
func IsInlineMigratable(vol *v1.Volume) bool {
	return false
}
<|endoftext|>


<commit_before>package p2p

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"sync/atomic"
	"time"

	acm "github.com/tendermint/tendermint/account"
	. "github.com/tendermint/tendermint/common"
	"github.com/tendermint/tendermint/types"
)

type Reactor interface {
	Start(sw *Switch)
	Stop()
	GetChannels() []*ChannelDescriptor
	AddPeer(peer *Peer)
	RemovePeer(peer *Peer, reason interface{})
	Receive(chId byte, peer *Peer, msgBytes []byte)
}

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

type BaseReactor struct{}

func (_ BaseReactor) Start(sw *Switch)                               {}
func (_ BaseReactor) Stop()                                          {}
func (_ BaseReactor) GetChannels() []*ChannelDescriptor              { return nil }
func (_ BaseReactor) AddPeer(peer *Peer)                             {}
func (_ BaseReactor) RemovePeer(peer *Peer, reason interface{})      {}
func (_ BaseReactor) Receive(chId byte, peer *Peer, msgBytes []byte) {}

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

/*
The `Switch` handles peer connections and exposes an API to receive incoming messages
on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
incoming messages are received on the reactor.
*/
type Switch struct {
	listeners    []Listener
	reactors     map[string]Reactor
	chDescs      []*ChannelDescriptor
	reactorsByCh map[byte]Reactor
	peers        *PeerSet
	dialing      *CMap
	running      uint32
	nodeInfo     *types.NodeInfo    // our node info
	nodePrivKey  acm.PrivKeyEd25519 // our node privkey
}

var (
	ErrSwitchDuplicatePeer      = errors.New("Duplicate peer")
	ErrSwitchMaxPeersPerIPRange = errors.New("IP range has too many peers")
)

const (
	peerDialTimeoutSeconds  = 3  // TODO make this configurable
	handshakeTimeoutSeconds = 20 // TODO make this configurable
	maxNumPeers             = 50 // TODO make this configurable
)

func NewSwitch() *Switch {
	sw := &Switch{
		reactors:     make(map[string]Reactor),
		chDescs:      make([]*ChannelDescriptor, 0),
		reactorsByCh: make(map[byte]Reactor),
		peers:        NewPeerSet(),
		dialing:      NewCMap(),
		running:      0,
		nodeInfo:     nil,
	}
	return sw
}

// Not goroutine safe.
func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor {
	// Validate the reactor.
	// No two reactors can share the same channel.
	reactorChannels := reactor.GetChannels()
	for _, chDesc := range reactorChannels {
		chId := chDesc.Id
		if sw.reactorsByCh[chId] != nil {
			panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, sw.reactorsByCh[chId], reactor))
		}
		sw.chDescs = append(sw.chDescs, chDesc)
		sw.reactorsByCh[chId] = reactor
	}
	sw.reactors[name] = reactor
	return reactor
}

// Not goroutine safe.
func (sw *Switch) Reactors() map[string]Reactor {
	return sw.reactors
}

// Not goroutine safe.
func (sw *Switch) Reactor(name string) Reactor {
	return sw.reactors[name]
}

// Not goroutine safe.
func (sw *Switch) AddListener(l Listener) {
	sw.listeners = append(sw.listeners, l)
}

// Not goroutine safe.
func (sw *Switch) Listeners() []Listener {
	return sw.listeners
}

// Not goroutine safe.
func (sw *Switch) IsListening() bool {
	return len(sw.listeners) > 0
}

// Not goroutine safe.
func (sw *Switch) SetNodeInfo(nodeInfo *types.NodeInfo) {
	sw.nodeInfo = nodeInfo
}

// Not goroutine safe.
func (sw *Switch) NodeInfo() *types.NodeInfo {
	return sw.nodeInfo
}

// Not goroutine safe.
// NOTE: Overwrites sw.nodeInfo.PubKey
func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) {
	sw.nodePrivKey = nodePrivKey
	if sw.nodeInfo != nil {
		sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519)
	}
}

func (sw *Switch) Start() {
	if atomic.CompareAndSwapUint32(&sw.running, 0, 1) {
		// Start reactors
		for _, reactor := range sw.reactors {
			reactor.Start(sw)
		}
		// Start peers
		for _, peer := range sw.peers.List() {
			sw.startInitPeer(peer)
		}
		// Start listeners
		for _, listener := range sw.listeners {
			go sw.listenerRoutine(listener)
		}
	}
}

func (sw *Switch) Stop() {
	if atomic.CompareAndSwapUint32(&sw.running, 1, 0) {
		// Stop listeners
		for _, listener := range sw.listeners {
			listener.Stop()
		}
		sw.listeners = nil
		// Stop peers
		for _, peer := range sw.peers.List() {
			peer.stop()
		}
		sw.peers = NewPeerSet()
		// Stop reactors
		for _, reactor := range sw.reactors {
			reactor.Stop()
		}
	}
}

// NOTE: This performs a blocking handshake before the peer is added.
// CONTRACT: Iff error is returned, peer is nil, and conn is immediately closed.
func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
	// Set deadline for handshake so we don't block forever on conn.ReadFull
	conn.SetDeadline(time.Now().Add(handshakeTimeoutSeconds * time.Second))

	// First, encrypt the connection.
	sconn, err := MakeSecretConnection(conn, sw.nodePrivKey)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// Then, perform node handshake
	peerNodeInfo, err := peerHandshake(sconn, sw.nodeInfo)
	if err != nil {
		sconn.Close()
		return nil, err
	}
	// Check that the professed PubKey matches the sconn's.
	if !peerNodeInfo.PubKey.Equals(sconn.RemotePubKey()) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
			peerNodeInfo.PubKey, sconn.RemotePubKey())
	}
	// Avoid self
	if peerNodeInfo.PubKey.Equals(sw.nodeInfo.PubKey) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection from self")
	}
	// Check version, chain id
	if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil {
		sconn.Close()
		return nil, err
	}

	// The peerNodeInfo is not verified, so overwrite
	// the IP, and the port too if we dialed out
	// Everything else we just have to trust
	ip, port, _ := net.SplitHostPort(sconn.RemoteAddr().String())
	peerNodeInfo.Host = ip
	if outbound {
		porti, _ := strconv.Atoi(port)
		peerNodeInfo.P2PPort = uint16(porti)
	}
	peer := newPeer(sconn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)

	// Add the peer to .peers
	// ignore if duplicate or if we already have too many for that IP range
	if err := sw.peers.Add(peer); err != nil {
		log.Info("Ignoring peer", "error", err, "peer", peer)
		peer.stop() // will also close sconn
		return nil, err
	}

	// remove deadline and start peer
	conn.SetDeadline(time.Time{})
	if atomic.LoadUint32(&sw.running) == 1 {
		sw.startInitPeer(peer)
	}

	log.Info("Added peer", "peer", peer)
	return peer, nil
}

func (sw *Switch) startInitPeer(peer *Peer) {
	peer.start()               // spawn send/recv routines
	sw.addPeerToReactors(peer) // run AddPeer on each reactor
}

func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) {
	log.Debug("Dialing address", "address", addr)
	sw.dialing.Set(addr.IP.String(), addr)
	conn, err := addr.DialTimeout(peerDialTimeoutSeconds * time.Second)
	sw.dialing.Delete(addr.IP.String())
	if err != nil {
		log.Debug("Failed dialing address", "address", addr, "error", err)
		return nil, err
	}
	peer, err := sw.AddPeerWithConnection(conn, true)
	if err != nil {
		log.Debug("Failed adding peer", "address", addr, "conn", conn, "error", err)
		return nil, err
	}
	log.Info("Dialed and added peer", "address", addr, "peer", peer)
	return peer, nil
}

func (sw *Switch) IsDialing(addr *NetAddress) bool {
	return sw.dialing.Has(addr.IP.String())
}

// Broadcast runs a go routine for each attempted send, which will block
// trying to send for defaultSendTimeoutSeconds. Returns a channel
// which receives success values for each attempted send (false if times out)
func (sw *Switch) Broadcast(chId byte, msg interface{}) chan bool {
	successChan := make(chan bool, len(sw.peers.List()))
	log.Debug("Broadcast", "channel", chId, "msg", msg)
	for _, peer := range sw.peers.List() {
		go func(peer *Peer) {
			success := peer.Send(chId, msg)
			successChan <- success
		}(peer)
	}
	return successChan

}

// Returns the count of outbound/inbound and outbound-dialing peers.
func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
	peers := sw.peers.List()
	for _, peer := range peers {
		if peer.outbound {
			outbound++
		} else {
			inbound++
		}
	}
	dialing = sw.dialing.Size()
	return
}

func (sw *Switch) Peers() IPeerSet {
	return sw.peers
}

// Disconnect from a peer due to external error.
// TODO: make record depending on reason.
func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) {
	log.Info("Stopping peer for error", "peer", peer, "error", reason)
	sw.peers.Remove(peer)
	peer.stop()
	sw.removePeerFromReactors(peer, reason)
}

// Disconnect from a peer gracefully.
// TODO: handle graceful disconnects.
func (sw *Switch) StopPeerGracefully(peer *Peer) {
	log.Info("Stopping peer gracefully")
	sw.peers.Remove(peer)
	peer.stop()
	sw.removePeerFromReactors(peer, nil)
}

func (sw *Switch) addPeerToReactors(peer *Peer) {
	for _, reactor := range sw.reactors {
		reactor.AddPeer(peer)
	}
}

func (sw *Switch) removePeerFromReactors(peer *Peer, reason interface{}) {
	for _, reactor := range sw.reactors {
		reactor.RemovePeer(peer, reason)
	}
}

func (sw *Switch) listenerRoutine(l Listener) {
	for {
		inConn, ok := <-l.Connections()
		if !ok {
			break
		}

		// ignore connection if we already have enough
		if maxNumPeers <= sw.peers.Size() {
			log.Debug("Ignoring inbound connection: already have enough peers", "conn", inConn, "numPeers", sw.peers.Size(), "max", maxNumPeers)
			continue
		}

		// Ignore connections from IP ranges for which we have too many
		if sw.peers.HasMaxForIPRange(inConn) {
			log.Debug("Ignoring inbound connection: already have enough peers for that IP range", "address", inConn.RemoteAddr().String())
			continue
		}

		// New inbound connection!
		_, err := sw.AddPeerWithConnection(inConn, false)
		if err != nil {
			log.Info("Ignoring inbound connection: error on AddPeerWithConnection", "conn", inConn, "error", err)
			continue
		}

		// NOTE: We don't yet have the listening port of the
		// remote (if they have a listener at all).
		// The peerHandshake will handle that
	}

	// cleanup
}

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

type SwitchEventNewPeer struct {
	Peer *Peer
}

type SwitchEventDonePeer struct {
	Peer  *Peer
	Error interface{}
}
<commit_msg>actually stop peer on failed Add<commit_after>package p2p

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"sync/atomic"
	"time"

	acm "github.com/tendermint/tendermint/account"
	. "github.com/tendermint/tendermint/common"
	"github.com/tendermint/tendermint/types"
)

type Reactor interface {
	Start(sw *Switch)
	Stop()
	GetChannels() []*ChannelDescriptor
	AddPeer(peer *Peer)
	RemovePeer(peer *Peer, reason interface{})
	Receive(chId byte, peer *Peer, msgBytes []byte)
}

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

type BaseReactor struct{}

func (_ BaseReactor) Start(sw *Switch)                               {}
func (_ BaseReactor) Stop()                                          {}
func (_ BaseReactor) GetChannels() []*ChannelDescriptor              { return nil }
func (_ BaseReactor) AddPeer(peer *Peer)                             {}
func (_ BaseReactor) RemovePeer(peer *Peer, reason interface{})      {}
func (_ BaseReactor) Receive(chId byte, peer *Peer, msgBytes []byte) {}

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

/*
The `Switch` handles peer connections and exposes an API to receive incoming messages
on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
incoming messages are received on the reactor.
*/
type Switch struct {
	listeners    []Listener
	reactors     map[string]Reactor
	chDescs      []*ChannelDescriptor
	reactorsByCh map[byte]Reactor
	peers        *PeerSet
	dialing      *CMap
	running      uint32
	nodeInfo     *types.NodeInfo    // our node info
	nodePrivKey  acm.PrivKeyEd25519 // our node privkey
}

var (
	ErrSwitchDuplicatePeer      = errors.New("Duplicate peer")
	ErrSwitchMaxPeersPerIPRange = errors.New("IP range has too many peers")
)

const (
	peerDialTimeoutSeconds  = 3  // TODO make this configurable
	handshakeTimeoutSeconds = 20 // TODO make this configurable
	maxNumPeers             = 50 // TODO make this configurable
)

func NewSwitch() *Switch {
	sw := &Switch{
		reactors:     make(map[string]Reactor),
		chDescs:      make([]*ChannelDescriptor, 0),
		reactorsByCh: make(map[byte]Reactor),
		peers:        NewPeerSet(),
		dialing:      NewCMap(),
		running:      0,
		nodeInfo:     nil,
	}
	return sw
}

// Not goroutine safe.
func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor {
	// Validate the reactor.
	// No two reactors can share the same channel.
	reactorChannels := reactor.GetChannels()
	for _, chDesc := range reactorChannels {
		chId := chDesc.Id
		if sw.reactorsByCh[chId] != nil {
			panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, sw.reactorsByCh[chId], reactor))
		}
		sw.chDescs = append(sw.chDescs, chDesc)
		sw.reactorsByCh[chId] = reactor
	}
	sw.reactors[name] = reactor
	return reactor
}

// Not goroutine safe.
func (sw *Switch) Reactors() map[string]Reactor {
	return sw.reactors
}

// Not goroutine safe.
func (sw *Switch) Reactor(name string) Reactor {
	return sw.reactors[name]
}

// Not goroutine safe.
func (sw *Switch) AddListener(l Listener) {
	sw.listeners = append(sw.listeners, l)
}

// Not goroutine safe.
func (sw *Switch) Listeners() []Listener {
	return sw.listeners
}

// Not goroutine safe.
func (sw *Switch) IsListening() bool {
	return len(sw.listeners) > 0
}

// Not goroutine safe.
func (sw *Switch) SetNodeInfo(nodeInfo *types.NodeInfo) {
	sw.nodeInfo = nodeInfo
}

// Not goroutine safe.
func (sw *Switch) NodeInfo() *types.NodeInfo {
	return sw.nodeInfo
}

// Not goroutine safe.
// NOTE: Overwrites sw.nodeInfo.PubKey
func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) {
	sw.nodePrivKey = nodePrivKey
	if sw.nodeInfo != nil {
		sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519)
	}
}

func (sw *Switch) Start() {
	if atomic.CompareAndSwapUint32(&sw.running, 0, 1) {
		// Start reactors
		for _, reactor := range sw.reactors {
			reactor.Start(sw)
		}
		// Start peers
		for _, peer := range sw.peers.List() {
			sw.startInitPeer(peer)
		}
		// Start listeners
		for _, listener := range sw.listeners {
			go sw.listenerRoutine(listener)
		}
	}
}

func (sw *Switch) Stop() {
	if atomic.CompareAndSwapUint32(&sw.running, 1, 0) {
		// Stop listeners
		for _, listener := range sw.listeners {
			listener.Stop()
		}
		sw.listeners = nil
		// Stop peers
		for _, peer := range sw.peers.List() {
			peer.stop()
		}
		sw.peers = NewPeerSet()
		// Stop reactors
		for _, reactor := range sw.reactors {
			reactor.Stop()
		}
	}
}

// NOTE: This performs a blocking handshake before the peer is added.
// CONTRACT: Iff error is returned, peer is nil, and conn is immediately closed.
func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
	// Set deadline for handshake so we don't block forever on conn.ReadFull
	conn.SetDeadline(time.Now().Add(handshakeTimeoutSeconds * time.Second))

	// First, encrypt the connection.
	sconn, err := MakeSecretConnection(conn, sw.nodePrivKey)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// Then, perform node handshake
	peerNodeInfo, err := peerHandshake(sconn, sw.nodeInfo)
	if err != nil {
		sconn.Close()
		return nil, err
	}
	// Check that the professed PubKey matches the sconn's.
	if !peerNodeInfo.PubKey.Equals(sconn.RemotePubKey()) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
			peerNodeInfo.PubKey, sconn.RemotePubKey())
	}
	// Avoid self
	if peerNodeInfo.PubKey.Equals(sw.nodeInfo.PubKey) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection from self")
	}
	// Check version, chain id
	if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil {
		sconn.Close()
		return nil, err
	}

	// The peerNodeInfo is not verified, so overwrite
	// the IP, and the port too if we dialed out
	// Everything else we just have to trust
	ip, port, _ := net.SplitHostPort(sconn.RemoteAddr().String())
	peerNodeInfo.Host = ip
	if outbound {
		porti, _ := strconv.Atoi(port)
		peerNodeInfo.P2PPort = uint16(porti)
	}
	peer := newPeer(sconn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)

	// Add the peer to .peers
	// ignore if duplicate or if we already have too many for that IP range
	if err := sw.peers.Add(peer); err != nil {
		log.Info("Ignoring peer", "error", err, "peer", peer)
		peer.mconn.Stop()
		return nil, err
	}

	// remove deadline and start peer
	conn.SetDeadline(time.Time{})
	if atomic.LoadUint32(&sw.running) == 1 {
		sw.startInitPeer(peer)
	}

	log.Info("Added peer", "peer", peer)
	return peer, nil
}

func (sw *Switch) startInitPeer(peer *Peer) {
	peer.start()               // spawn send/recv routines
	sw.addPeerToReactors(peer) // run AddPeer on each reactor
}

func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) {
	log.Debug("Dialing address", "address", addr)
	sw.dialing.Set(addr.IP.String(), addr)
	conn, err := addr.DialTimeout(peerDialTimeoutSeconds * time.Second)
	sw.dialing.Delete(addr.IP.String())
	if err != nil {
		log.Debug("Failed dialing address", "address", addr, "error", err)
		return nil, err
	}
	peer, err := sw.AddPeerWithConnection(conn, true)
	if err != nil {
		log.Debug("Failed adding peer", "address", addr, "conn", conn, "error", err)
		return nil, err
	}
	log.Info("Dialed and added peer", "address", addr, "peer", peer)
	return peer, nil
}

func (sw *Switch) IsDialing(addr *NetAddress) bool {
	return sw.dialing.Has(addr.IP.String())
}

// Broadcast runs a go routine for each attempted send, which will block
// trying to send for defaultSendTimeoutSeconds. Returns a channel
// which receives success values for each attempted send (false if times out)
func (sw *Switch) Broadcast(chId byte, msg interface{}) chan bool {
	successChan := make(chan bool, len(sw.peers.List()))
	log.Debug("Broadcast", "channel", chId, "msg", msg)
	for _, peer := range sw.peers.List() {
		go func(peer *Peer) {
			success := peer.Send(chId, msg)
			successChan <- success
		}(peer)
	}
	return successChan

}

// Returns the count of outbound/inbound and outbound-dialing peers.
func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
	peers := sw.peers.List()
	for _, peer := range peers {
		if peer.outbound {
			outbound++
		} else {
			inbound++
		}
	}
	dialing = sw.dialing.Size()
	return
}

func (sw *Switch) Peers() IPeerSet {
	return sw.peers
}

// Disconnect from a peer due to external error.
// TODO: make record depending on reason.
func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) {
	log.Info("Stopping peer for error", "peer", peer, "error", reason)
	sw.peers.Remove(peer)
	peer.stop()
	sw.removePeerFromReactors(peer, reason)
}

// Disconnect from a peer gracefully.
// TODO: handle graceful disconnects.
func (sw *Switch) StopPeerGracefully(peer *Peer) {
	log.Info("Stopping peer gracefully")
	sw.peers.Remove(peer)
	peer.stop()
	sw.removePeerFromReactors(peer, nil)
}

func (sw *Switch) addPeerToReactors(peer *Peer) {
	for _, reactor := range sw.reactors {
		reactor.AddPeer(peer)
	}
}

func (sw *Switch) removePeerFromReactors(peer *Peer, reason interface{}) {
	for _, reactor := range sw.reactors {
		reactor.RemovePeer(peer, reason)
	}
}

func (sw *Switch) listenerRoutine(l Listener) {
	for {
		inConn, ok := <-l.Connections()
		if !ok {
			break
		}

		// ignore connection if we already have enough
		if maxNumPeers <= sw.peers.Size() {
			log.Debug("Ignoring inbound connection: already have enough peers", "conn", inConn, "numPeers", sw.peers.Size(), "max", maxNumPeers)
			continue
		}

		// Ignore connections from IP ranges for which we have too many
		if sw.peers.HasMaxForIPRange(inConn) {
			log.Debug("Ignoring inbound connection: already have enough peers for that IP range", "address", inConn.RemoteAddr().String())
			continue
		}

		// New inbound connection!
		_, err := sw.AddPeerWithConnection(inConn, false)
		if err != nil {
			log.Info("Ignoring inbound connection: error on AddPeerWithConnection", "conn", inConn, "error", err)
			continue
		}

		// NOTE: We don't yet have the listening port of the
		// remote (if they have a listener at all).
		// The peerHandshake will handle that
	}

	// cleanup
}

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

type SwitchEventNewPeer struct {
	Peer *Peer
}

type SwitchEventDonePeer struct {
	Peer  *Peer
	Error interface{}
}
<|endoftext|>