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

package views

import (
	"encoding/json"
	"strings"

	"github.com/ernestio/api-gateway/models"
	"log"

	graph "gopkg.in/r3labs/graph.v2"
)

// ServiceRender : Service representation to be rendered on the frontend
type ServiceRender struct {
	ID              string              `json:"id"`
	DatacenterID    int                 `json:"datacenter_id"`
	Name            string              `json:"name"`
	Version         string              `json:"version"`
	Status          string              `json:"status"`
	UserID          int                 `json:"user_id"`
	UserName        string              `json:"user_name"`
	LastKnownError  string              `json:"last_known_error"`
	Options         string              `json:"options"`
	Definition      string              `json:"definition"`
	Vpcs            []map[string]string `json:"vpcs"`
	Networks        []map[string]string `json:"networks"`
	Instances       []map[string]string `json:"instances"`
	Nats            []map[string]string `json:"nats"`
	SecurityGroups  []map[string]string `json:"security_groups"`
	Elbs            []map[string]string `json:"elbs"`
	RDSClusters     []map[string]string `json:"rds_clusters"`
	RDSInstances    []map[string]string `json:"rds_instances"`
	EBSVolumes      []map[string]string `json:"ebs_volumes"`
	LoadBalancers   []map[string]string `json:"load_balancers"`
	SQLDatabases    []map[string]string `json:"sql_databases"`
	VirtualMachines []map[string]string `json:"virtual_machines"`
}

// Render : Map a Service to a ServiceRender
func (o *ServiceRender) Render(s models.Service) (err error) {
	o.ID = s.ID
	o.DatacenterID = s.DatacenterID
	o.Name = s.Name
	o.Version = s.Version.String()
	o.Status = s.Status
	o.UserID = s.UserID
	o.UserName = s.UserName
	if def, ok := s.Definition.(string); ok == true {
		o.Definition = def
	}

	g, err := s.Mapping()
	if err != nil {
		log.Println(err.Error())
		return err
	}

	o.Vpcs = RenderVpcs(g)
	o.Networks = RenderNetworks(g)
	o.SecurityGroups = RenderSecurityGroups(g)
	o.Nats = RenderNats(g)
	o.Instances = RenderInstances(g)
	o.Elbs = RenderELBs(g)
	o.RDSClusters = RenderRDSClusters(g)
	o.RDSInstances = RenderRDSInstances(g)
	o.EBSVolumes = RenderEBSVolumes(g)
	o.LoadBalancers = RenderLoadBalancers(g)
	o.SQLDatabases = RenderSQLDatabases(g)
	o.VirtualMachines = RenderVirtualMachines(g)

	return err
}

// RenderVpcs : renders a services vpcs
func RenderVpcs(g *graph.Graph) []map[string]string {
	var vpcs []map[string]string

	for _, n := range g.GetComponents().ByType("vpc") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["vpc_aws_id"].(string)
		subnet, _ := (*gc)["vpc_subnet"].(string)
		vpcs = append(vpcs, map[string]string{
			"name":       name,
			"vpc_id":     id,
			"vpc_subnet": subnet,
		})
	}

	return vpcs
}

// RenderNetworks : renders a services networks
func RenderNetworks(g *graph.Graph) []map[string]string {
	var networks []map[string]string

	for _, n := range g.GetComponents().ByType("network") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["network_aws_id"].(string)
		az, _ := (*gc)["availability_zone"].(string)
		networks = append(networks, map[string]string{
			"name":              name,
			"network_aws_id":    id,
			"availability_zone": az,
		})
	}

	return networks
}

// RenderSecurityGroups : renders a services security groups
func RenderSecurityGroups(g *graph.Graph) []map[string]string {
	var sgs []map[string]string

	for _, n := range g.GetComponents().ByType("firewall") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["security_group_aws_id"].(string)
		sgs = append(sgs, map[string]string{
			"name":                  name,
			"security_group_aws_id": id,
		})
	}

	return sgs
}

// RenderNats : renders a services nat gateways
func RenderNats(g *graph.Graph) []map[string]string {
	var nats []map[string]string

	for _, n := range g.GetComponents().ByType("nat") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["nat_gateway_aws_id"].(string)
		pubIP, _ := (*gc)["nat_gateway_allocation_ip"].(string)
		nats = append(nats, map[string]string{
			"name":               name,
			"nat_gateway_aws_id": id,
			"public_ip":          pubIP,
		})
	}

	return nats
}

// RenderELBs : renders a services elbs
func RenderELBs(g *graph.Graph) []map[string]string {
	var elbs []map[string]string

	for _, n := range g.GetComponents().ByType("elb") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		dns, _ := (*gc)["dns_name"].(string)
		elbs = append(elbs, map[string]string{
			"name":     name,
			"dns_name": dns,
		})
	}

	return elbs
}

// RenderInstances : renders a services instances
func RenderInstances(g *graph.Graph) []map[string]string {
	var instances []map[string]string

	for _, n := range g.GetComponents().ByType("instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["instance_aws_id"].(string)
		pip, _ := (*gc)["public_ip"].(string)
		ip, _ := (*gc)["ip"].(string)
		instances = append(instances, map[string]string{
			"name":            name,
			"instance_aws_id": id,
			"public_ip":       pip,
			"ip":              ip,
		})
	}

	return instances
}

// RenderRDSClusters : renders a services rds clusters
func RenderRDSClusters(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_cluster") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderRDSInstances : renders a services rds instances
func RenderRDSInstances(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderEBSVolumes : renders a services ebs volumes
func RenderEBSVolumes(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("ebs_volume") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["volume_aws_id"].(string)
		rdss = append(rdss, map[string]string{
			"name":          name,
			"volume_aws_id": id,
		})
	}

	return rdss
}

// RenderLoadBalancers : renders load balancers
func RenderLoadBalancers(g *graph.Graph) []map[string]string {
	return renderResources(g, "lb", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		configs, _ := (*gc)["frontend_ip_configurations"].([]interface{})
		cfg, _ := configs[0].(map[string]string)

		return map[string]string{
			"name":      name,
			"id":        id,
			"public_ip": cfg["public_ip_address"],
		}
	})
}

// RenderVirtualMachines : renders virtual machines
func RenderVirtualMachines(g *graph.Graph) []map[string]string {
	var resources []map[string]string
	mappedIPs := make(map[string]interface{}, 0)
	existingIPs := make(map[string]string, 0)

	for _, ip := range g.GetComponents().ByType("public_ip") {
		gc := ip.(*graph.GenericComponent)
		id, _ := (*gc)["id"].(string)
		ipAddress, _ := (*gc)["ip_address"].(string)
		existingIPs[id] = ipAddress
	}

	for _, ni := range g.GetComponents().ByType("network_interface") {
		var public []string
		var private []string

		gc := ni.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		ips := make(map[string][]string)

		configs, _ := (*gc)["ip_configuration"].([]interface{})
		for _, cfg := range configs {
			c, _ := cfg.(map[string]interface{})
			pubID, _ := c["public_ip_address_id"].(string)
			pri, _ := c["private_ip_address"].(string)
			if pub, ok := existingIPs[pubID]; ok {
				public = append(public, pub)
			}
			private = append(private, pri)
		}

		ips["public"] = public
		ips["private"] = private
		mappedIPs[name] = make(map[string][]string, 0)
		mappedIPs[name] = ips
	}

	for _, n := range g.GetComponents().ByType("virtual_machine") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		networks, _ := (*gc)["network_interfaces"].([]interface{})
		publicIPs := make([]string, 0)
		privateIPs := make([]string, 0)
		for _, ni := range networks {
			netName := ni.(string)
			if val, ok := mappedIPs[netName]; ok {
				ips, _ := val.(map[string][]string)
				publicIPs = append(publicIPs, ips["public"]...)
				privateIPs = append(privateIPs, ips["private"]...)
			}
		}

		resources = append(resources, map[string]string{
			"name":       name,
			"id":         id,
			"public_ip":  strings.Join(publicIPs, ", "),
			"private_ip": strings.Join(privateIPs, ", "),
		})
	}

	return resources
}

// RenderSQLDatabases : renders sql databases
func RenderSQLDatabases(g *graph.Graph) []map[string]string {
	return renderResources(g, "sql_database", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		server, _ := (*gc)["server_name"].(string)
		id, _ := (*gc)["id"].(string)

		return map[string]string{
			"name":        name,
			"server_name": server,
			"id":          id,
		}
	})
}

type convert func(*graph.GenericComponent) map[string]string

func renderResources(g *graph.Graph, resourceType string, f convert) (resources []map[string]string) {
	for _, n := range g.GetComponents().ByType(resourceType) {
		gc := n.(*graph.GenericComponent)
		resources = append(resources, f(gc))
	}

	return
}

// RenderCollection : Maps a collection of Service on a collection of ServiceRender
func (o *ServiceRender) RenderCollection(services []models.Service) (list []ServiceRender, err error) {
	for _, s := range services {
		var output ServiceRender
		if err := output.Render(s); err == nil {
			list = append(list, output)
		}
	}

	return list, nil
}

// ToJSON : Converts a ServiceRender to json string
func (o *ServiceRender) ToJSON() ([]byte, error) {
	return json.Marshal(o)
}
<commit_msg>Calculate loadbalancer IP<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package views

import (
	"encoding/json"
	"strings"

	"github.com/ernestio/api-gateway/models"
	"log"

	graph "gopkg.in/r3labs/graph.v2"
)

// ServiceRender : Service representation to be rendered on the frontend
type ServiceRender struct {
	ID              string              `json:"id"`
	DatacenterID    int                 `json:"datacenter_id"`
	Name            string              `json:"name"`
	Version         string              `json:"version"`
	Status          string              `json:"status"`
	UserID          int                 `json:"user_id"`
	UserName        string              `json:"user_name"`
	LastKnownError  string              `json:"last_known_error"`
	Options         string              `json:"options"`
	Definition      string              `json:"definition"`
	Vpcs            []map[string]string `json:"vpcs"`
	Networks        []map[string]string `json:"networks"`
	Instances       []map[string]string `json:"instances"`
	Nats            []map[string]string `json:"nats"`
	SecurityGroups  []map[string]string `json:"security_groups"`
	Elbs            []map[string]string `json:"elbs"`
	RDSClusters     []map[string]string `json:"rds_clusters"`
	RDSInstances    []map[string]string `json:"rds_instances"`
	EBSVolumes      []map[string]string `json:"ebs_volumes"`
	LoadBalancers   []map[string]string `json:"load_balancers"`
	SQLDatabases    []map[string]string `json:"sql_databases"`
	VirtualMachines []map[string]string `json:"virtual_machines"`
}

// Render : Map a Service to a ServiceRender
func (o *ServiceRender) Render(s models.Service) (err error) {
	o.ID = s.ID
	o.DatacenterID = s.DatacenterID
	o.Name = s.Name
	o.Version = s.Version.String()
	o.Status = s.Status
	o.UserID = s.UserID
	o.UserName = s.UserName
	if def, ok := s.Definition.(string); ok == true {
		o.Definition = def
	}

	g, err := s.Mapping()
	if err != nil {
		log.Println(err.Error())
		return err
	}

	o.Vpcs = RenderVpcs(g)
	o.Networks = RenderNetworks(g)
	o.SecurityGroups = RenderSecurityGroups(g)
	o.Nats = RenderNats(g)
	o.Instances = RenderInstances(g)
	o.Elbs = RenderELBs(g)
	o.RDSClusters = RenderRDSClusters(g)
	o.RDSInstances = RenderRDSInstances(g)
	o.EBSVolumes = RenderEBSVolumes(g)
	o.LoadBalancers = RenderLoadBalancers(g)
	o.SQLDatabases = RenderSQLDatabases(g)
	o.VirtualMachines = RenderVirtualMachines(g)

	return err
}

// RenderVpcs : renders a services vpcs
func RenderVpcs(g *graph.Graph) []map[string]string {
	var vpcs []map[string]string

	for _, n := range g.GetComponents().ByType("vpc") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["vpc_aws_id"].(string)
		subnet, _ := (*gc)["vpc_subnet"].(string)
		vpcs = append(vpcs, map[string]string{
			"name":       name,
			"vpc_id":     id,
			"vpc_subnet": subnet,
		})
	}

	return vpcs
}

// RenderNetworks : renders a services networks
func RenderNetworks(g *graph.Graph) []map[string]string {
	var networks []map[string]string

	for _, n := range g.GetComponents().ByType("network") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["network_aws_id"].(string)
		az, _ := (*gc)["availability_zone"].(string)
		networks = append(networks, map[string]string{
			"name":              name,
			"network_aws_id":    id,
			"availability_zone": az,
		})
	}

	return networks
}

// RenderSecurityGroups : renders a services security groups
func RenderSecurityGroups(g *graph.Graph) []map[string]string {
	var sgs []map[string]string

	for _, n := range g.GetComponents().ByType("firewall") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["security_group_aws_id"].(string)
		sgs = append(sgs, map[string]string{
			"name":                  name,
			"security_group_aws_id": id,
		})
	}

	return sgs
}

// RenderNats : renders a services nat gateways
func RenderNats(g *graph.Graph) []map[string]string {
	var nats []map[string]string

	for _, n := range g.GetComponents().ByType("nat") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["nat_gateway_aws_id"].(string)
		pubIP, _ := (*gc)["nat_gateway_allocation_ip"].(string)
		nats = append(nats, map[string]string{
			"name":               name,
			"nat_gateway_aws_id": id,
			"public_ip":          pubIP,
		})
	}

	return nats
}

// RenderELBs : renders a services elbs
func RenderELBs(g *graph.Graph) []map[string]string {
	var elbs []map[string]string

	for _, n := range g.GetComponents().ByType("elb") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		dns, _ := (*gc)["dns_name"].(string)
		elbs = append(elbs, map[string]string{
			"name":     name,
			"dns_name": dns,
		})
	}

	return elbs
}

// RenderInstances : renders a services instances
func RenderInstances(g *graph.Graph) []map[string]string {
	var instances []map[string]string

	for _, n := range g.GetComponents().ByType("instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["instance_aws_id"].(string)
		pip, _ := (*gc)["public_ip"].(string)
		ip, _ := (*gc)["ip"].(string)
		instances = append(instances, map[string]string{
			"name":            name,
			"instance_aws_id": id,
			"public_ip":       pip,
			"ip":              ip,
		})
	}

	return instances
}

// RenderRDSClusters : renders a services rds clusters
func RenderRDSClusters(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_cluster") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderRDSInstances : renders a services rds instances
func RenderRDSInstances(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderEBSVolumes : renders a services ebs volumes
func RenderEBSVolumes(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("ebs_volume") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["volume_aws_id"].(string)
		rdss = append(rdss, map[string]string{
			"name":          name,
			"volume_aws_id": id,
		})
	}

	return rdss
}

// RenderLoadBalancers : renders load balancers
func RenderLoadBalancers(g *graph.Graph) []map[string]string {
	var lbs []map[string]string
	ips := listIPAddresses(g)

	for _, n := range g.GetComponents().ByType("lb") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		configs, _ := (*gc)["frontend_ip_configurations"].([]interface{})
		cfg, _ := configs[0].(map[string]interface{})
		ipID, _ := cfg["public_ip_address_id"].(string)
		ip, _ := ips[ipID]

		lbs = append(lbs, map[string]string{
			"name":      name,
			"id":        id,
			"public_ip": ip,
		})
	}

	return lbs
}

func listIPAddresses(g *graph.Graph) map[string]string {
	existingIPs := make(map[string]string, 0)

	for _, ip := range g.GetComponents().ByType("public_ip") {
		gc := ip.(*graph.GenericComponent)
		id, _ := (*gc)["id"].(string)
		ipAddress, _ := (*gc)["ip_address"].(string)
		existingIPs[id] = ipAddress
	}

	return existingIPs
}

// RenderVirtualMachines : renders virtual machines
func RenderVirtualMachines(g *graph.Graph) []map[string]string {
	var resources []map[string]string
	mappedIPs := make(map[string]interface{}, 0)
	existingIPs := listIPAddresses(g)

	for _, ni := range g.GetComponents().ByType("network_interface") {
		var public []string
		var private []string

		gc := ni.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		ips := make(map[string][]string)

		configs, _ := (*gc)["ip_configuration"].([]interface{})
		for _, cfg := range configs {
			c, _ := cfg.(map[string]interface{})
			pubID, _ := c["public_ip_address_id"].(string)
			pri, _ := c["private_ip_address"].(string)
			if pub, ok := existingIPs[pubID]; ok {
				public = append(public, pub)
			}
			private = append(private, pri)
		}

		ips["public"] = public
		ips["private"] = private
		mappedIPs[name] = make(map[string][]string, 0)
		mappedIPs[name] = ips
	}

	for _, n := range g.GetComponents().ByType("virtual_machine") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		networks, _ := (*gc)["network_interfaces"].([]interface{})
		publicIPs := make([]string, 0)
		privateIPs := make([]string, 0)
		for _, ni := range networks {
			netName := ni.(string)
			if val, ok := mappedIPs[netName]; ok {
				ips, _ := val.(map[string][]string)
				publicIPs = append(publicIPs, ips["public"]...)
				privateIPs = append(privateIPs, ips["private"]...)
			}
		}

		resources = append(resources, map[string]string{
			"name":       name,
			"id":         id,
			"public_ip":  strings.Join(publicIPs, ", "),
			"private_ip": strings.Join(privateIPs, ", "),
		})
	}

	return resources
}

// RenderSQLDatabases : renders sql databases
func RenderSQLDatabases(g *graph.Graph) []map[string]string {
	return renderResources(g, "sql_database", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		server, _ := (*gc)["server_name"].(string)
		id, _ := (*gc)["id"].(string)

		return map[string]string{
			"name":        name,
			"server_name": server,
			"id":          id,
		}
	})
}

type convert func(*graph.GenericComponent) map[string]string

func renderResources(g *graph.Graph, resourceType string, f convert) (resources []map[string]string) {
	for _, n := range g.GetComponents().ByType(resourceType) {
		gc := n.(*graph.GenericComponent)
		resources = append(resources, f(gc))
	}

	return
}

// RenderCollection : Maps a collection of Service on a collection of ServiceRender
func (o *ServiceRender) RenderCollection(services []models.Service) (list []ServiceRender, err error) {
	for _, s := range services {
		var output ServiceRender
		if err := output.Render(s); err == nil {
			list = append(list, output)
		}
	}

	return list, nil
}

// ToJSON : Converts a ServiceRender to json string
func (o *ServiceRender) ToJSON() ([]byte, error) {
	return json.Marshal(o)
}
<|endoftext|>


<commit_before>package dht

import (
	"context"
	"encoding/json"
	"strings"
	"sync"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"

	messagebus "github.com/nimona/go-nimona-messagebus"
	net "github.com/nimona/go-nimona-net"
)

const (
	protocolID = "dht-kad/v1"
)

// DHTNode is the struct that implements the dht protocol
type DHTNode struct {
	localPeer  net.Peer
	store      *Store
	net        net.Network
	messageBus messagebus.MessageBus
	queries    map[string]*query
	mt         sync.RWMutex
}

func NewDHTNode(bps []net.Peer, lp net.Peer, nn net.Network) (*DHTNode, error) {
	// create new routing table
	st, _ := newStore()

	// create messagebud
	mb, err := messagebus.New(protocolID, nn)
	if err != nil {
		return nil, err
	}

	// Create DHT node
	nd := &DHTNode{
		localPeer:  lp,
		store:      st,
		net:        nn,
		messageBus: mb,
		queries:    map[string]*query{},
	}

	// Register message bus, message handler
	if err := mb.HandleMessage(nd.messageHandler); err != nil {
		return nil, err
	}

	// Add bootstrap nodes
	for _, peer := range bps {
		if err := nd.storePeer(peer, true); err != nil {
			logrus.WithField("error", err).Error("new could not store peer")
		}
		if err := nd.putPeer(peer); err != nil {
			logrus.WithField("error", err).Error("new could not put peer")
		}
	}

	// start refresh worker
	// ticker := time.NewTicker(15 * time.Second)
	// quit := make(chan struct{})
	go func() {
		// refresh for the first time
		nd.refresh()
		// and then just wait
		// for {
		// 	select {
		// 	case <-ticker.C:
		// 		nd.refresh()
		// 	case <-quit:
		// 		ticker.Stop()
		// 		return
		// 	}
		// }
	}()

	return nd, nil
}

func (nd *DHTNode) refresh() {
	logrus.Infof("Refreshing")
	cps, err := nd.store.FindKeysNearestTo(KeyPrefixPeer, nd.GetLocalPeer().ID, numPeersNear*10)
	if err != nil {
		logrus.WithError(err).Warnf("refresh could not get peers ids")
		return
	}
	ctx := context.Background()
	for _, cp := range cps {
		res, err := nd.Get(ctx, cp)
		if err != nil {
			logrus.WithError(err).WithField("peerID", cps).Warnf("refresh could not get for peer")
			continue
		}
		for range res {
			// just swallow channel results
		}
	}
}

func (nd *DHTNode) messageHandler(hash []byte, msg messagebus.Message) error {
	switch msg.Payload.Type {
	case MessageTypeGet:
		getMsg := &messageGet{}
		if err := json.Unmarshal(msg.Payload.Data, getMsg); err != nil {
			return err
		}
		nd.getHandler(getMsg)
	case MessageTypePut:
		putMsg := &messagePut{}
		if err := json.Unmarshal(msg.Payload.Data, putMsg); err != nil {
			return err
		}
		nd.putHandler(putMsg)
	default:
		logrus.Info("Call type not implemented")
	}
	return nil
}

func (nd *DHTNode) Get(ctx context.Context, key string) (chan string, error) {
	logrus.Infof("Searching for key %s", key)

	// create query
	// TODO query needs the context
	q := &query{
		id:               uuid.New().String(),
		dht:              nd,
		key:              key,
		contactedPeers:   []string{},
		results:          make(chan string, 100),
		incomingMessages: make(chan messagePut, 100),
		lock:             &sync.RWMutex{},
	}

	// and store it
	nd.mt.Lock()
	nd.queries[q.id] = q
	nd.mt.Unlock()

	// run query
	q.Run(ctx)

	// return results channel
	return q.results, nil
}

func (nd *DHTNode) GetPeer(ctx context.Context, id string) (net.Peer, error) {
	// get peer key
	res, err := nd.Get(ctx, getPeerKey(id))
	if err != nil {
		return net.Peer{}, err
	}

	// hold addresses
	addrs := []string{}

	// go through results and create addresses array
	for addr := range res {
		addrs = appendIfMissing(addrs, addr)
	}

	// check addrs
	if len(addrs) == 0 {
		return net.Peer{}, ErrPeerNotFound
	}

	return net.Peer{
		ID:        id,
		Addresses: addrs,
	}, nil
}

func (nd *DHTNode) sendMsgPeer(msgType string, msg interface{}, peerID string) error {
	if peerID == nd.localPeer.ID {
		return nil
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	pl := &messagebus.Payload{
		Creator: nd.localPeer.ID,
		Coded:   "json",
		Type:    msgType,
		Data:    data,
	}

	return nd.messageBus.Send(pl, []string{peerID})
}

func (nd *DHTNode) getHandler(msg *messageGet) {
	// origin peer is asking for a peer

	// store info on origin peer
	nd.storePeer(msg.OriginPeer, false)
	nd.putPeer(msg.OriginPeer)

	// check if we have the value of the key
	ks, err := nd.store.Get(msg.Key)
	if err != nil {
		logrus.WithField("msg", msg).Error("Failed to find nodes near")
		return
	}

	logrus.Infof("%+v", nd.store.pairs)

	// send them if we do
	if len(ks) > 0 {
		msgPut := &messagePut{
			QueryID:    msg.QueryID,
			OriginPeer: msg.OriginPeer,
			Key:        msg.Key,
			Values:     ks,
		}
		// send response
		if err := nd.sendMsgPeer(MessageTypePut, msgPut, msg.OriginPeer.ID); err != nil {
			logrus.WithError(err).Warnf("getHandler could not send msg")
		}
	}

	// find peers nearest peers that might have it
	cps, err := nd.store.FindKeysNearestTo(KeyPrefixPeer, msg.Key, numPeersNear)
	if err != nil {
		logrus.WithError(err).Error("getHandler could not find nearest peers")
		return
	}

	// give up if there are no peers
	if len(cps) == 0 {
		return
	}

	// send messages with closes peers
	for _, cp := range cps {
		cpid := trimKey(cp, KeyPrefixPeer)
		// skil us and original peer
		if cpid == msg.OriginPeer.ID || cpid == nd.GetLocalPeer().ID {
			continue
		}
		// get neighbor addresses
		addrs, err := nd.store.Get(cp)
		if err != nil {
			logrus.WithError(err).Warnf("getHandler could not get addrs")
			continue
		}
		// create a response
		msgPut := &messagePut{
			QueryID:    msg.QueryID,
			OriginPeer: msg.OriginPeer,
			Key:        cp,
			Values:     addrs,
		}
		// send response
		if err := nd.sendMsgPeer(MessageTypePut, msgPut, msg.OriginPeer.ID); err != nil {
			logrus.WithError(err).Warnf("getHandler could not send msg")
		}
	}
}

func (nd *DHTNode) putHandler(msg *messagePut) {
	// A peer we asked is informing us of a peer
	logrus.WithField("key", msg.Key).Infof("Got response")

	// check if this still a valid query
	if q, ok := nd.queries[msg.QueryID]; ok {
		q.incomingMessages <- *msg
	}

	// add values to our store
	if checkKey(msg.Key) {
		for _, v := range msg.Values {
			nd.store.Put(msg.Key, v, false)
		}
	}

	// check if this is a peer
	if strings.HasPrefix(msg.Key, KeyPrefixPeer) {
		pr, err := nd.gatherPeer(strings.Replace(msg.Key, KeyPrefixPeer, "", 1))
		if err != nil {
			logrus.WithError(err).Infof("putHandler could get pairs for putPeer")
			return
		}
		if err := nd.putPeer(pr); err != nil {
			logrus.WithError(err).Infof("putHandler could get putPeer")
			return
		}
	}
}

func (nd *DHTNode) gatherPeer(peerID string) (net.Peer, error) {
	addrs, err := nd.store.Get(peerID)
	if err != nil {
		return net.Peer{}, err
	}
	pr := net.Peer{
		ID:        peerID,
		Addresses: addrs,
	}
	return pr, nil
}

func (nd *DHTNode) putPeer(peer net.Peer) error {
	logrus.Infof("Adding peer to network peer=%v", peer)
	// add peer to network
	if err := nd.net.PutPeer(peer); err != nil {
		logrus.WithError(err).Warnf("Could not add peer to network")
		return err
	}
	logrus.Infof("PUT PEER %+v", peer)
	return nil
}

func (nd *DHTNode) storePeer(peer net.Peer, persistent bool) error {
	for _, addr := range peer.Addresses {
		logrus.WithField("k", getPeerKey(peer.ID)).WithField("v", addr).Infof("Adding peer addresses to kv")
		if err := nd.store.Put(getPeerKey(peer.ID), addr, persistent); err != nil {
			logrus.WithError(err).WithField("peerID", peer.ID).Warnf("storePeer could not put peer")
		}
	}
	return nil
}

func (nd *DHTNode) GetLocalPeer() net.Peer {
	return nd.localPeer
}
<commit_msg>Add refreshing<commit_after>package dht

import (
	"context"
	"encoding/json"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"

	messagebus "github.com/nimona/go-nimona-messagebus"
	net "github.com/nimona/go-nimona-net"
)

const (
	protocolID = "dht-kad/v1"
)

// DHTNode is the struct that implements the dht protocol
type DHTNode struct {
	localPeer  net.Peer
	store      *Store
	net        net.Network
	messageBus messagebus.MessageBus
	queries    map[string]*query
	mt         sync.RWMutex
}

func NewDHTNode(bps []net.Peer, lp net.Peer, nn net.Network) (*DHTNode, error) {
	// create new routing table
	st, _ := newStore()

	// create messagebud
	mb, err := messagebus.New(protocolID, nn)
	if err != nil {
		return nil, err
	}

	// Create DHT node
	nd := &DHTNode{
		localPeer:  lp,
		store:      st,
		net:        nn,
		messageBus: mb,
		queries:    map[string]*query{},
	}

	// Register message bus, message handler
	if err := mb.HandleMessage(nd.messageHandler); err != nil {
		return nil, err
	}

	// Add bootstrap nodes
	for _, peer := range bps {
		if err := nd.storePeer(peer, true); err != nil {
			logrus.WithField("error", err).Error("new could not store peer")
		}
		if err := nd.putPeer(peer); err != nil {
			logrus.WithField("error", err).Error("new could not put peer")
		}
	}

	// start refresh worker
	ticker := time.NewTicker(15 * time.Second)
	quit := make(chan struct{})
	go func() {
		// refresh for the first time
		nd.refresh()
		// and then just wait
		for {
			select {
			case <-ticker.C:
				nd.refresh()
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()

	return nd, nil
}

func (nd *DHTNode) refresh() {
	logrus.Infof("Refreshing")
	cps, err := nd.store.FindKeysNearestTo(KeyPrefixPeer, nd.GetLocalPeer().ID, numPeersNear*10)
	if err != nil {
		logrus.WithError(err).Warnf("refresh could not get peers ids")
		return
	}
	ctx := context.Background()
	for _, cp := range cps {
		res, err := nd.Get(ctx, cp)
		if err != nil {
			logrus.WithError(err).WithField("peerID", cps).Warnf("refresh could not get for peer")
			continue
		}
		for range res {
			// just swallow channel results
		}
	}
}

func (nd *DHTNode) messageHandler(hash []byte, msg messagebus.Message) error {
	switch msg.Payload.Type {
	case MessageTypeGet:
		getMsg := &messageGet{}
		if err := json.Unmarshal(msg.Payload.Data, getMsg); err != nil {
			return err
		}
		nd.getHandler(getMsg)
	case MessageTypePut:
		putMsg := &messagePut{}
		if err := json.Unmarshal(msg.Payload.Data, putMsg); err != nil {
			return err
		}
		nd.putHandler(putMsg)
	default:
		logrus.Info("Call type not implemented")
	}
	return nil
}

func (nd *DHTNode) Get(ctx context.Context, key string) (chan string, error) {
	logrus.Infof("Searching for key %s", key)

	// create query
	// TODO query needs the context
	q := &query{
		id:               uuid.New().String(),
		dht:              nd,
		key:              key,
		contactedPeers:   []string{},
		results:          make(chan string, 100),
		incomingMessages: make(chan messagePut, 100),
		lock:             &sync.RWMutex{},
	}

	// and store it
	nd.mt.Lock()
	nd.queries[q.id] = q
	nd.mt.Unlock()

	// run query
	q.Run(ctx)

	// return results channel
	return q.results, nil
}

func (nd *DHTNode) GetPeer(ctx context.Context, id string) (net.Peer, error) {
	// get peer key
	res, err := nd.Get(ctx, getPeerKey(id))
	if err != nil {
		return net.Peer{}, err
	}

	// hold addresses
	addrs := []string{}

	// go through results and create addresses array
	for addr := range res {
		addrs = appendIfMissing(addrs, addr)
	}

	// check addrs
	if len(addrs) == 0 {
		return net.Peer{}, ErrPeerNotFound
	}

	return net.Peer{
		ID:        id,
		Addresses: addrs,
	}, nil
}

func (nd *DHTNode) sendMsgPeer(msgType string, msg interface{}, peerID string) error {
	if peerID == nd.localPeer.ID {
		return nil
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	pl := &messagebus.Payload{
		Creator: nd.localPeer.ID,
		Coded:   "json",
		Type:    msgType,
		Data:    data,
	}

	return nd.messageBus.Send(pl, []string{peerID})
}

func (nd *DHTNode) getHandler(msg *messageGet) {
	// origin peer is asking for a peer

	// store info on origin peer
	nd.storePeer(msg.OriginPeer, false)
	nd.putPeer(msg.OriginPeer)

	// check if we have the value of the key
	ks, err := nd.store.Get(msg.Key)
	if err != nil {
		logrus.WithField("msg", msg).Error("Failed to find nodes near")
		return
	}

	logrus.Infof("%+v", nd.store.pairs)

	// send them if we do
	if len(ks) > 0 {
		msgPut := &messagePut{
			QueryID:    msg.QueryID,
			OriginPeer: msg.OriginPeer,
			Key:        msg.Key,
			Values:     ks,
		}
		// send response
		if err := nd.sendMsgPeer(MessageTypePut, msgPut, msg.OriginPeer.ID); err != nil {
			logrus.WithError(err).Warnf("getHandler could not send msg")
		}
	}

	// find peers nearest peers that might have it
	cps, err := nd.store.FindKeysNearestTo(KeyPrefixPeer, msg.Key, numPeersNear)
	if err != nil {
		logrus.WithError(err).Error("getHandler could not find nearest peers")
		return
	}

	// give up if there are no peers
	if len(cps) == 0 {
		return
	}

	// send messages with closes peers
	for _, cp := range cps {
		cpid := trimKey(cp, KeyPrefixPeer)
		// skil us and original peer
		if cpid == msg.OriginPeer.ID || cpid == nd.GetLocalPeer().ID {
			continue
		}
		// get neighbor addresses
		addrs, err := nd.store.Get(cp)
		if err != nil {
			logrus.WithError(err).Warnf("getHandler could not get addrs")
			continue
		}
		// create a response
		msgPut := &messagePut{
			QueryID:    msg.QueryID,
			OriginPeer: msg.OriginPeer,
			Key:        cp,
			Values:     addrs,
		}
		// send response
		if err := nd.sendMsgPeer(MessageTypePut, msgPut, msg.OriginPeer.ID); err != nil {
			logrus.WithError(err).Warnf("getHandler could not send msg")
		}
	}
}

func (nd *DHTNode) putHandler(msg *messagePut) {
	// A peer we asked is informing us of a peer
	logrus.WithField("key", msg.Key).Infof("Got response")

	// check if this still a valid query
	if q, ok := nd.queries[msg.QueryID]; ok {
		q.incomingMessages <- *msg
	}

	// add values to our store
	if checkKey(msg.Key) {
		for _, v := range msg.Values {
			nd.store.Put(msg.Key, v, false)
		}
	}

	// check if this is a peer
	if strings.HasPrefix(msg.Key, KeyPrefixPeer) {
		pr, err := nd.gatherPeer(strings.Replace(msg.Key, KeyPrefixPeer, "", 1))
		if err != nil {
			logrus.WithError(err).Infof("putHandler could get pairs for putPeer")
			return
		}
		if err := nd.putPeer(pr); err != nil {
			logrus.WithError(err).Infof("putHandler could get putPeer")
			return
		}
	}
}

func (nd *DHTNode) gatherPeer(peerID string) (net.Peer, error) {
	addrs, err := nd.store.Get(peerID)
	if err != nil {
		return net.Peer{}, err
	}
	pr := net.Peer{
		ID:        peerID,
		Addresses: addrs,
	}
	return pr, nil
}

func (nd *DHTNode) putPeer(peer net.Peer) error {
	logrus.Infof("Adding peer to network peer=%v", peer)
	// add peer to network
	if err := nd.net.PutPeer(peer); err != nil {
		logrus.WithError(err).Warnf("Could not add peer to network")
		return err
	}
	logrus.Infof("PUT PEER %+v", peer)
	return nil
}

func (nd *DHTNode) storePeer(peer net.Peer, persistent bool) error {
	for _, addr := range peer.Addresses {
		logrus.WithField("k", getPeerKey(peer.ID)).WithField("v", addr).Infof("Adding peer addresses to kv")
		if err := nd.store.Put(getPeerKey(peer.ID), addr, persistent); err != nil {
			logrus.WithError(err).WithField("peerID", peer.ID).Warnf("storePeer could not put peer")
		}
	}
	return nil
}

func (nd *DHTNode) GetLocalPeer() net.Peer {
	return nd.localPeer
}
<|endoftext|>


<commit_before>package instances

import (
	"bufio"
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/testing"
	"strings"
)

type imageSuite struct {
	testing.LoggingSuite
}

var _ = Suite(&imageSuite{})

func (s *imageSuite) SetUpSuite(c *C) {
	s.LoggingSuite.SetUpSuite(c)
}

func (s *imageSuite) TearDownSuite(c *C) {
	s.LoggingSuite.TearDownTest(c)
}

var imagesData = imagesFields(
	"instance-store amd64 us-east-1 ami-00000011 paravirtual",
	"ebs amd64 eu-west-1 ami-00000016 paravirtual",
	"ebs i386 ap-northeast-1 ami-00000023 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000026 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000087 hvm",
	"ebs amd64 test ami-00000033 paravirtual",
	"ebs i386 test ami-00000034 paravirtual",
	"ebs amd64 test ami-00000035 hvm",
	"ebs i386 i386-only ami-00000036 paravirtual",
)

func imagesFields(srcs ...string) string {
	strs := make([]string, len(srcs))
	for i, src := range srcs {
		parts := strings.Split(src, " ")
		if len(parts) != 5 {
			panic("bad clouddata field input")
		}
		args := make([]interface{}, len(parts))
		for i, part := range parts {
			args[i] = part
		}
		// Ignored fields are left empty for clarity's sake, and two additional
		// tabs are tacked on to the end to verify extra columns are ignored.
		strs[i] = fmt.Sprintf("\t\t\t\t%s\t%s\t%s\t%s\t\t\t%s\t\t\n", args...)
	}
	return strings.Join(strs, "")
}

type instanceSpecTestParams struct {
	desc                string
	region              string
	arches              []string
	constraints         string
	instanceTypes       []InstanceType
	defaultImageId      string
	defaultInstanceType string
	imageId             string
	instanceTypeId      string
	instanceTypeName    string
	err                 string
}

func (p *instanceSpecTestParams) init() {
	if p.arches == nil {
		p.arches = Both
	}
	if p.instanceTypes == nil {
		p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: Both}}
		p.instanceTypeId = "1"
		p.instanceTypeName = "it-1"
	}
}

var findInstanceSpecTests = []instanceSpecTestParams{
	{
		desc:           "image exists in metadata",
		region:         "test",
		defaultImageId: "1234",
		imageId:        "ami-00000033",
	},
	{
		desc:           "no image exists in metadata, use supplied default",
		region:         "invalid-region",
		defaultImageId: "1234",
		imageId:        "1234",
	},
	{
		desc:    "no image exists in metadata, no default supplied",
		region:  "invalid-region",
		imageId: "1234",
		err:     `no "raring" images in invalid-region with arches \[amd64 i386\], and no default specified`,
	},
	{
		desc:          "no valid instance types",
		region:        "test",
		instanceTypes: []InstanceType{},
		err:           `no instance types in test matching constraints "cpu-power=100", and no default specified`,
	},
	{
		desc:          "no compatible instance types",
		region:        "i386-only",
		instanceTypes: []InstanceType{{Id: "1", Name: "it-1", Arches: Amd64, Mem: 2048}},
		err:           `no "raring" images in i386-only matching instance types \[it-1\]`,
	},
	{
		desc:        "fallback instance type, enough memory for mongodb",
		region:      "test",
		constraints: "mem=8G",
		instanceTypes: []InstanceType{
			{Id: "3", Name: "it-3", Arches: Amd64, Mem: 4096},
			{Id: "2", Name: "it-2", Arches: Amd64, Mem: 2048},
			{Id: "1", Name: "it-1", Arches: Amd64, Mem: 512},
		},
		imageId:          "ami-00000033",
		instanceTypeId:   "2",
		instanceTypeName: "it-2",
	},
	{
		desc:        "fallback instance type, not enough memory for mongodb",
		region:      "test",
		constraints: "mem=4G",
		instanceTypes: []InstanceType{
			{Id: "2", Name: "it-2", Arches: Amd64, Mem: 256},
			{Id: "1", Name: "it-1", Arches: Amd64, Mem: 512},
		},
		imageId:          "ami-00000033",
		instanceTypeId:   "1",
		instanceTypeName: "it-1",
	},
}

func (s *imageSuite) TestFindInstanceSpec(c *C) {
	for _, t := range findInstanceSpecTests {
		c.Logf("test: %v", t.desc)
		t.init()
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		spec, err := FindInstanceSpec(r, &InstanceConstraint{
			Series:         "raring",
			Region:         t.region,
			Arches:         t.arches,
			Constraints:    constraints.MustParse(t.constraints),
			DefaultImageId: t.defaultImageId,
		}, t.instanceTypes, nil)
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(spec.Image.Id, Equals, t.imageId)
		c.Check(spec.InstanceTypeId, Equals, t.instanceTypeId)
		c.Check(spec.InstanceTypeName, Equals, t.instanceTypeName)
	}
}

var getImagesTests = []struct {
	region string
	series string
	arches []string
	images []Image
	err    string
}{
	{
		region: "us-east-1",
		series: "precise",
		arches: Both,
		err:    `no "precise" images in us-east-1 with arches \[amd64 i386\]`,
	}, {
		region: "eu-west-1",
		series: "precise",
		arches: []string{"i386"},
		err:    `no "precise" images in eu-west-1 with arches \[i386\]`,
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: Both,
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
			{"ami-00000023", "i386", false},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"amd64"},
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"i386"},
		images: []Image{
			{"ami-00000023", "i386", false},
		},
	},
}

func (s *imageSuite) TestGetImages(c *C) {
	var ebs = "ebs"
	var cluster = "hvm"
	for i, t := range getImagesTests {
		c.Logf("test %d", i)
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		images, err := getImages(r, &InstanceConstraint{
			Region:  t.region,
			Series:  t.series,
			Arches:  t.arches,
			Storage: &ebs,
			Cluster: &cluster,
		})
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(images, DeepEquals, t.images)
	}
}

var imageMatchtests = []struct {
	image Image
	itype InstanceType
	match bool
}{
	{
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"i386", "amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
		match: true,
	}, {
		image: Image{Arch: "i386"},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
	},
}

func (s *imageSuite) TestImageMatch(c *C) {
	for i, t := range imageMatchtests {
		c.Logf("test %d", i)
		c.Check(t.image.match(t.itype), Equals, t.match)
	}
}
<commit_msg>Add missing test setup<commit_after>package instances

import (
	"bufio"
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/constraints"
	coretesting "launchpad.net/juju-core/testing"
	"strings"
	"testing"
)

type imageSuite struct {
	coretesting.LoggingSuite
}

func Test(t *testing.T) {
	TestingT(t)
}

var _ = Suite(&imageSuite{})

func (s *imageSuite) SetUpSuite(c *C) {
	s.LoggingSuite.SetUpSuite(c)
}

func (s *imageSuite) TearDownSuite(c *C) {
	s.LoggingSuite.TearDownTest(c)
}

var imagesData = imagesFields(
	"instance-store amd64 us-east-1 ami-00000011 paravirtual",
	"ebs amd64 eu-west-1 ami-00000016 paravirtual",
	"ebs i386 ap-northeast-1 ami-00000023 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000026 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000087 hvm",
	"ebs amd64 test ami-00000033 paravirtual",
	"ebs i386 test ami-00000034 paravirtual",
	"ebs amd64 test ami-00000035 hvm",
	"ebs i386 i386-only ami-00000036 paravirtual",
)

func imagesFields(srcs ...string) string {
	strs := make([]string, len(srcs))
	for i, src := range srcs {
		parts := strings.Split(src, " ")
		if len(parts) != 5 {
			panic("bad clouddata field input")
		}
		args := make([]interface{}, len(parts))
		for i, part := range parts {
			args[i] = part
		}
		// Ignored fields are left empty for clarity's sake, and two additional
		// tabs are tacked on to the end to verify extra columns are ignored.
		strs[i] = fmt.Sprintf("\t\t\t\t%s\t%s\t%s\t%s\t\t\t%s\t\t\n", args...)
	}
	return strings.Join(strs, "")
}

type instanceSpecTestParams struct {
	desc                string
	region              string
	arches              []string
	constraints         string
	instanceTypes       []InstanceType
	defaultImageId      string
	defaultInstanceType string
	imageId             string
	instanceTypeId      string
	instanceTypeName    string
	err                 string
}

func (p *instanceSpecTestParams) init() {
	if p.arches == nil {
		p.arches = Both
	}
	if p.instanceTypes == nil {
		p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: Both}}
		p.instanceTypeId = "1"
		p.instanceTypeName = "it-1"
	}
}

var findInstanceSpecTests = []instanceSpecTestParams{
	{
		desc:           "image exists in metadata",
		region:         "test",
		defaultImageId: "1234",
		imageId:        "ami-00000033",
	},
	{
		desc:           "no image exists in metadata, use supplied default",
		region:         "invalid-region",
		defaultImageId: "1234",
		imageId:        "1234",
	},
	{
		desc:    "no image exists in metadata, no default supplied",
		region:  "invalid-region",
		imageId: "1234",
		err:     `no "raring" images in invalid-region with arches \[amd64 i386\], and no default specified`,
	},
	{
		desc:          "no valid instance types",
		region:        "test",
		instanceTypes: []InstanceType{},
		err:           `no instance types in test matching constraints "cpu-power=100", and no default specified`,
	},
	{
		desc:          "no compatible instance types",
		region:        "i386-only",
		instanceTypes: []InstanceType{{Id: "1", Name: "it-1", Arches: Amd64, Mem: 2048}},
		err:           `no "raring" images in i386-only matching instance types \[it-1\]`,
	},
	{
		desc:        "fallback instance type, enough memory for mongodb",
		region:      "test",
		constraints: "mem=8G",
		instanceTypes: []InstanceType{
			{Id: "3", Name: "it-3", Arches: Amd64, Mem: 4096},
			{Id: "2", Name: "it-2", Arches: Amd64, Mem: 2048},
			{Id: "1", Name: "it-1", Arches: Amd64, Mem: 512},
		},
		imageId:          "ami-00000033",
		instanceTypeId:   "2",
		instanceTypeName: "it-2",
	},
	{
		desc:        "fallback instance type, not enough memory for mongodb",
		region:      "test",
		constraints: "mem=4G",
		instanceTypes: []InstanceType{
			{Id: "2", Name: "it-2", Arches: Amd64, Mem: 256},
			{Id: "1", Name: "it-1", Arches: Amd64, Mem: 512},
		},
		imageId:          "ami-00000033",
		instanceTypeId:   "1",
		instanceTypeName: "it-1",
	},
}

func (s *imageSuite) TestFindInstanceSpec(c *C) {
	for _, t := range findInstanceSpecTests {
		c.Logf("test: %v", t.desc)
		t.init()
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		spec, err := FindInstanceSpec(r, &InstanceConstraint{
			Series:         "raring",
			Region:         t.region,
			Arches:         t.arches,
			Constraints:    constraints.MustParse(t.constraints),
			DefaultImageId: t.defaultImageId,
		}, t.instanceTypes, nil)
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(spec.Image.Id, Equals, t.imageId)
		c.Check(spec.InstanceTypeId, Equals, t.instanceTypeId)
		c.Check(spec.InstanceTypeName, Equals, t.instanceTypeName)
	}
}

var getImagesTests = []struct {
	region string
	series string
	arches []string
	images []Image
	err    string
}{
	{
		region: "us-east-1",
		series: "precise",
		arches: Both,
		err:    `no "precise" images in us-east-1 with arches \[amd64 i386\]`,
	}, {
		region: "eu-west-1",
		series: "precise",
		arches: []string{"i386"},
		err:    `no "precise" images in eu-west-1 with arches \[i386\]`,
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: Both,
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
			{"ami-00000023", "i386", false},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"amd64"},
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"i386"},
		images: []Image{
			{"ami-00000023", "i386", false},
		},
	},
}

func (s *imageSuite) TestGetImages(c *C) {
	var ebs = "ebs"
	var cluster = "hvm"
	for i, t := range getImagesTests {
		c.Logf("test %d", i)
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		images, err := getImages(r, &InstanceConstraint{
			Region:  t.region,
			Series:  t.series,
			Arches:  t.arches,
			Storage: &ebs,
			Cluster: &cluster,
		})
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(images, DeepEquals, t.images)
	}
}

var imageMatchtests = []struct {
	image Image
	itype InstanceType
	match bool
}{
	{
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"i386", "amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
		match: true,
	}, {
		image: Image{Arch: "i386"},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
	},
}

func (s *imageSuite) TestImageMatch(c *C) {
	for i, t := range imageMatchtests {
		c.Logf("test %d", i)
		c.Check(t.image.match(t.itype), Equals, t.match)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	// https://www.bluetooth.com/specifications/specs/cycling-power-service-1-1/
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var (
	KnownServiceNames = map[bluetooth.UUID]string{
		bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
		bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
		bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
	}
	KnownCharacteristicNames = map[bluetooth.UUID]string{
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement: "Cycling Power Measure",
		bluetooth.CharacteristicUUIDCyclingPowerFeature:     "Cycling Power Feature",
		bluetooth.CharacteristicUUIDHeartRateMeasurement:    "Heart Rate Measurement",
	}
)

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind  MetricKind
	value int
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	if name, ok := KnownCharacteristicNames[src.ch.UUID()]; ok {
		return name
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())
}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		handler := src.notificationHandler()
		src.ch.EnableNotifications(handler)
	}
}

func (src *MetricSource) notificationHandler() func([]byte) {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return src.handleCyclingPowerMeasurement

	// TODO
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return src.handleHeartRateMeasurement
	}

	return nil
}

func (src *MetricSource) emit(m DeviceMetric) {
	for _, sink := range src.sinks {
		sink <- m
	}
}

const (
	// BPM size, 0 if u8, 1 if u16
	HeartRateFlagSize = 1 << 0

	// 00 unsupported
	// 01 unsupported
	// 10 supported, not detected
	// 11 supported, detected
	HeartRateFlagContactStatus = (1 << 1) | (1 << 2)

	HeartRateFlagHasEnergyExpended = 1 << 3
	HeartRateFlagHasRRInterval     = 1 << 4

	// bits 5-8 reserved
)

func (src *MetricSource) handleHeartRateMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flag := buf[0]

	is16Bit := (flag & HeartRateFlagSize) != 0
	contactStatus := (flag & HeartRateFlagContactStatus) >> 1

	contactSupported := contactStatus&(0b10) != 0
	contactFound := contactStatus&(0b01) != 0

	// No use sending this metric if the sensor isn't reading.
	if contactSupported && !contactFound {
		return
	}

	var hr int = int(buf[1])
	if is16Bit {
		hr = (hr << 8) | int(buf[2])
	}

	src.emit(DeviceMetric{
		kind:  MetricHeartRate,
		value: hr,
	})
}

const (
	CyclingPowerFlagHasPedalPowerBalance           = 1 << 0
	CyclingPowerFlagPedalPowerBalanceReference     = 1 << 1
	CyclingPowerFlagHasAccumulatedTorque           = 1 << 2
	CyclingPowerFlagAccumulatedTorqueSource        = 1 << 3
	CyclingPowerFlagHasWheelRevolution             = 1 << 4
	CyclingPowerFlagHasCrankRevolution             = 1 << 5
	CyclingPowerFlagHasExtremeForceMagnitudes      = 1 << 6
	CyclingPowerFlagHasExtremeTorqueMagnitudes     = 1 << 7
	CyclingPowerFlagHasExtremeAngles               = 1 << 8
	CyclingPowerFlagHasTopDeadSpotAngle            = 1 << 9
	CyclingPowerFlagHasBottomDeadSpotAngle         = 1 << 10
	CyclingPowerFlagHasAccumulatedEnergy           = 1 << 11
	CyclingPowerFlagHasOffsetCompensationIndicator = 1 << 12

	// Bits 13-16 reserved
)

// Two flag bytes, followed by a 16 bit power reading. All subsequent
// fields are optional, based on the flag bits set.
//
// sint16  instantaneous_power      watts with resolution 1
// uint8   pedal_power_balance      percentage with resolution 1/2
// uint16  accumulated_torque       newton meters with resolution 1/32
// uint32  wheel_rev_cumulative     unitless
// uint16  wheel_rev_last_time      seconds with resolution 1/2048
// uint16  crank_rev_cumulative     unitless
// uint16  crank_rev_last_time      seconds with resolution 1/1024
// sint16  extreme_force_max_magn   newtons with resolution 1
// sint16  extreme_force_min_magn   newtons with resolution 1
// sint16  extreme_torque_max_magn  newton meters with resolution 1/32
// sint16  extreme_torque_min_magn  newton meters with resolution 1/32
// uint12  extreme_angles_max       degrees with resolution 1
// uint12  extreme_angles_min       degrees with resolution 1
// uint16  top_dead_spot_angle      degrees with resolution 1
// uint16  bottom_dead_spot_angle   degrees with resolution 1
// uint16  accumulated_energy       kilojoules with resolution 1
func (src *MetricSource) handleCyclingPowerMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flags := uint16(buf[0]) | uint16(buf[1]<<8)

	watts := int16(buf[2]<<8) | int16(buf[3])
	fmt.Printf("power measure: %d watts, flags=%b\n", watts, flags)

	if flags&CyclingPowerFlagHasAccumulatedEnergy != 0 {
		fmt.Println("also have energy")
	}

}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}
	connectLock := sync.Mutex{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		params := bluetooth.ConnectionParams{
			ConnectionTimeout: bluetooth.Duration(100),
		}

		// TODO: We should add a time bound for this
		for {
			// TODO: tiny-go/bluetooth's Connect is not
			// thread-safe. Multiple concurrent calls will race and
			// return the wrong data to the wrong caller.
			connectLock.Lock()
			defer connectLock.Unlock()

			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, params)
			if err != nil {
				continue
			}

			deviceChan <- device
			break
		}

		wg.Done()
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingSpeedCadenceAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	metricsChan := make(chan DeviceMetric)
	go func() {
		for m := range metricsChan {
			fmt.Printf("Metric: %+v\n", m)
		}
	}()

	for device := range deviceChan {
		fmt.Println("Initializing device...")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			if name, ok := KnownServiceNames[service.UUID()]; ok {
				fmt.Printf("\tservice: %s\n", name)
			} else {
				fmt.Printf("\tservice: unknown <%+v>\n", service.UUID().String())
			}

			knownChars := KnownServiceCharacteristicUUIDs[service.UUID()]
			chars, err := service.DiscoverCharacteristics(knownChars)
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				name := KnownCharacteristicNames[char.UUID()]
				fmt.Printf("\t\tcharacteristic: %s\n", name)

				src := NewMetricSource(&service, &char)
				src.AddSink(metricsChan)
			}
		}
	}

	println("that's all!")
	select {}
}
<commit_msg>Emit power metric<commit_after>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	// https://www.bluetooth.com/specifications/specs/cycling-power-service-1-1/
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var (
	KnownServiceNames = map[bluetooth.UUID]string{
		bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
		bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
		bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
	}
	KnownCharacteristicNames = map[bluetooth.UUID]string{
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement: "Cycling Power Measure",
		bluetooth.CharacteristicUUIDCyclingPowerFeature:     "Cycling Power Feature",
		bluetooth.CharacteristicUUIDHeartRateMeasurement:    "Heart Rate Measurement",
	}
)

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind  MetricKind
	value int
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	if name, ok := KnownCharacteristicNames[src.ch.UUID()]; ok {
		return name
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())
}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		handler := src.notificationHandler()
		src.ch.EnableNotifications(handler)
	}
}

func (src *MetricSource) notificationHandler() func([]byte) {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return src.handleCyclingPowerMeasurement

	// TODO
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return src.handleHeartRateMeasurement
	}

	return nil
}

func (src *MetricSource) emit(m DeviceMetric) {
	for _, sink := range src.sinks {
		sink <- m
	}
}

const (
	// BPM size, 0 if u8, 1 if u16
	HeartRateFlagSize = 1 << 0

	// 00 unsupported
	// 01 unsupported
	// 10 supported, not detected
	// 11 supported, detected
	HeartRateFlagContactStatus = (1 << 1) | (1 << 2)

	HeartRateFlagHasEnergyExpended = 1 << 3
	HeartRateFlagHasRRInterval     = 1 << 4

	// bits 5-8 reserved
)

func (src *MetricSource) handleHeartRateMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flag := buf[0]

	is16Bit := (flag & HeartRateFlagSize) != 0
	contactStatus := (flag & HeartRateFlagContactStatus) >> 1

	contactSupported := contactStatus&(0b10) != 0
	contactFound := contactStatus&(0b01) != 0

	// No use sending this metric if the sensor isn't reading.
	if contactSupported && !contactFound {
		return
	}

	var hr int = int(buf[1])
	if is16Bit {
		hr = (hr << 8) | int(buf[2])
	}

	src.emit(DeviceMetric{
		kind:  MetricHeartRate,
		value: hr,
	})
}

const (
	CyclingPowerFlagHasPedalPowerBalance           = 1 << 0
	CyclingPowerFlagPedalPowerBalanceReference     = 1 << 1
	CyclingPowerFlagHasAccumulatedTorque           = 1 << 2
	CyclingPowerFlagAccumulatedTorqueSource        = 1 << 3
	CyclingPowerFlagHasWheelRevolution             = 1 << 4
	CyclingPowerFlagHasCrankRevolution             = 1 << 5
	CyclingPowerFlagHasExtremeForceMagnitudes      = 1 << 6
	CyclingPowerFlagHasExtremeTorqueMagnitudes     = 1 << 7
	CyclingPowerFlagHasExtremeAngles               = 1 << 8
	CyclingPowerFlagHasTopDeadSpotAngle            = 1 << 9
	CyclingPowerFlagHasBottomDeadSpotAngle         = 1 << 10
	CyclingPowerFlagHasAccumulatedEnergy           = 1 << 11
	CyclingPowerFlagHasOffsetCompensationIndicator = 1 << 12

	// Bits 13-16 reserved
)

// Two flag bytes, followed by a 16 bit power reading. All subsequent
// fields are optional, based on the flag bits set.
//
// sint16  instantaneous_power      watts with resolution 1
// uint8   pedal_power_balance      percentage with resolution 1/2
// uint16  accumulated_torque       newton meters with resolution 1/32
// uint32  wheel_rev_cumulative     unitless
// uint16  wheel_rev_last_time      seconds with resolution 1/2048
// uint16  crank_rev_cumulative     unitless
// uint16  crank_rev_last_time      seconds with resolution 1/1024
// sint16  extreme_force_max_magn   newtons with resolution 1
// sint16  extreme_force_min_magn   newtons with resolution 1
// sint16  extreme_torque_max_magn  newton meters with resolution 1/32
// sint16  extreme_torque_min_magn  newton meters with resolution 1/32
// uint12  extreme_angles_max       degrees with resolution 1
// uint12  extreme_angles_min       degrees with resolution 1
// uint16  top_dead_spot_angle      degrees with resolution 1
// uint16  bottom_dead_spot_angle   degrees with resolution 1
// uint16  accumulated_energy       kilojoules with resolution 1
func (src *MetricSource) handleCyclingPowerMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flags := uint16(buf[0]) | uint16(buf[1]<<8)

	powerWatts := int16(buf[2]<<8) | int16(buf[3])

	// Power meters will send packets even if nothing's happening.
	if powerWatts == 0 {
		return
	}

	src.emit(DeviceMetric{
		kind:  MetricCyclingPower,
		value: int(powerWatts),
	})

	if flags&CyclingPowerFlagHasAccumulatedEnergy != 0 {
		fmt.Println("also have energy")
	}

}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}
	connectLock := sync.Mutex{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		params := bluetooth.ConnectionParams{
			ConnectionTimeout: bluetooth.Duration(100),
		}

		// TODO: We should add a time bound for this
		for {
			// TODO: tiny-go/bluetooth's Connect is not
			// thread-safe. Multiple concurrent calls will race and
			// return the wrong data to the wrong caller.
			connectLock.Lock()
			defer connectLock.Unlock()

			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, params)
			if err != nil {
				continue
			}

			deviceChan <- device
			break
		}

		wg.Done()
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingSpeedCadenceAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	metricsChan := make(chan DeviceMetric)
	go func() {
		for m := range metricsChan {
			fmt.Printf("Metric: %+v\n", m)
		}
	}()

	for device := range deviceChan {
		fmt.Println("Initializing device...")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			if name, ok := KnownServiceNames[service.UUID()]; ok {
				fmt.Printf("\tservice: %s\n", name)
			} else {
				fmt.Printf("\tservice: unknown <%+v>\n", service.UUID().String())
			}

			knownChars := KnownServiceCharacteristicUUIDs[service.UUID()]
			chars, err := service.DiscoverCharacteristics(knownChars)
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				name := KnownCharacteristicNames[char.UUID()]
				fmt.Printf("\t\tcharacteristic: %s\n", name)

				src := NewMetricSource(&service, &char)
				src.AddSink(metricsChan)
			}
		}
	}

	println("that's all!")
	select {}
}
<|endoftext|>


<commit_before>// Title：顾问通话总览列表
//
// Description:
//
// Author:black
//
// Createtime:2013-09-26 15:50
//
// Version:1.0
//
// 修改历史:版本号 修改日期 修改人 修改说明
//
// 1.0 2013-09-26 15:50 black 创建文档
package server

import (
	"fmt"
	"github.com/hjqhezgh/commonlib"
	"github.com/hjqhezgh/lessgo"
	"math"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"text/template"
	"time"
)

//顾问分页数据服务
func ConsultantPhoneListAction(w http.ResponseWriter, r *http.Request) {

	m := make(map[string]interface{})

	employee := lessgo.GetCurrentEmployee(r)

	if employee.UserId == "" {
		lessgo.Log.Warn("用户未登陆")
		m["success"] = false
		m["code"] = 100
		m["msg"] = "用户未登陆"
		commonlib.OutputJson(w, m, " ")
		return
	}

	err := r.ParseForm()

	if err != nil {
		m["success"] = false
		m["code"] = 100
		m["msg"] = "出现错误，请联系IT部门，错误信息:" + err.Error()
		commonlib.OutputJson(w, m, " ")
		return
	}

	pageNoString := r.FormValue("page")
	pageNo := 1
	if pageNoString != "" {
		pageNo, err = strconv.Atoi(pageNoString)
		if err != nil {
			pageNo = 1
			lessgo.Log.Warn("错误的pageNo:", pageNo)
		}
	}

	pageSizeString := r.FormValue("rows")
	pageSize := 10
	if pageSizeString != "" {
		pageSize, err = strconv.Atoi(pageSizeString)
		if err != nil {
			lessgo.Log.Warn("错误的pageSize:", pageSize)
		}
	}

	dataType := ""

	roleIds := strings.Split(employee.RoleId, ",")

	for _, roleId := range roleIds {
		if roleId == "1" || roleId == "3" {
			dataType = "all"
			break
		} else if roleId == "2" {
			dataType = "center"
			break
		} else{
			dataType = "self"
		}
	}

	cid := r.FormValue("cid-eq")
	name := r.FormValue("name-like")
	year := r.FormValue("year-eq")
	month := r.FormValue("month-eq")
	week := r.FormValue("week-eq")
	startTime := r.FormValue("start_time-eq")

	st := ""
	et := ""
	flag := true

	if startTime != "" {
		st = startTime + " 00:00:00"
		et = startTime + " 23:59:59"
	} else {
		if week != "" && month != "" && year != "" {
			st, et, flag = lessgo.FindRangeTimeDim("", "", year+month+week)
		} else if month != "" && year != "" {
			st, et, flag = lessgo.FindRangeTimeDim("", year+month, "")
		} else if year != "" {
			st, et, flag = lessgo.FindRangeTimeDim(year, "", "")
		}
	}

	params := []interface{}{}

	sql := ""
	countSql := ""

	if dataType == "all" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio where remotephone!='' and remotephone is not null "

		if cid != "" {
			sql += " and cid=? "
			params = append(params, cid)
		}

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		if name != "" {
			sql += " where e.really_name like ? "
			params = append(params, "%"+name+"%")
		}

		sql += " order by rank.rowNo "

	} else if dataType == "center" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio  where cid=? and remotephone!='' and remotephone is not null "

		userId, _ := strconv.Atoi(employee.UserId)
		_employee, err := FindEmployeeById(userId)
		if err != nil {
			m["success"] = false
			m["code"] = 100
			m["msg"] = "出现错误，请联系IT部门，错误信息:" + err.Error()
			commonlib.OutputJson(w, m, " ")
			return
		}
		params = append(params, _employee.CenterId)

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone  left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		if name != "" {
			sql += " where e.really_name like ? "
			params = append(params, "%"+name+"%")
		}

		sql += " order by rank.rowNo "

	} else if dataType == "self" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone  left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		sql += " where e.user_id=? "

		params = append(params, employee.UserId)

		sql += " order by rank.rowNo "
	}

	countSql = "select count(1) from (" + sql + ") num"

	lessgo.Log.Debug(countSql)

	db := lessgo.GetMySQL()
	defer db.Close()

	rows, err := db.Query(countSql, params...)

	if err != nil {
		lessgo.Log.Warn(err.Error())
		m["success"] = false
		m["code"] = 100
		m["msg"] = "系统发生错误，请联系IT部门"
		commonlib.OutputJson(w, m, " ")
		return
	}

	totalNum := 0

	if rows.Next() {
		err := rows.Scan(&totalNum)

		if err != nil {
			lessgo.Log.Warn(err.Error())
			m["success"] = false
			m["code"] = 100
			m["msg"] = "系统发生错误，请联系IT部门"
			commonlib.OutputJson(w, m, " ")
			return
		}
	}

	totalPage := int(math.Ceil(float64(totalNum) / float64(pageSize)))

	currPageNo := pageNo

	if currPageNo > totalPage {
		currPageNo = totalPage
	}

	lessgo.Log.Debug(sql + " limit ?,?")

	params = append(params, (currPageNo-1)*pageSize)
	params = append(params, pageSize)

	rows, err = db.Query(sql+" limit ?,?", params...)

	if err != nil {
		lessgo.Log.Warn(err.Error())
		m["success"] = false
		m["code"] = 100
		m["msg"] = "系统发生错误，请联系IT部门"
		commonlib.OutputJson(w, m, " ")
		return
	}

	objects := []interface{}{}

	for rows.Next() {

		model := new(lessgo.Model)
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		model.Id = r.Intn(1000)
		model.Props = []*lessgo.Prop{}

		fillObjects := []interface{}{}

		for i := 0; i < 9; i++ {
			prop := new(lessgo.Prop)
			prop.Name = fmt.Sprint(i)
			prop.Value = ""
			fillObjects = append(fillObjects, &prop.Value)
			model.Props = append(model.Props, prop)
		}

		err = commonlib.PutRecord(rows, fillObjects...)

		if err != nil {
			lessgo.Log.Warn(err.Error())
			m["success"] = false
			m["code"] = 100
			m["msg"] = "系统发生错误，请联系IT部门"
			commonlib.OutputJson(w, m, " ")
			return
		}

		objects = append(objects, model)
	}

	pageData := commonlib.BulidTraditionPage(currPageNo, pageSize, totalNum, objects)

	m["PageData"] = pageData
	m["DataLength"] = len(pageData.Datas) - 1
	if len(pageData.Datas) > 0 {
		m["FieldLength"] = len(pageData.Datas[0].(*lessgo.Model).Props) - 1
	}

	commonlib.RenderTemplate(w, r, "entity_page.json", m, template.FuncMap{"getPropValue": lessgo.GetPropValue, "compareInt": lessgo.CompareInt, "dealJsonString": lessgo.DealJsonString}, "../lessgo/template/entity_page.json")

}
<commit_msg>修改通话模块权限<commit_after>// Title：顾问通话总览列表
//
// Description:
//
// Author:black
//
// Createtime:2013-09-26 15:50
//
// Version:1.0
//
// 修改历史:版本号 修改日期 修改人 修改说明
//
// 1.0 2013-09-26 15:50 black 创建文档
package server

import (
	"fmt"
	"github.com/hjqhezgh/commonlib"
	"github.com/hjqhezgh/lessgo"
	"math"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"text/template"
	"time"
)

//顾问分页数据服务
func ConsultantPhoneListAction(w http.ResponseWriter, r *http.Request) {

	m := make(map[string]interface{})

	employee := lessgo.GetCurrentEmployee(r)

	if employee.UserId == "" {
		lessgo.Log.Warn("用户未登陆")
		m["success"] = false
		m["code"] = 100
		m["msg"] = "用户未登陆"
		commonlib.OutputJson(w, m, " ")
		return
	}

	err := r.ParseForm()

	if err != nil {
		m["success"] = false
		m["code"] = 100
		m["msg"] = "出现错误，请联系IT部门，错误信息:" + err.Error()
		commonlib.OutputJson(w, m, " ")
		return
	}

	pageNoString := r.FormValue("page")
	pageNo := 1
	if pageNoString != "" {
		pageNo, err = strconv.Atoi(pageNoString)
		if err != nil {
			pageNo = 1
			lessgo.Log.Warn("错误的pageNo:", pageNo)
		}
	}

	pageSizeString := r.FormValue("rows")
	pageSize := 10
	if pageSizeString != "" {
		pageSize, err = strconv.Atoi(pageSizeString)
		if err != nil {
			lessgo.Log.Warn("错误的pageSize:", pageSize)
		}
	}

	dataType := ""

	roleIds := strings.Split(employee.RoleId, ",")

	for _, roleId := range roleIds {
		if roleId == "1" || roleId == "3" || roleId == "6" || roleId=="10"{
			dataType = "all"
			break
		} else if roleId == "2" {
			dataType = "center"
			break
		} else{
			dataType = "self"
		}
	}

	cid := r.FormValue("cid-eq")
	name := r.FormValue("name-like")
	year := r.FormValue("year-eq")
	month := r.FormValue("month-eq")
	week := r.FormValue("week-eq")
	startTime := r.FormValue("start_time-eq")

	st := ""
	et := ""
	flag := true

	if startTime != "" {
		st = startTime + " 00:00:00"
		et = startTime + " 23:59:59"
	} else {
		if week != "" && month != "" && year != "" {
			st, et, flag = lessgo.FindRangeTimeDim("", "", year+month+week)
		} else if month != "" && year != "" {
			st, et, flag = lessgo.FindRangeTimeDim("", year+month, "")
		} else if year != "" {
			st, et, flag = lessgo.FindRangeTimeDim(year, "", "")
		}
	}

	params := []interface{}{}

	sql := ""
	countSql := ""

	if dataType == "all" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio where remotephone!='' and remotephone is not null "

		if cid != "" {
			sql += " and cid=? "
			params = append(params, cid)
		}

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		if name != "" {
			sql += " where e.really_name like ? "
			params = append(params, "%"+name+"%")
		}

		sql += " order by rank.rowNo "

	} else if dataType == "center" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio  where cid=? and remotephone!='' and remotephone is not null "

		userId, _ := strconv.Atoi(employee.UserId)
		_employee, err := FindEmployeeById(userId)
		if err != nil {
			m["success"] = false
			m["code"] = 100
			m["msg"] = "出现错误，请联系IT部门，错误信息:" + err.Error()
			commonlib.OutputJson(w, m, " ")
			return
		}
		params = append(params, _employee.CenterId)

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone  left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		if name != "" {
			sql += " where e.really_name like ? "
			params = append(params, "%"+name+"%")
		}

		sql += " order by rank.rowNo "

	} else if dataType == "self" {

		sql += "select c.name,c.cid,e.user_id,e.really_name,phone_count.num a,rank.rowNo b,phone_count.num c,phone_count.num d,phone_count.localphone from (select count(*) num,localphone,cid from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone) phone_count left join center c on c.cid=phone_count.cid left join employee e on e.phone_in_center=phone_count.localphone  left join (select a.*,(@rowNum:=@rowNum+1) as rowNo from (select count(*) num,localphone from audio where remotephone!='' and remotephone is not null "

		if flag {
			if st != "" && et != "" {
				sql += " and start_time >= ? and start_time<= ?"
				params = append(params, st)
				params = append(params, et)
			}
		} else { //找不到相应的时间区间
			sql += " and start_time >= ? and start_time<= ?"
			params = append(params, "2000-01-01 00:00:00")
			params = append(params, "2000-01-01 00:00:01")
		}

		sql += " group by  localphone order by num desc) a,(Select (@rowNum :=0) ) b)rank on rank.localphone=phone_count.localphone "

		sql += " where e.user_id=? "

		params = append(params, employee.UserId)

		sql += " order by rank.rowNo "
	}

	countSql = "select count(1) from (" + sql + ") num"

	lessgo.Log.Debug(countSql)

	db := lessgo.GetMySQL()
	defer db.Close()

	rows, err := db.Query(countSql, params...)

	if err != nil {
		lessgo.Log.Warn(err.Error())
		m["success"] = false
		m["code"] = 100
		m["msg"] = "系统发生错误，请联系IT部门"
		commonlib.OutputJson(w, m, " ")
		return
	}

	totalNum := 0

	if rows.Next() {
		err := rows.Scan(&totalNum)

		if err != nil {
			lessgo.Log.Warn(err.Error())
			m["success"] = false
			m["code"] = 100
			m["msg"] = "系统发生错误，请联系IT部门"
			commonlib.OutputJson(w, m, " ")
			return
		}
	}

	totalPage := int(math.Ceil(float64(totalNum) / float64(pageSize)))

	currPageNo := pageNo

	if currPageNo > totalPage {
		currPageNo = totalPage
	}

	lessgo.Log.Debug(sql + " limit ?,?")

	params = append(params, (currPageNo-1)*pageSize)
	params = append(params, pageSize)

	rows, err = db.Query(sql+" limit ?,?", params...)

	if err != nil {
		lessgo.Log.Warn(err.Error())
		m["success"] = false
		m["code"] = 100
		m["msg"] = "系统发生错误，请联系IT部门"
		commonlib.OutputJson(w, m, " ")
		return
	}

	objects := []interface{}{}

	for rows.Next() {

		model := new(lessgo.Model)
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		model.Id = r.Intn(1000)
		model.Props = []*lessgo.Prop{}

		fillObjects := []interface{}{}

		for i := 0; i < 9; i++ {
			prop := new(lessgo.Prop)
			prop.Name = fmt.Sprint(i)
			prop.Value = ""
			fillObjects = append(fillObjects, &prop.Value)
			model.Props = append(model.Props, prop)
		}

		err = commonlib.PutRecord(rows, fillObjects...)

		if err != nil {
			lessgo.Log.Warn(err.Error())
			m["success"] = false
			m["code"] = 100
			m["msg"] = "系统发生错误，请联系IT部门"
			commonlib.OutputJson(w, m, " ")
			return
		}

		objects = append(objects, model)
	}

	pageData := commonlib.BulidTraditionPage(currPageNo, pageSize, totalNum, objects)

	m["PageData"] = pageData
	m["DataLength"] = len(pageData.Datas) - 1
	if len(pageData.Datas) > 0 {
		m["FieldLength"] = len(pageData.Datas[0].(*lessgo.Model).Props) - 1
	}

	commonlib.RenderTemplate(w, r, "entity_page.json", m, template.FuncMap{"getPropValue": lessgo.GetPropValue, "compareInt": lessgo.CompareInt, "dealJsonString": lessgo.DealJsonString}, "../lessgo/template/entity_page.json")

}
<|endoftext|>


<commit_before>package helm

import (
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/version"

	hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"

	log "github.com/sirupsen/logrus"
)

func checkVersionedRelease(newVersion *types.Version, repo *types.Repository, namespace, name string, chart *hapi_chart.Chart, config *hapi_chart.Config) (plan *UpdatePlan, shouldUpdateRelease bool, err error) {
	plan = &UpdatePlan{
		Chart:     chart,
		Namespace: namespace,
		Name:      name,
		Values:    make(map[string]string),
	}

	eventRepoRef, err := image.Parse(repo.Name)
	if err != nil {
		log.WithFields(log.Fields{
			"error":           err,
			"repository_name": repo.Name,
		}).Error("provider.helm: failed to parse event repository name")
		return
	}

	// getting configuration
	vals, err := values(chart, config)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.helm: failed to get values.yaml for release")
		return
	}

	keelCfg, err := getKeelConfig(vals)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.helm: failed to get keel configuration for release")
		// ignoring this release, no keel config found
		return plan, false, nil
	}
	// checking for impacted images
	for _, imageDetails := range keelCfg.Images {

		imageRef, err := parseImage(vals, &imageDetails)
		if err != nil {
			log.WithFields(log.Fields{
				"error":           err,
				"repository_name": imageDetails.RepositoryPath,
				"repository_tag":  imageDetails.TagPath,
			}).Error("provider.helm: failed to parse image")
			continue
		}

		if imageRef.Repository() != eventRepoRef.Repository() {
			log.WithFields(log.Fields{
				"parsed_image_name": imageRef.Remote(),
				"target_image_name": repo.Name,
			}).Info("provider.helm: images do not match, ignoring")
			continue
		}

		// checking policy and whether we should update
		if keelCfg.Policy == types.PolicyTypeForce || imageRef.Tag() == "latest" {
			path, value := getPlanValues(newVersion, imageRef, &imageDetails)
			plan.Values[path] = value
			plan.NewVersion = newVersion.String()
			plan.CurrentVersion = imageRef.Tag()
			plan.Config = keelCfg
			shouldUpdateRelease = true

			log.WithFields(log.Fields{
				"parsed_image":     imageRef.Remote(),
				"target_image":     repo.Name,
				"target_image_tag": repo.Tag,
				"policy":           keelCfg.Policy,
			}).Info("provider.helm: impacted release container found")
			continue
		}

		// checking current
		currentVersion, err := version.GetVersion(imageRef.Tag())
		if err != nil {
			log.WithFields(log.Fields{
				"error":               err,
				"container_image":     imageRef.Repository(),
				"container_image_tag": imageRef.Tag(),
				"keel_policy":         keelCfg.Policy,
			}).Error("provider.helm: failed to get image version, is it tagged as semver?")
			continue
		}

		log.WithFields(log.Fields{
			"name":            name,
			"namespace":       namespace,
			"container_image": imageRef.Repository(),
			"current_version": currentVersion.String(),
			"policy":          keelCfg.Policy,
		}).Info("provider.helm: current image version")

		shouldUpdate, err := version.ShouldUpdate(currentVersion, newVersion, keelCfg.Policy)
		if err != nil {
			log.WithFields(log.Fields{
				"error":           err,
				"new_version":     newVersion.String(),
				"current_version": currentVersion.String(),
				"keel_policy":     keelCfg.Policy,
			}).Error("provider.helm: got error while checking whether deployment should be updated")
			continue
		}

		log.WithFields(log.Fields{
			"name":            name,
			"namespace":       namespace,
			"container_image": imageRef.Repository(),
			"current_version": currentVersion.String(),
			"new_version":     newVersion.String(),
			"policy":          keelCfg.Policy,
			"should_update":   shouldUpdate,
		}).Info("provider.helm: checked version, deciding whether to update")

		if shouldUpdate {
			path, value := getPlanValues(newVersion, imageRef, &imageDetails)
			plan.Values[path] = value
			plan.NewVersion = newVersion.String()
			plan.CurrentVersion = currentVersion.String()
			plan.Config = keelCfg
			shouldUpdateRelease = true

			log.WithFields(log.Fields{
				"container_image":  imageRef.Repository(),
				"target_image":     repo.Name,
				"target_image_tag": repo.Tag,
				"policy":           keelCfg.Policy,
			}).Info("provider.helm: impacted release tags found")
		}

	}
	return plan, shouldUpdateRelease, nil
}
<commit_msg>collecting release notes<commit_after>package helm

import (
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/util/image"
	"github.com/keel-hq/keel/util/version"

	hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"

	log "github.com/sirupsen/logrus"
)

func checkVersionedRelease(newVersion *types.Version, repo *types.Repository, namespace, name string, chart *hapi_chart.Chart, config *hapi_chart.Config) (plan *UpdatePlan, shouldUpdateRelease bool, err error) {
	plan = &UpdatePlan{
		Chart:     chart,
		Namespace: namespace,
		Name:      name,
		Values:    make(map[string]string),
	}

	eventRepoRef, err := image.Parse(repo.Name)
	if err != nil {
		log.WithFields(log.Fields{
			"error":           err,
			"repository_name": repo.Name,
		}).Error("provider.helm: failed to parse event repository name")
		return
	}

	// getting configuration
	vals, err := values(chart, config)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.helm: failed to get values.yaml for release")
		return
	}

	keelCfg, err := getKeelConfig(vals)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("provider.helm: failed to get keel configuration for release")
		// ignoring this release, no keel config found
		return plan, false, nil
	}
	// checking for impacted images
	for _, imageDetails := range keelCfg.Images {

		imageRef, err := parseImage(vals, &imageDetails)
		if err != nil {
			log.WithFields(log.Fields{
				"error":           err,
				"repository_name": imageDetails.RepositoryPath,
				"repository_tag":  imageDetails.TagPath,
			}).Error("provider.helm: failed to parse image")
			continue
		}

		if imageRef.Repository() != eventRepoRef.Repository() {
			log.WithFields(log.Fields{
				"parsed_image_name": imageRef.Remote(),
				"target_image_name": repo.Name,
			}).Info("provider.helm: images do not match, ignoring")
			continue
		}

		// checking policy and whether we should update
		if keelCfg.Policy == types.PolicyTypeForce || imageRef.Tag() == "latest" {
			path, value := getPlanValues(newVersion, imageRef, &imageDetails)
			plan.Values[path] = value
			plan.NewVersion = newVersion.String()
			plan.CurrentVersion = imageRef.Tag()
			plan.Config = keelCfg
			shouldUpdateRelease = true
			if imageDetails.ReleaseNotes != "" {
				plan.ReleaseNotes = append(plan.ReleaseNotes, imageDetails.ReleaseNotes)
			}

			log.WithFields(log.Fields{
				"parsed_image":     imageRef.Remote(),
				"target_image":     repo.Name,
				"target_image_tag": repo.Tag,
				"policy":           keelCfg.Policy,
			}).Info("provider.helm: impacted release container found")
			continue
		}

		// checking current
		currentVersion, err := version.GetVersion(imageRef.Tag())
		if err != nil {
			log.WithFields(log.Fields{
				"error":               err,
				"container_image":     imageRef.Repository(),
				"container_image_tag": imageRef.Tag(),
				"keel_policy":         keelCfg.Policy,
			}).Error("provider.helm: failed to get image version, is it tagged as semver?")
			continue
		}

		log.WithFields(log.Fields{
			"name":            name,
			"namespace":       namespace,
			"container_image": imageRef.Repository(),
			"current_version": currentVersion.String(),
			"policy":          keelCfg.Policy,
		}).Info("provider.helm: current image version")

		shouldUpdate, err := version.ShouldUpdate(currentVersion, newVersion, keelCfg.Policy)
		if err != nil {
			log.WithFields(log.Fields{
				"error":           err,
				"new_version":     newVersion.String(),
				"current_version": currentVersion.String(),
				"keel_policy":     keelCfg.Policy,
			}).Error("provider.helm: got error while checking whether deployment should be updated")
			continue
		}

		log.WithFields(log.Fields{
			"name":            name,
			"namespace":       namespace,
			"container_image": imageRef.Repository(),
			"current_version": currentVersion.String(),
			"new_version":     newVersion.String(),
			"policy":          keelCfg.Policy,
			"should_update":   shouldUpdate,
		}).Info("provider.helm: checked version, deciding whether to update")

		if shouldUpdate {
			path, value := getPlanValues(newVersion, imageRef, &imageDetails)
			plan.Values[path] = value
			plan.NewVersion = newVersion.String()
			plan.CurrentVersion = currentVersion.String()
			plan.Config = keelCfg
			shouldUpdateRelease = true
			if imageDetails.ReleaseNotes != "" {
				plan.ReleaseNotes = append(plan.ReleaseNotes, imageDetails.ReleaseNotes)
			}

			log.WithFields(log.Fields{
				"container_image":  imageRef.Repository(),
				"target_image":     repo.Name,
				"target_image_tag": repo.Tag,
				"policy":           keelCfg.Policy,
			}).Info("provider.helm: impacted release tags found")
		}

	}
	return plan, shouldUpdateRelease, nil
}
<|endoftext|>