<commit_before>package main

import (
	"fmt"
	"strings"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/parse"
)

// goTypeSQLite3 converts given SQL type to Go type. https://www.sqlite.org/datatype3.html
func goTypeSQLite3(sqlType string, nullable bool) (typ string, pack string, comment string) {
	// SQLite3 has quite unique dynamic type system with storage classes and type affinities.
	// In short:
	// * table columns don't have rigid types;
	// * value has storage class (null, integer, real, text, blob), which defines how value is stored on disk;
	// * table column has type affinity (text, numeric, integer, real, blob), which defines preferred storage class
	//   for values in this column;
	// * table column declared type in CREATE TABLE defines column affinity with a set of rules;
	// * table_info returns column declared type;
	// * we try to mirror SQLite's set of rules of defining column affinity from declared type to define Go type;
	// * we also extend this set of rules with some common SQL data types;
	// * it's not 100% accurate (because SQLite is dynamically typed), but it follows actual and best practices.

	sqlType = strings.ToLower(sqlType)

	// SQLite rules 1-4
	switch {
	case strings.Contains(sqlType, "int"):
		return maybePointer("int64", nullable), "", ""

	case strings.Contains(sqlType, "char") || strings.Contains(sqlType, "clob") || strings.Contains(sqlType, "text"):
		return maybePointer("string", nullable), "", ""

	case strings.Contains(sqlType, "blob") || sqlType == "":
		return "[]byte", "", "" // never a pointer

	case strings.Contains(sqlType, "real") || strings.Contains(sqlType, "floa") || strings.Contains(sqlType, "doub"):
		return maybePointer("float64", nullable), "", ""
	}

	// common SQL data types
	switch {
	// numeric, decimal, etc.
	case strings.Contains(sqlType, "num") || strings.Contains(sqlType, "dec"):
		return maybePointer("string", nullable), "", ""

	// bool, boolean, etc.
	case strings.Contains(sqlType, "bool"):
		return maybePointer("bool", nullable), "", ""

	// date, datetime, timestamp, etc.
	case strings.Contains(sqlType, "date") || strings.Contains(sqlType, "time"):
		return maybePointer("time.Time", nullable), "time", ""

	default:
		// logger.Fatalf("unhandled SQLite3 type %q", sqlType)
		return "[]byte", "", fmt.Sprintf("// FIXME unhandled database type %q", sqlType) // never a pointer
	}
}

// initModelsSQLite3 returns structs from SQLite3 database.
func initModelsSQLite3(db *reform.DB) (structs []StructData) {
	tables, err := db.SelectAllFrom(sqliteMasterView, "WHERE type = ?", "table")
	if err != nil {
		logger.Fatalf("%s", err)
	}

	for _, table := range tables {
		imports := make(map[string]struct{})
		tableName := table.(*sqliteMaster).Name
		if tableName == "sqlite_sequence" {
			continue
		}

		str := parse.StructInfo{
			Type:         convertName(tableName),
			SQLName:      tableName,
			PKFieldIndex: -1,
		}
		var comments []string

		rows, err := db.Query("PRAGMA table_info(" + tableName + ")") // no placeholders for PRAGMA
		if err != nil {
			logger.Fatalf("%s", err)
		}
		for {
			var column sqliteTableInfo
			if err = db.NextRow(&column, rows); err != nil {
				break
			}
			if column.PK {
				str.PKFieldIndex = len(str.Fields)
			}
			typ, pack, comment := goTypeSQLite3(column.Type, !column.NotNull)
			if pack != "" {
				imports[pack] = struct{}{}
			}
			comments = append(comments, comment)
			str.Fields = append(str.Fields, parse.FieldInfo{
				Name:   convertName(column.Name),
				Type:   typ,
				Column: column.Name,
			})
		}
		if err != reform.ErrNoRows {
			logger.Fatalf("%s", err)
		}
		if err = rows.Close(); err != nil {
			logger.Fatalf("%s", err)
		}

		structs = append(structs, StructData{
			Imports:       imports,
			StructInfo:    str,
			FieldComments: comments,
		})
	}

	return
}
<commit_msg>sqlite: generate models for views: https://github.com/go-reform/reform/issues/181<commit_after>package main

import (
	"fmt"
	"strings"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/parse"
)

// goTypeSQLite3 converts given SQL type to Go type. https://www.sqlite.org/datatype3.html
func goTypeSQLite3(sqlType string, nullable bool) (typ string, pack string, comment string) {
	// SQLite3 has quite unique dynamic type system with storage classes and type affinities.
	// In short:
	// * table columns don't have rigid types;
	// * value has storage class (null, integer, real, text, blob), which defines how value is stored on disk;
	// * table column has type affinity (text, numeric, integer, real, blob), which defines preferred storage class
	//   for values in this column;
	// * table column declared type in CREATE TABLE defines column affinity with a set of rules;
	// * table_info returns column declared type;
	// * we try to mirror SQLite's set of rules of defining column affinity from declared type to define Go type;
	// * we also extend this set of rules with some common SQL data types;
	// * it's not 100% accurate (because SQLite is dynamically typed), but it follows actual and best practices.

	sqlType = strings.ToLower(sqlType)

	// SQLite rules 1-4
	switch {
	case strings.Contains(sqlType, "int"):
		return maybePointer("int64", nullable), "", ""

	case strings.Contains(sqlType, "char") || strings.Contains(sqlType, "clob") || strings.Contains(sqlType, "text"):
		return maybePointer("string", nullable), "", ""

	case strings.Contains(sqlType, "blob") || sqlType == "":
		return "[]byte", "", "" // never a pointer

	case strings.Contains(sqlType, "real") || strings.Contains(sqlType, "floa") || strings.Contains(sqlType, "doub"):
		return maybePointer("float64", nullable), "", ""
	}

	// common SQL data types
	switch {
	// numeric, decimal, etc.
	case strings.Contains(sqlType, "num") || strings.Contains(sqlType, "dec"):
		return maybePointer("string", nullable), "", ""

	// bool, boolean, etc.
	case strings.Contains(sqlType, "bool"):
		return maybePointer("bool", nullable), "", ""

	// date, datetime, timestamp, etc.
	case strings.Contains(sqlType, "date") || strings.Contains(sqlType, "time"):
		return maybePointer("time.Time", nullable), "time", ""

	default:
		// logger.Fatalf("unhandled SQLite3 type %q", sqlType)
		return "[]byte", "", fmt.Sprintf("// FIXME unhandled database type %q", sqlType) // never a pointer
	}
}

// initModelsSQLite3 returns structs from SQLite3 database.
func initModelsSQLite3(db *reform.DB) (structs []StructData) {
	tables, err := db.SelectAllFrom(sqliteMasterView, "WHERE type in (?, ?)", "table", "view")
	if err != nil {
		logger.Fatalf("%s", err)
	}

	for _, table := range tables {
		imports := make(map[string]struct{})
		tableName := table.(*sqliteMaster).Name
		if tableName == "sqlite_sequence" {
			continue
		}

		str := parse.StructInfo{
			Type:         convertName(tableName),
			SQLName:      tableName,
			PKFieldIndex: -1,
		}
		var comments []string

		rows, err := db.Query("PRAGMA table_info(" + tableName + ")") // no placeholders for PRAGMA
		if err != nil {
			logger.Fatalf("%s", err)
		}
		for {
			var column sqliteTableInfo
			if err = db.NextRow(&column, rows); err != nil {
				break
			}
			if column.PK {
				str.PKFieldIndex = len(str.Fields)
			}
			typ, pack, comment := goTypeSQLite3(column.Type, !column.NotNull)
			if pack != "" {
				imports[pack] = struct{}{}
			}
			comments = append(comments, comment)
			str.Fields = append(str.Fields, parse.FieldInfo{
				Name:   convertName(column.Name),
				Type:   typ,
				Column: column.Name,
			})
		}
		if err != reform.ErrNoRows {
			logger.Fatalf("%s", err)
		}
		if err = rows.Close(); err != nil {
			logger.Fatalf("%s", err)
		}

		structs = append(structs, StructData{
			Imports:       imports,
			StructInfo:    str,
			FieldComments: comments,
		})
	}

	return
}
<|endoftext|>


<commit_before>package somatree

import (
	"crypto/sha512"
	"encoding/base64"
	"encoding/json"
	"io"
	"sort"

	"github.com/satori/go.uuid"
)

type Checker interface {
	SetCheck(c Check)
	LoadInstance(i CheckInstance)

	inheritCheck(c Check)
	inheritCheckDeep(c Check)
	storeCheck(c Check)
	syncCheck(childId string)
	checkCheck(checkId string) bool
}

type CheckGetter interface {
	GetCheckId() string
	GetSourceCheckId() string
	GetCheckConfigId() string
	GetSourceType() string
	GetIsInherited() bool
	GetInheritedFrom() string
	GetInheritance() bool
	GetChildrenOnly() bool
	GetView() string
	GetCapabilityId() string
	GetInterval() uint64
	GetItemId(objType string, objId uuid.UUID) uuid.UUID
}

type Check struct {
	Id            uuid.UUID
	SourceId      uuid.UUID
	SourceType    string
	Inherited     bool
	InheritedFrom uuid.UUID
	CapabilityId  uuid.UUID
	ConfigId      uuid.UUID
	Inheritance   bool
	ChildrenOnly  bool
	View          string
	Interval      uint64
	Thresholds    []CheckThreshold
	Constraints   []CheckConstraint
	Items         []CheckItem
}

type CheckItem struct {
	ObjectId   uuid.UUID
	ObjectType string
	ItemId     uuid.UUID
}

type CheckThreshold struct {
	Predicate string
	Level     uint8
	Value     int64
}

type CheckConstraint struct {
	Type  string
	Key   string
	Value string
}

type CheckInstance struct {
	InstanceId            uuid.UUID
	CheckId               uuid.UUID
	ConfigId              uuid.UUID
	InstanceConfigId      uuid.UUID
	Version               uint64
	ConstraintHash        string
	ConstraintValHash     string
	ConstraintOncall      string                         // Ids
	ConstraintService     map[string]string              // svcName->value
	ConstraintSystem      map[string]string              // Id->value
	ConstraintCustom      map[string]string              // Id->value
	ConstraintNative      map[string]string              // prop->value
	ConstraintAttribute   map[string]map[string][]string // svcId->attr->[ value, value, ... ]
	InstanceServiceConfig map[string]string              // attr->value
	InstanceService       string
	InstanceSvcCfgHash    string
}

func (c *Check) GetItemId(objType string, objId uuid.UUID) uuid.UUID {
	if !uuid.Equal(c.Id, uuid.Nil) {
		return c.Id
	}
	for _, item := range c.Items {
		if objType == item.ObjectType && uuid.Equal(item.ObjectId, objId) {
			return item.ItemId
		}
	}
	return uuid.Nil
}

func (c *Check) GetCheckId() string {
	return c.Id.String()
}

func (c *Check) GetSourceCheckId() string {
	return c.SourceId.String()
}

func (c *Check) GetCheckConfigId() string {
	return c.ConfigId.String()
}

func (c *Check) GetSourceType() string {
	return c.SourceType
}

func (c *Check) GetIsInherited() bool {
	return c.Inherited
}

func (c *Check) GetInheritedFrom() string {
	return c.InheritedFrom.String()
}

func (c *Check) GetInheritance() bool {
	return c.Inheritance
}

func (c *Check) GetChildrenOnly() bool {
	return c.ChildrenOnly
}

func (c *Check) GetView() string {
	return c.View
}

func (c *Check) GetCapabilityId() string {
	return c.CapabilityId.String()
}

func (c *Check) GetInterval() uint64 {
	return c.Interval
}

func (c *Check) MakeAction() Action {
	return Action{
		Check: proto.Check{
			CheckId:       c.GetCheckId(),
			SourceCheckId: c.GetSourceCheckId(),
			CheckConfigId: c.GetCheckConfigId(),
			SourceType:    c.GetSourceType(),
			IsInherited:   c.GetIsInherited(),
			InheritedFrom: c.GetInheritedFrom(),
			Inheritance:   c.GetInheritance(),
			ChildrenOnly:  c.GetChildrenOnly(),
			CapabilityId:  c.GetCapabilityId(),
		},
	}
}

func (tci *CheckInstance) Clone() CheckInstance {
	cl := CheckInstance{
		Version:            tci.Version,
		ConstraintHash:     tci.ConstraintHash,
		ConstraintValHash:  tci.ConstraintValHash,
		ConstraintOncall:   tci.ConstraintOncall,
		InstanceSvcCfgHash: tci.InstanceSvcCfgHash,
		InstanceService:    tci.InstanceService,
	}
	cl.InstanceConfigId, _ = uuid.FromString(tci.InstanceConfigId.String())
	cl.InstanceId, _ = uuid.FromString(tci.InstanceId.String())
	cl.CheckId, _ = uuid.FromString(tci.CheckId.String())
	cl.ConfigId, _ = uuid.FromString(tci.ConfigId.String())
	cl.ConstraintService = make(map[string]string)
	for k, v := range tci.ConstraintService {
		t := v
		cl.ConstraintService[k] = t
	}
	cl.ConstraintSystem = make(map[string]string)
	for k, v := range tci.ConstraintSystem {
		t := v
		cl.ConstraintSystem[k] = t
	}
	cl.ConstraintCustom = make(map[string]string)
	for k, v := range tci.ConstraintCustom {
		t := v
		cl.ConstraintCustom[k] = t
	}
	cl.ConstraintNative = make(map[string]string)
	for k, v := range tci.ConstraintNative {
		t := v
		cl.ConstraintNative[k] = t
	}
	cl.InstanceServiceConfig = make(map[string]string)
	for k, v := range tci.InstanceServiceConfig {
		t := v
		cl.InstanceServiceConfig[k] = t
	}
	cl.ConstraintAttribute = make(map[string]map[string][]string, 0)
	for k, _ := range tci.ConstraintAttribute {
		cl.ConstraintAttribute[k] = make(map[string][]string)
		for k2, aVal := range tci.ConstraintAttribute[k] {
			for _, val := range aVal {
				t := val
				cl.ConstraintAttribute[k][k2] = append(cl.ConstraintAttribute[k][k2], t)
			}
		}
	}

	return cl
}

func (tci *CheckInstance) calcConstraintHash() {
	h := sha512.New()
	io.WriteString(h, tci.ConstraintOncall)

	services := []string{}
	for i, _ := range tci.ConstraintService {
		j := i
		services = append(services, j)
	}
	sort.Strings(services)
	for _, i := range services {
		io.WriteString(h, i)
	}

	systems := []string{}
	for i, _ := range tci.ConstraintSystem {
		j := i
		systems = append(systems, j)
	}
	sort.Strings(systems)
	for _, i := range systems {
		io.WriteString(h, i)
	}

	customs := []string{}
	for i, _ := range tci.ConstraintCustom {
		j := i
		customs = append(customs, j)
	}
	sort.Strings(customs)
	for _, i := range customs {
		io.WriteString(h, i)
	}

	natives := []string{}
	for i, _ := range tci.ConstraintNative {
		j := i
		natives = append(natives, j)
	}
	sort.Strings(natives)
	for _, i := range natives {
		io.WriteString(h, i)
	}

	attributes := []string{}
	for i, _ := range tci.ConstraintAttribute {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		svcattr := []string{}
		for j, _ := range tci.ConstraintAttribute[i] {
			k := j
			svcattr = append(svcattr, k)
		}
		sort.Strings(svcattr)
		io.WriteString(h, i)
		for _, l := range svcattr {
			io.WriteString(h, l)
		}
	}
	tci.ConstraintHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (tci *CheckInstance) calcConstraintValHash() {
	h := sha512.New()
	io.WriteString(h, tci.ConstraintOncall)

	services := []string{}
	for i, _ := range tci.ConstraintService {
		j := i
		services = append(services, j)
	}
	sort.Strings(services)
	for _, i := range services {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintService[i])
	}

	systems := []string{}
	for i, _ := range tci.ConstraintSystem {
		j := i
		systems = append(systems, j)
	}
	sort.Strings(systems)
	for _, i := range systems {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintSystem[i])
	}

	customs := []string{}
	for i, _ := range tci.ConstraintCustom {
		j := i
		customs = append(customs, j)
	}
	sort.Strings(customs)
	for _, i := range customs {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintCustom[i])
	}

	natives := []string{}
	for i, _ := range tci.ConstraintNative {
		j := i
		natives = append(natives, j)
	}
	sort.Strings(natives)
	for _, i := range natives {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintNative[i])
	}

	attributes := []string{}
	for i, _ := range tci.ConstraintAttribute {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		svcattr := []string{}
		for j, _ := range tci.ConstraintAttribute[i] {
			k := j
			svcattr = append(svcattr, k)
		}
		sort.Strings(svcattr)
		io.WriteString(h, i)
		for _, l := range svcattr {
			io.WriteString(h, l)
			vals := make([]string, len(tci.ConstraintAttribute[i][l]))
			copy(vals, tci.ConstraintAttribute[i][l])
			sort.Strings(vals)
			for _, m := range vals {
				io.WriteString(h, m)
			}
		}
	}
	tci.ConstraintValHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (tci *CheckInstance) calcInstanceSvcCfgHash() {
	h := sha512.New()

	attributes := []string{}
	for i, _ := range tci.InstanceServiceConfig {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		io.WriteString(h, i)
		io.WriteString(h, tci.InstanceServiceConfig[i])
	}
	tci.InstanceSvcCfgHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (c *Check) clone() Check {
	cl := Check{
		SourceType:   c.SourceType,
		Inherited:    c.Inherited,
		Inheritance:  c.Inheritance,
		ChildrenOnly: c.ChildrenOnly,
		View:         c.View,
		Interval:     c.Interval,
	}
	cl.Id, _ = uuid.FromString(c.Id.String())
	cl.SourceId, _ = uuid.FromString(c.SourceId.String())
	cl.InheritedFrom, _ = uuid.FromString(c.InheritedFrom.String())
	cl.CapabilityId, _ = uuid.FromString(c.CapabilityId.String())
	cl.ConfigId, _ = uuid.FromString(c.ConfigId.String())
	cl.Thresholds = make([]CheckThreshold, len(c.Thresholds))
	for i, thr := range c.Thresholds {
		n := CheckThreshold{
			Predicate: thr.Predicate,
			Level:     thr.Level,
			Value:     thr.Value,
		}
		cl.Thresholds[i] = n
	}
	cl.Constraints = make([]CheckConstraint, len(c.Constraints))
	for i, ctr := range c.Constraints {
		n := CheckConstraint{
			Type:  ctr.Type,
			Key:   ctr.Key,
			Value: ctr.Value,
		}
		cl.Constraints[i] = n
	}
	cl.Items = make([]CheckItem, len(c.Items))
	for i, item := range c.Items {
		n := CheckItem{
			ObjectType: item.ObjectType,
		}
		n.ItemId, _ = uuid.FromString(item.ItemId.String())
		n.ObjectId, _ = uuid.FromString(item.ObjectId.String())
		cl.Items[i] = n
	}
	return cl
}

func (ci CheckInstance) MakeAction() Action {
	serviceCfg, err := json.Marshal(ci.InstanceServiceConfig)
	if err != nil {
		serviceCfg = []byte{}
	}

	return Action{
		CheckInstance: proto.CheckInstance{
			InstanceId:            ci.InstanceId.String(),
			CheckId:               ci.CheckId.String(),
			ConfigId:              ci.ConfigId.String(),
			InstanceConfigId:      ci.InstanceConfigId.String(),
			Version:               ci.Version,
			ConstraintHash:        ci.ConstraintHash,
			ConstraintValHash:     ci.ConstraintValHash,
			InstanceSvcCfgHash:    ci.InstanceSvcCfgHash,
			InstanceService:       ci.InstanceService,
			InstanceServiceConfig: string(serviceCfg),
		},
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add Clone functions to checker structs<commit_after>package somatree

import (
	"crypto/sha512"
	"encoding/base64"
	"encoding/json"
	"io"
	"sort"

	"github.com/satori/go.uuid"
)

type Checker interface {
	SetCheck(c Check)
	LoadInstance(i CheckInstance)

	inheritCheck(c Check)
	inheritCheckDeep(c Check)
	storeCheck(c Check)
	syncCheck(childId string)
	checkCheck(checkId string) bool
}

type CheckGetter interface {
	GetCheckId() string
	GetSourceCheckId() string
	GetCheckConfigId() string
	GetSourceType() string
	GetIsInherited() bool
	GetInheritedFrom() string
	GetInheritance() bool
	GetChildrenOnly() bool
	GetView() string
	GetCapabilityId() string
	GetInterval() uint64
	GetItemId(objType string, objId uuid.UUID) uuid.UUID
}

type Check struct {
	Id            uuid.UUID
	SourceId      uuid.UUID
	SourceType    string
	Inherited     bool
	InheritedFrom uuid.UUID
	CapabilityId  uuid.UUID
	ConfigId      uuid.UUID
	Inheritance   bool
	ChildrenOnly  bool
	View          string
	Interval      uint64
	Thresholds    []CheckThreshold
	Constraints   []CheckConstraint
	Items         []CheckItem
}

func (c *Check) Clone() Check {
	ng := Check{
		SourceType:   c.SourceType,
		Inherited:    c.Inherited,
		Inheritance:  c.Inheritance,
		ChildrenOnly: c.ChildrenOnly,
		View:         c.View,
		Interval:     c.Interval,
	}
	ng.Id, _ = uuid.FromString(c.Id.String())
	ng.SourceId, _ = uuid.FromString(c.SourceId.String())
	ng.InheritedFrom, _ = uuid.FromString(c.InheritedFrom.String())
	ng.CapabilityId, _ = uuid.FromString(c.CapabilityId.String())
	ng.ConfigId, _ = uuid.FromString(c.ConfigId.String())

	ng.Thresholds = make([]CheckThreshold, len(c.Thresholds))
	for i, _ := range c.Thresholds {
		ng.Thresholds[i] = c.Thresholds[i].Clone()
	}

	ng.Constraints = make([]CheckConstraint, len(c.Constraints))
	for i, _ := range c.Constraints {
		ng.Constraints[i] = c.Constraints[i].Clone()
	}

	ng.Items = make([]CheckItem, len(c.Items))
	for i, _ := range c.Items {
		ng.Items[i] = c.Items[i].Clone()
	}

	return ng
}

type CheckItem struct {
	ObjectId   uuid.UUID
	ObjectType string
	ItemId     uuid.UUID
}

func (ci *CheckItem) Clone() CheckItem {
	oid, _ := uuid.FromString(ci.ObjectId.String())
	iid, _ := uuid.FromString(ci.ItemId.String())
	return CheckItem{
		ObjectId:   oid,
		ObjectType: ci.ObjectType,
		ItemId:     iid,
	}
}

type CheckThreshold struct {
	Predicate string
	Level     uint8
	Value     int64
}

func (ct *CheckThreshold) Clone() CheckThreshold {
	return CheckThreshold{
		Predicate: ct.Predicate,
		Level:     ct.Level,
		Value:     ct.Value,
	}
}

type CheckConstraint struct {
	Type  string
	Key   string
	Value string
}

func (cc *CheckConstraint) Clone() CheckConstraint {
	return CheckConstraint{
		Type:  cc.Type,
		Key:   cc.Key,
		Value: cc.Value,
	}
}

type CheckInstance struct {
	InstanceId            uuid.UUID
	CheckId               uuid.UUID
	ConfigId              uuid.UUID
	InstanceConfigId      uuid.UUID
	Version               uint64
	ConstraintHash        string
	ConstraintValHash     string
	ConstraintOncall      string                         // Ids
	ConstraintService     map[string]string              // svcName->value
	ConstraintSystem      map[string]string              // Id->value
	ConstraintCustom      map[string]string              // Id->value
	ConstraintNative      map[string]string              // prop->value
	ConstraintAttribute   map[string]map[string][]string // svcId->attr->[ value, value, ... ]
	InstanceServiceConfig map[string]string              // attr->value
	InstanceService       string
	InstanceSvcCfgHash    string
}

func (c *Check) GetItemId(objType string, objId uuid.UUID) uuid.UUID {
	if !uuid.Equal(c.Id, uuid.Nil) {
		return c.Id
	}
	for _, item := range c.Items {
		if objType == item.ObjectType && uuid.Equal(item.ObjectId, objId) {
			return item.ItemId
		}
	}
	return uuid.Nil
}

func (c *Check) GetCheckId() string {
	return c.Id.String()
}

func (c *Check) GetSourceCheckId() string {
	return c.SourceId.String()
}

func (c *Check) GetCheckConfigId() string {
	return c.ConfigId.String()
}

func (c *Check) GetSourceType() string {
	return c.SourceType
}

func (c *Check) GetIsInherited() bool {
	return c.Inherited
}

func (c *Check) GetInheritedFrom() string {
	return c.InheritedFrom.String()
}

func (c *Check) GetInheritance() bool {
	return c.Inheritance
}

func (c *Check) GetChildrenOnly() bool {
	return c.ChildrenOnly
}

func (c *Check) GetView() string {
	return c.View
}

func (c *Check) GetCapabilityId() string {
	return c.CapabilityId.String()
}

func (c *Check) GetInterval() uint64 {
	return c.Interval
}

func (c *Check) MakeAction() Action {
	return Action{
		Check: proto.Check{
			CheckId:       c.GetCheckId(),
			SourceCheckId: c.GetSourceCheckId(),
			CheckConfigId: c.GetCheckConfigId(),
			SourceType:    c.GetSourceType(),
			IsInherited:   c.GetIsInherited(),
			InheritedFrom: c.GetInheritedFrom(),
			Inheritance:   c.GetInheritance(),
			ChildrenOnly:  c.GetChildrenOnly(),
			CapabilityId:  c.GetCapabilityId(),
		},
	}
}

func (tci *CheckInstance) Clone() CheckInstance {
	cl := CheckInstance{
		Version:            tci.Version,
		ConstraintHash:     tci.ConstraintHash,
		ConstraintValHash:  tci.ConstraintValHash,
		ConstraintOncall:   tci.ConstraintOncall,
		InstanceSvcCfgHash: tci.InstanceSvcCfgHash,
		InstanceService:    tci.InstanceService,
	}
	cl.InstanceConfigId, _ = uuid.FromString(tci.InstanceConfigId.String())
	cl.InstanceId, _ = uuid.FromString(tci.InstanceId.String())
	cl.CheckId, _ = uuid.FromString(tci.CheckId.String())
	cl.ConfigId, _ = uuid.FromString(tci.ConfigId.String())
	cl.ConstraintService = make(map[string]string)
	for k, v := range tci.ConstraintService {
		t := v
		cl.ConstraintService[k] = t
	}
	cl.ConstraintSystem = make(map[string]string)
	for k, v := range tci.ConstraintSystem {
		t := v
		cl.ConstraintSystem[k] = t
	}
	cl.ConstraintCustom = make(map[string]string)
	for k, v := range tci.ConstraintCustom {
		t := v
		cl.ConstraintCustom[k] = t
	}
	cl.ConstraintNative = make(map[string]string)
	for k, v := range tci.ConstraintNative {
		t := v
		cl.ConstraintNative[k] = t
	}
	cl.InstanceServiceConfig = make(map[string]string)
	for k, v := range tci.InstanceServiceConfig {
		t := v
		cl.InstanceServiceConfig[k] = t
	}
	cl.ConstraintAttribute = make(map[string]map[string][]string, 0)
	for k, _ := range tci.ConstraintAttribute {
		cl.ConstraintAttribute[k] = make(map[string][]string)
		for k2, aVal := range tci.ConstraintAttribute[k] {
			for _, val := range aVal {
				t := val
				cl.ConstraintAttribute[k][k2] = append(cl.ConstraintAttribute[k][k2], t)
			}
		}
	}

	return cl
}

func (tci *CheckInstance) calcConstraintHash() {
	h := sha512.New()
	io.WriteString(h, tci.ConstraintOncall)

	services := []string{}
	for i, _ := range tci.ConstraintService {
		j := i
		services = append(services, j)
	}
	sort.Strings(services)
	for _, i := range services {
		io.WriteString(h, i)
	}

	systems := []string{}
	for i, _ := range tci.ConstraintSystem {
		j := i
		systems = append(systems, j)
	}
	sort.Strings(systems)
	for _, i := range systems {
		io.WriteString(h, i)
	}

	customs := []string{}
	for i, _ := range tci.ConstraintCustom {
		j := i
		customs = append(customs, j)
	}
	sort.Strings(customs)
	for _, i := range customs {
		io.WriteString(h, i)
	}

	natives := []string{}
	for i, _ := range tci.ConstraintNative {
		j := i
		natives = append(natives, j)
	}
	sort.Strings(natives)
	for _, i := range natives {
		io.WriteString(h, i)
	}

	attributes := []string{}
	for i, _ := range tci.ConstraintAttribute {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		svcattr := []string{}
		for j, _ := range tci.ConstraintAttribute[i] {
			k := j
			svcattr = append(svcattr, k)
		}
		sort.Strings(svcattr)
		io.WriteString(h, i)
		for _, l := range svcattr {
			io.WriteString(h, l)
		}
	}
	tci.ConstraintHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (tci *CheckInstance) calcConstraintValHash() {
	h := sha512.New()
	io.WriteString(h, tci.ConstraintOncall)

	services := []string{}
	for i, _ := range tci.ConstraintService {
		j := i
		services = append(services, j)
	}
	sort.Strings(services)
	for _, i := range services {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintService[i])
	}

	systems := []string{}
	for i, _ := range tci.ConstraintSystem {
		j := i
		systems = append(systems, j)
	}
	sort.Strings(systems)
	for _, i := range systems {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintSystem[i])
	}

	customs := []string{}
	for i, _ := range tci.ConstraintCustom {
		j := i
		customs = append(customs, j)
	}
	sort.Strings(customs)
	for _, i := range customs {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintCustom[i])
	}

	natives := []string{}
	for i, _ := range tci.ConstraintNative {
		j := i
		natives = append(natives, j)
	}
	sort.Strings(natives)
	for _, i := range natives {
		io.WriteString(h, i)
		io.WriteString(h, tci.ConstraintNative[i])
	}

	attributes := []string{}
	for i, _ := range tci.ConstraintAttribute {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		svcattr := []string{}
		for j, _ := range tci.ConstraintAttribute[i] {
			k := j
			svcattr = append(svcattr, k)
		}
		sort.Strings(svcattr)
		io.WriteString(h, i)
		for _, l := range svcattr {
			io.WriteString(h, l)
			vals := make([]string, len(tci.ConstraintAttribute[i][l]))
			copy(vals, tci.ConstraintAttribute[i][l])
			sort.Strings(vals)
			for _, m := range vals {
				io.WriteString(h, m)
			}
		}
	}
	tci.ConstraintValHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (tci *CheckInstance) calcInstanceSvcCfgHash() {
	h := sha512.New()

	attributes := []string{}
	for i, _ := range tci.InstanceServiceConfig {
		j := i
		attributes = append(attributes, j)
	}
	sort.Strings(attributes)
	for _, i := range attributes {
		io.WriteString(h, i)
		io.WriteString(h, tci.InstanceServiceConfig[i])
	}
	tci.InstanceSvcCfgHash = base64.URLEncoding.EncodeToString(h.Sum(nil))
}

func (c *Check) clone() Check {
	cl := Check{
		SourceType:   c.SourceType,
		Inherited:    c.Inherited,
		Inheritance:  c.Inheritance,
		ChildrenOnly: c.ChildrenOnly,
		View:         c.View,
		Interval:     c.Interval,
	}
	cl.Id, _ = uuid.FromString(c.Id.String())
	cl.SourceId, _ = uuid.FromString(c.SourceId.String())
	cl.InheritedFrom, _ = uuid.FromString(c.InheritedFrom.String())
	cl.CapabilityId, _ = uuid.FromString(c.CapabilityId.String())
	cl.ConfigId, _ = uuid.FromString(c.ConfigId.String())
	cl.Thresholds = make([]CheckThreshold, len(c.Thresholds))
	for i, thr := range c.Thresholds {
		n := CheckThreshold{
			Predicate: thr.Predicate,
			Level:     thr.Level,
			Value:     thr.Value,
		}
		cl.Thresholds[i] = n
	}
	cl.Constraints = make([]CheckConstraint, len(c.Constraints))
	for i, ctr := range c.Constraints {
		n := CheckConstraint{
			Type:  ctr.Type,
			Key:   ctr.Key,
			Value: ctr.Value,
		}
		cl.Constraints[i] = n
	}
	cl.Items = make([]CheckItem, len(c.Items))
	for i, item := range c.Items {
		n := CheckItem{
			ObjectType: item.ObjectType,
		}
		n.ItemId, _ = uuid.FromString(item.ItemId.String())
		n.ObjectId, _ = uuid.FromString(item.ObjectId.String())
		cl.Items[i] = n
	}
	return cl
}

func (ci CheckInstance) MakeAction() Action {
	serviceCfg, err := json.Marshal(ci.InstanceServiceConfig)
	if err != nil {
		serviceCfg = []byte{}
	}

	return Action{
		CheckInstance: proto.CheckInstance{
			InstanceId:            ci.InstanceId.String(),
			CheckId:               ci.CheckId.String(),
			ConfigId:              ci.ConfigId.String(),
			InstanceConfigId:      ci.InstanceConfigId.String(),
			Version:               ci.Version,
			ConstraintHash:        ci.ConstraintHash,
			ConstraintValHash:     ci.ConstraintValHash,
			InstanceSvcCfgHash:    ci.InstanceSvcCfgHash,
			InstanceService:       ci.InstanceService,
			InstanceServiceConfig: string(serviceCfg),
		},
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package replicaset

import (
	"fmt"
	"testing"
	"time"

	gc "launchpad.net/gocheck"

	"labix.org/v2/mgo"

	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils"
)

var (
	name = "juju"
	root *coretesting.MgoInstance
)

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

func newServer() (*coretesting.MgoInstance, error) {
	inst := &coretesting.MgoInstance{Params: []string{"--replSet", name}}

	err := inst.Start(true)
	if err != nil {
		return nil, fmt.Errorf("Error starting mongo server: %s", err.Error())
	}

	// by dialing right now, we'll wait until it's running
	strategy := utils.AttemptStrategy{Total: time.Second * 5, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		var session *mgo.Session
		session, err = inst.DialDirect()
		if err != nil {
			err = fmt.Errorf("Error dialing mongo server %q: %s", inst.Addr(), err.Error())
		} else {
			session.SetMode(mgo.Monotonic, true)
			err = session.Ping()
			if err != nil {
				err = fmt.Errorf("Error pinging mongo server %q: %s", inst.Addr(), err.Error())
			}
			session.Close()
		}
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	return inst, err
}

type MongoSuite struct{}

var _ = gc.Suite(&MongoSuite{})

func (s *MongoSuite) SetUpSuite(c *gc.C) {
	var err error
	// do all this stuff here, since we don't want to have to redo it for each test
	root, err = newServer()
	if err != nil {
		c.Fatalf("Got error from Start of root server: %s", err.Error())
	}
	// note, this is an actual test around Initiate, but again, I don't want to
	// have to redo it, so I just do it once.
	dialAndTestInitiate(c)
}

func (s *MongoSuite) TearDownTest(c *gc.C) {
	// remove all secondaries from the replicaset on test teardown
	session, err := root.DialDirect()
	if err != nil {
		c.Logf("Failed to dial root during test cleanup: %v", err)
		return
	}
	defer session.Close()
	mems, err := CurrentMembers(session)
	if err != nil {
		c.Logf("Failed to get list of memners during test cleanup: %v", err)
		return
	}

	addrs := []string{}
	for _, m := range mems {
		if root.Addr() != m.Address {
			addrs = append(addrs, m.Address)
		}
	}
	if err = Remove(session, addrs...); err != nil {
		c.Logf("Error removing secondaries: %v", err)
	}
}

func dialAndTestInitiate(c *gc.C) {
	session := root.MustDialDirect()
	defer session.Close()

	err := Initiate(session, root.Addr(), name)
	c.Assert(err, gc.IsNil)

	// Ids start at 1 for us, so we can differentiate between set and unset
	expectedMembers := []Member{Member{Id: 1, Address: root.Addr()}}

	// need to set mode to strong so that we wait for the write to succeed
	// before reading and thus ensure that we're getting consistent reads.
	session.SetMode(mgo.Strong, false)

	mems, err := CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// now add some data so we get a more real-life test
	loadData(session, c)
}

func loadData(session *mgo.Session, c *gc.C) {
	type foo struct {
		Name    string
		Address string
		Count   int
	}

	for col := 0; col < 10; col++ {
		foos := make([]foo, 10000)
		for n := range foos {
			foos[n] = foo{
				Name:    fmt.Sprintf("name_%d_%d", col, n),
				Address: fmt.Sprintf("address_%d_%d", col, n),
				Count:   n * (col + 1),
			}
		}

		err := session.DB("testing").C(fmt.Sprintf("data%d", col)).Insert(foos)
		c.Assert(err, gc.IsNil)
	}
}

func (s *MongoSuite) TearDownSuite(c *gc.C) {
	root.Destroy()
}

func (s *MongoSuite) TestAddRemoveSet(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	members := make([]Member, 0, 5)

	// Add should be idempotent, so re-adding root here shouldn't result in
	// two copies of root in the replica set
	members = append(members, Member{Address: root.Addr()})

	instances := make([]*coretesting.MgoInstance, 0, 5)
	instances = append(instances, root)

	for x := 0; x < 4; x++ {
		inst, err := newServer()
		c.Assert(err, gc.IsNil)
		instances = append(instances, inst)
		defer inst.Destroy()
		defer Remove(session, inst.Addr())

		key := fmt.Sprintf("key%d", x)
		val := fmt.Sprintf("val%d", x)

		tags := map[string]string{key: val}

		members = append(members, Member{Address: inst.Addr(), Tags: tags})
	}

	var err error

	strategy := utils.AttemptStrategy{Total: time.Second * 30, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, members...)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers := make([]Member, len(members))
	for x, m := range members {
		// Ids should start at 1 (for the root) and go up
		m.Id = x + 1
		expectedMembers[x] = m
	}

	var cfg *Config
	attempt = strategy.Start()
	for attempt.Next() {
		cfg, err = CurrentConfig(session)
		if err == nil || !attempt.HasNext() {
			break
		}
	}

	c.Assert(err, gc.IsNil)
	c.Assert(cfg.Name, gc.Equals, name)

	// 2 since we already changed it once
	c.Assert(cfg.Version, gc.Equals, 2)

	mems := cfg.Members

	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// Now remove the last two Members
	attempt = strategy.Start()
	for attempt.Next() {
		err = Remove(session, members[3].Address, members[4].Address)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers = expectedMembers[0:3]

	mems, err = CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// now let's mix it up and set the new members to a mix of the previous
	// plus the new arbiter
	mems = []Member{members[3], mems[2], mems[0], members[4]}

	attempt = strategy.Start()
	for attempt.Next() {
		err = Set(session, mems)
		if err == nil || !attempt.HasNext() {
			break
		}
	}

	c.Assert(err, gc.IsNil)

	attempt = strategy.Start()
	for attempt.Next() {
		// can dial whichever replica address here, mongo will figure it out
		session = instances[0].MustDialDirect()
		err = session.Ping()
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers = []Member{members[3], expectedMembers[2], expectedMembers[0], members[4]}

	// any new members will get an id of max(other_ids...)+1
	expectedMembers[0].Id = 4
	expectedMembers[3].Id = 5

	attempt = strategy.Start()
	for attempt.Next() {
		mems, err = CurrentMembers(session)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)
}

func (s *MongoSuite) TestIsMaster(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	expected := IsMasterResults{
		// The following fields hold information about the specific mongodb node.
		IsMaster:  true,
		Secondary: false,
		Arbiter:   false,
		Address:   root.Addr(),
		LocalTime: time.Time{},

		// The following fields hold information about the replica set.
		ReplicaSetName: name,
		Addresses:      []string{root.Addr()},
		Arbiters:       nil,
		PrimaryAddress: root.Addr(),
	}

	res, err := IsMaster(session)
	c.Assert(err, gc.IsNil)
	c.Check(closeEnough(res.LocalTime, time.Now()), gc.Equals, true)
	res.LocalTime = time.Time{}
	c.Check(*res, gc.DeepEquals, expected)
}

func (s *MongoSuite) TestCurrentStatus(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	inst1, err := newServer()
	c.Assert(err, gc.IsNil)
	defer inst1.Destroy()
	defer Remove(session, inst1.Addr())

	inst2, err := newServer()
	c.Assert(err, gc.IsNil)
	defer inst2.Destroy()
	defer Remove(session, inst2.Addr())

	strategy := utils.AttemptStrategy{Total: time.Second * 30, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, Member{Address: inst1.Addr()}, Member{Address: inst2.Addr()})
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expected := &Status{
		Name: name,
		Members: []MemberStatus{{
			Id:      1,
			Address: root.Addr(),
			Self:    true,
			ErrMsg:  "",
			Healthy: true,
			State:   PrimaryState,
		}, {
			Id:      2,
			Address: inst1.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}, {
			Id:      3,
			Address: inst2.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}},
	}

	strategy.Total = time.Second * 60
	attempt = strategy.Start()
	var res *Status
	for attempt.Next() {
		var err error
		res, err = CurrentStatus(session)

		if err != nil && !attempt.HasNext() {
			c.Errorf("Couldn't get status before timeout, got err: %v", err)
			return
		}

		if res.Members[0].State == PrimaryState &&
			res.Members[1].State == SecondaryState &&
			res.Members[2].State == SecondaryState {
			break
		}
		if !attempt.HasNext() {
			c.Errorf("Servers did not get into final state before timeout.  Status: %#v", res)
			return
		}
	}

	for x, _ := range res.Members {
		// non-empty uptime and ping
		c.Check(res.Members[x].Uptime, gc.Not(gc.Equals), 0)

		// ping is always going to be zero since we're on localhost
		// so we can't really test it right now

		// now overwrite Uptime so it won't throw off DeepEquals
		res.Members[x].Uptime = 0
	}
	c.Check(res, gc.DeepEquals, expected)
}

func closeEnough(expected, obtained time.Time) bool {
	t := obtained.Sub(expected)
	return (-500*time.Millisecond) < t && t < (500*time.Millisecond)
}
<commit_msg>[r=axwalk] replicaset: fix race/nil pointer deref in test<commit_after>package replicaset

import (
	"fmt"
	"testing"
	"time"

	gc "launchpad.net/gocheck"

	"labix.org/v2/mgo"

	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils"
)

var (
	name = "juju"
	root *coretesting.MgoInstance
)

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

func newServer() (*coretesting.MgoInstance, error) {
	inst := &coretesting.MgoInstance{Params: []string{"--replSet", name}}

	err := inst.Start(true)
	if err != nil {
		return nil, fmt.Errorf("Error starting mongo server: %s", err.Error())
	}

	// by dialing right now, we'll wait until it's running
	strategy := utils.AttemptStrategy{Total: time.Second * 5, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		var session *mgo.Session
		session, err = inst.DialDirect()
		if err != nil {
			err = fmt.Errorf("Error dialing mongo server %q: %s", inst.Addr(), err.Error())
		} else {
			session.SetMode(mgo.Monotonic, true)
			err = session.Ping()
			if err != nil {
				err = fmt.Errorf("Error pinging mongo server %q: %s", inst.Addr(), err.Error())
			}
			session.Close()
		}
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	return inst, err
}

type MongoSuite struct{}

var _ = gc.Suite(&MongoSuite{})

func (s *MongoSuite) SetUpSuite(c *gc.C) {
	var err error
	// do all this stuff here, since we don't want to have to redo it for each test
	root, err = newServer()
	if err != nil {
		c.Fatalf("Got error from Start of root server: %s", err.Error())
	}
	// note, this is an actual test around Initiate, but again, I don't want to
	// have to redo it, so I just do it once.
	dialAndTestInitiate(c)
}

func (s *MongoSuite) TearDownTest(c *gc.C) {
	// remove all secondaries from the replicaset on test teardown
	session, err := root.DialDirect()
	if err != nil {
		c.Logf("Failed to dial root during test cleanup: %v", err)
		return
	}
	defer session.Close()
	mems, err := CurrentMembers(session)
	if err != nil {
		c.Logf("Failed to get list of memners during test cleanup: %v", err)
		return
	}

	addrs := []string{}
	for _, m := range mems {
		if root.Addr() != m.Address {
			addrs = append(addrs, m.Address)
		}
	}
	if err = Remove(session, addrs...); err != nil {
		c.Logf("Error removing secondaries: %v", err)
	}
}

func dialAndTestInitiate(c *gc.C) {
	session := root.MustDialDirect()
	defer session.Close()

	err := Initiate(session, root.Addr(), name)
	c.Assert(err, gc.IsNil)

	// Ids start at 1 for us, so we can differentiate between set and unset
	expectedMembers := []Member{Member{Id: 1, Address: root.Addr()}}

	// need to set mode to strong so that we wait for the write to succeed
	// before reading and thus ensure that we're getting consistent reads.
	session.SetMode(mgo.Strong, false)

	mems, err := CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// now add some data so we get a more real-life test
	loadData(session, c)
}

func loadData(session *mgo.Session, c *gc.C) {
	type foo struct {
		Name    string
		Address string
		Count   int
	}

	for col := 0; col < 10; col++ {
		foos := make([]foo, 10000)
		for n := range foos {
			foos[n] = foo{
				Name:    fmt.Sprintf("name_%d_%d", col, n),
				Address: fmt.Sprintf("address_%d_%d", col, n),
				Count:   n * (col + 1),
			}
		}

		err := session.DB("testing").C(fmt.Sprintf("data%d", col)).Insert(foos)
		c.Assert(err, gc.IsNil)
	}
}

func (s *MongoSuite) TearDownSuite(c *gc.C) {
	root.Destroy()
}

func (s *MongoSuite) TestAddRemoveSet(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	members := make([]Member, 0, 5)

	// Add should be idempotent, so re-adding root here shouldn't result in
	// two copies of root in the replica set
	members = append(members, Member{Address: root.Addr()})

	instances := make([]*coretesting.MgoInstance, 0, 5)
	instances = append(instances, root)

	for x := 0; x < 4; x++ {
		inst, err := newServer()
		c.Assert(err, gc.IsNil)
		instances = append(instances, inst)
		defer inst.Destroy()
		defer Remove(session, inst.Addr())

		key := fmt.Sprintf("key%d", x)
		val := fmt.Sprintf("val%d", x)

		tags := map[string]string{key: val}

		members = append(members, Member{Address: inst.Addr(), Tags: tags})
	}

	var err error

	strategy := utils.AttemptStrategy{Total: time.Second * 30, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, members...)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers := make([]Member, len(members))
	for x, m := range members {
		// Ids should start at 1 (for the root) and go up
		m.Id = x + 1
		expectedMembers[x] = m
	}

	var cfg *Config
	attempt = strategy.Start()
	for attempt.Next() {
		cfg, err = CurrentConfig(session)
		if err == nil || !attempt.HasNext() {
			break
		}
	}

	c.Assert(err, gc.IsNil)
	c.Assert(cfg.Name, gc.Equals, name)

	// 2 since we already changed it once
	c.Assert(cfg.Version, gc.Equals, 2)

	mems := cfg.Members

	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// Now remove the last two Members
	attempt = strategy.Start()
	for attempt.Next() {
		err = Remove(session, members[3].Address, members[4].Address)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers = expectedMembers[0:3]

	mems, err = CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)

	// now let's mix it up and set the new members to a mix of the previous
	// plus the new arbiter
	mems = []Member{members[3], mems[2], mems[0], members[4]}

	attempt = strategy.Start()
	for attempt.Next() {
		err = Set(session, mems)
		if err == nil || !attempt.HasNext() {
			break
		}
	}

	c.Assert(err, gc.IsNil)

	attempt = strategy.Start()
	for attempt.Next() {
		// can dial whichever replica address here, mongo will figure it out
		session = instances[0].MustDialDirect()
		err = session.Ping()
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expectedMembers = []Member{members[3], expectedMembers[2], expectedMembers[0], members[4]}

	// any new members will get an id of max(other_ids...)+1
	expectedMembers[0].Id = 4
	expectedMembers[3].Id = 5

	attempt = strategy.Start()
	for attempt.Next() {
		mems, err = CurrentMembers(session)
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)
	c.Assert(mems, gc.DeepEquals, expectedMembers)
}

func (s *MongoSuite) TestIsMaster(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	expected := IsMasterResults{
		// The following fields hold information about the specific mongodb node.
		IsMaster:  true,
		Secondary: false,
		Arbiter:   false,
		Address:   root.Addr(),
		LocalTime: time.Time{},

		// The following fields hold information about the replica set.
		ReplicaSetName: name,
		Addresses:      []string{root.Addr()},
		Arbiters:       nil,
		PrimaryAddress: root.Addr(),
	}

	res, err := IsMaster(session)
	c.Assert(err, gc.IsNil)
	c.Check(closeEnough(res.LocalTime, time.Now()), gc.Equals, true)
	res.LocalTime = time.Time{}
	c.Check(*res, gc.DeepEquals, expected)
}

func (s *MongoSuite) TestCurrentStatus(c *gc.C) {
	session := root.MustDial()
	defer session.Close()

	inst1, err := newServer()
	c.Assert(err, gc.IsNil)
	defer inst1.Destroy()
	defer Remove(session, inst1.Addr())

	inst2, err := newServer()
	c.Assert(err, gc.IsNil)
	defer inst2.Destroy()
	defer Remove(session, inst2.Addr())

	strategy := utils.AttemptStrategy{Total: time.Second * 30, Delay: time.Millisecond * 100}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, Member{Address: inst1.Addr()}, Member{Address: inst2.Addr()})
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expected := &Status{
		Name: name,
		Members: []MemberStatus{{
			Id:      1,
			Address: root.Addr(),
			Self:    true,
			ErrMsg:  "",
			Healthy: true,
			State:   PrimaryState,
		}, {
			Id:      2,
			Address: inst1.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}, {
			Id:      3,
			Address: inst2.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}},
	}

	strategy.Total = time.Second * 60
	attempt = strategy.Start()
	var res *Status
	for attempt.Next() {
		var err error
		res, err = CurrentStatus(session)
		if err != nil {
			if !attempt.HasNext() {
				c.Errorf("Couldn't get status before timeout, got err: %v", err)
				return
			} else {
				// try again
				continue
			}
		}

		if res.Members[0].State == PrimaryState &&
			res.Members[1].State == SecondaryState &&
			res.Members[2].State == SecondaryState {
			break
		}
		if !attempt.HasNext() {
			c.Errorf("Servers did not get into final state before timeout.  Status: %#v", res)
			return
		}
	}

	for x, _ := range res.Members {
		// non-empty uptime and ping
		c.Check(res.Members[x].Uptime, gc.Not(gc.Equals), 0)

		// ping is always going to be zero since we're on localhost
		// so we can't really test it right now

		// now overwrite Uptime so it won't throw off DeepEquals
		res.Members[x].Uptime = 0
	}
	c.Check(res, gc.DeepEquals, expected)
}

func closeEnough(expected, obtained time.Time) bool {
	t := obtained.Sub(expected)
	return (-500*time.Millisecond) < t && t < (500*time.Millisecond)
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package e2e

import (
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"syscall"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
	"github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric/cmd/common/signer"
	"github.com/hyperledger/fabric/common/policydsl"
	"github.com/hyperledger/fabric/integration/nwo"
	"github.com/hyperledger/fabric/integration/nwo/commands"
	"github.com/hyperledger/fabric/integration/ordererclient"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
)

var _ = Describe("InstantiationPolicy", func() {
	var (
		testDir string
		client  *docker.Client
		network *nwo.Network
		process ifrit.Process
	)

	BeforeEach(func() {
		var err error
		testDir, err = ioutil.TempDir("", "e2e")
		Expect(err).NotTo(HaveOccurred())

		client, err = docker.NewClientFromEnv()
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		if process != nil {
			process.Signal(syscall.SIGTERM)
			Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
		}
		if network != nil {
			network.Cleanup()
		}
		os.RemoveAll(testDir)
	})

	Describe("single node etcdraft network with single peer", func() {
		BeforeEach(func() {
			config := nwo.MinimalRaft()
			config.Profiles[1].Organizations = []string{"Org1", "Org2"}
			network = nwo.New(config, testDir, client, StartPort(), components)
			network.GenerateConfigTree()
			network.Bootstrap()

			networkRunner := network.NetworkGroupRunner()
			process = ifrit.Invoke(networkRunner)
			Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
		})

		It("honors the instantiation policy", func() {
			orderer := network.Orderer("orderer")

			org1Peer := network.Peer("Org1", "peer0")
			org2Peer := network.Peer("Org2", "peer0")

			network.CreateAndJoinChannel(orderer, "testchannel")

			By("attempting to deploy with an unsatisfied instantiation policy")

			goodDeploy := LSCCOperation{
				Operation: "deploy",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "badip",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org2"),
				},
			}

			ordererclient.Broadcast(network, orderer, goodDeploy.Tx(PeerSigner(network, org1Peer)))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 2, org1Peer)

			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: badip"))

			By("attempting to deploy with a satisfied instantiation policy")

			badDeploy := LSCCOperation{
				Operation: "deploy",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "goodip",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org1"),
				},
			}

			ordererclient.Broadcast(network, orderer, badDeploy.Tx(PeerSigner(network, org1Peer)))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 3, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: goodip"))

			By("upgrading without satisfying the previous instantiation policy")

			badUpgrade := LSCCOperation{
				Operation: "upgrade",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "wrongsubmitter",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org2"),
				},
			}

			ordererclient.Broadcast(network, orderer, badUpgrade.Tx(PeerSigner(network, org2Peer)))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 4, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: wrongsubmitter"))

			By("upgrading while satisfying the previous instantiation policy")

			goodUpgrade := LSCCOperation{
				Operation: "upgrade",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "rightsubmitter",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org1"),
				},
			}

			ordererclient.Broadcast(network, orderer, goodUpgrade.Tx(PeerSigner(network, org1Peer)))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 5, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: rightsubmitter"))
		})
	})
})

func PeerSigner(n *nwo.Network, p *nwo.Peer) *signer.Signer {
	conf := signer.Config{
		MSPID:        n.Organization(p.Organization).MSPID,
		IdentityPath: n.PeerUserCert(p, "Admin"),
		KeyPath:      n.PeerUserKey(p, "Admin"),
	}

	signer, err := signer.NewSigner(conf)
	Expect(err).NotTo(HaveOccurred())

	return signer
}

func MarshalOrPanic(msg proto.Message) []byte {
	b, err := proto.Marshal(msg)
	if err != nil {
		panic(err)
	}
	return b
}

func ListInstantiatedLegacy(n *nwo.Network, p *nwo.Peer, channel string) *gbytes.Buffer {
	sess, err := n.PeerAdminSession(p, commands.ChaincodeListInstantiatedLegacy{
		ChannelID:  channel,
		ClientAuth: n.ClientAuthRequired,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	return sess.Buffer()
}

type LSCCOperation struct {
	Operation         string
	ChannelID         string
	Name              string
	Version           string
	InstantiationOrgs []*nwo.Organization
}

func (lo *LSCCOperation) Tx(signer *signer.Signer) *common.Envelope {
	creatorBytes, err := signer.Serialize()
	Expect(err).NotTo(HaveOccurred())

	var instantiationMSPIDs []string
	for _, org := range lo.InstantiationOrgs {
		instantiationMSPIDs = append(instantiationMSPIDs, org.MSPID)
	}
	instantiationPolicy := policydsl.SignedByAnyMember(instantiationMSPIDs)

	nonce, err := time.Now().MarshalBinary()
	Expect(err).NotTo(HaveOccurred())

	timestamp, err := ptypes.TimestampProto(time.Now().UTC())
	Expect(err).NotTo(HaveOccurred())

	hasher := sha256.New()
	hasher.Write(nonce)
	hasher.Write(creatorBytes)
	txid := hex.EncodeToString(hasher.Sum(nil))

	signatureHeaderBytes := MarshalOrPanic(&common.SignatureHeader{
		Creator: creatorBytes,
		Nonce:   nonce,
	})

	channelHeaderBytes := MarshalOrPanic(&common.ChannelHeader{
		ChannelId: lo.ChannelID,
		Extension: MarshalOrPanic(&peer.ChaincodeHeaderExtension{
			ChaincodeId: &peer.ChaincodeID{
				Name: "lscc",
			},
		}),
		Timestamp: timestamp,
		TxId:      txid,
		Type:      int32(common.HeaderType_ENDORSER_TRANSACTION),
	})

	chaincodeDataBytes := MarshalOrPanic(&peer.ChaincodeData{
		Data:                []byte("a-big-bunch-of-fakery"),
		Id:                  []byte("a-friendly-fake-chaincode"),
		Escc:                "escc",
		Vscc:                "vscc",
		Name:                lo.Name,
		Version:             lo.Version,
		InstantiationPolicy: instantiationPolicy,
		Policy:              nil, // EndorsementPolicy deliberately left nil
	})

	proposalPayloadBytes := MarshalOrPanic(&peer.ChaincodeProposalPayload{
		Input: MarshalOrPanic(&peer.ChaincodeInvocationSpec{
			ChaincodeSpec: &peer.ChaincodeSpec{
				Input: &peer.ChaincodeInput{
					Args: [][]byte{
						[]byte(lo.Operation),
						[]byte(lo.ChannelID),
						MarshalOrPanic(&peer.ChaincodeDeploymentSpec{
							ChaincodeSpec: &peer.ChaincodeSpec{
								ChaincodeId: &peer.ChaincodeID{
									Name:    lo.Name,
									Version: lo.Version,
								},
								Input: &peer.ChaincodeInput{
									Args: [][]byte{[]byte("bogus-init-arg")},
								},
								Type: peer.ChaincodeSpec_GOLANG,
							},
						}),
						{}, // Endorsement policy bytes deliberately empty
						[]byte("escc"),
						[]byte("vscc"),
					},
				},
				Type: peer.ChaincodeSpec_GOLANG,
			},
		}),
	})

	propHash := sha256.New()
	propHash.Write(channelHeaderBytes)
	propHash.Write(signatureHeaderBytes)
	propHash.Write(proposalPayloadBytes)
	proposalHash := propHash.Sum(nil)[:]

	proposalResponsePayloadBytes := MarshalOrPanic(&peer.ProposalResponsePayload{
		ProposalHash: proposalHash,
		Extension: MarshalOrPanic(&peer.ChaincodeAction{
			ChaincodeId: &peer.ChaincodeID{
				Name:    "lscc",
				Version: "syscc",
			},
			Events: MarshalOrPanic(&peer.ChaincodeEvent{
				ChaincodeId: "lscc",
				EventName:   lo.Operation,
				Payload: MarshalOrPanic(&peer.LifecycleEvent{
					ChaincodeName: lo.Name,
				}),
			}),
			Response: &peer.Response{
				Payload: chaincodeDataBytes,
				Status:  200,
			},
			Results: MarshalOrPanic(&rwset.TxReadWriteSet{
				DataModel: rwset.TxReadWriteSet_KV,
				NsRwset: []*rwset.NsReadWriteSet{
					{
						Namespace: "lscc",
						Rwset: MarshalOrPanic(&kvrwset.KVRWSet{
							Writes: []*kvrwset.KVWrite{
								{
									Key:   lo.Name,
									Value: chaincodeDataBytes,
								},
							},
						}),
					},
					{
						Namespace: lo.Name,
						Rwset: MarshalOrPanic(&kvrwset.KVRWSet{
							Writes: []*kvrwset.KVWrite{
								{
									Key:   "bogus-key",
									Value: []byte("bogus-value"),
								},
							},
						}),
					},
				},
			}),
		}),
	})

	endorsementSignature, err := signer.Sign(append(proposalResponsePayloadBytes, creatorBytes...))
	Expect(err).NotTo(HaveOccurred())

	payloadBytes := MarshalOrPanic(&common.Payload{
		Header: &common.Header{
			ChannelHeader:   channelHeaderBytes,
			SignatureHeader: signatureHeaderBytes,
		},
		Data: MarshalOrPanic(&peer.Transaction{
			Actions: []*peer.TransactionAction{
				{
					Header: signatureHeaderBytes,
					Payload: MarshalOrPanic(&peer.ChaincodeActionPayload{
						ChaincodeProposalPayload: proposalPayloadBytes,
						Action: &peer.ChaincodeEndorsedAction{
							ProposalResponsePayload: proposalResponsePayloadBytes,
							Endorsements: []*peer.Endorsement{
								{
									Endorser:  creatorBytes,
									Signature: endorsementSignature,
								},
							},
						},
					}),
				},
			},
		}),
	})

	envSignature, err := signer.Sign(payloadBytes)
	Expect(err).NotTo(HaveOccurred())

	return &common.Envelope{
		Payload:   payloadBytes,
		Signature: envSignature,
	}
}
<commit_msg>e2e: replace common/signer with nwo.SigningIdentity<commit_after>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package e2e

import (
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"syscall"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
	"github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric/common/policydsl"
	"github.com/hyperledger/fabric/integration/nwo"
	"github.com/hyperledger/fabric/integration/nwo/commands"
	"github.com/hyperledger/fabric/integration/ordererclient"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/ifrit"
)

var _ = Describe("InstantiationPolicy", func() {
	var (
		testDir string
		client  *docker.Client
		network *nwo.Network
		process ifrit.Process
	)

	BeforeEach(func() {
		var err error
		testDir, err = ioutil.TempDir("", "e2e")
		Expect(err).NotTo(HaveOccurred())

		client, err = docker.NewClientFromEnv()
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		if process != nil {
			process.Signal(syscall.SIGTERM)
			Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
		}
		if network != nil {
			network.Cleanup()
		}
		os.RemoveAll(testDir)
	})

	Describe("single node etcdraft network with single peer", func() {
		BeforeEach(func() {
			config := nwo.MinimalRaft()
			config.Profiles[1].Organizations = []string{"Org1", "Org2"}
			network = nwo.New(config, testDir, client, StartPort(), components)
			network.GenerateConfigTree()
			network.Bootstrap()

			networkRunner := network.NetworkGroupRunner()
			process = ifrit.Invoke(networkRunner)
			Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
		})

		It("honors the instantiation policy", func() {
			orderer := network.Orderer("orderer")

			org1Peer := network.Peer("Org1", "peer0")
			org2Peer := network.Peer("Org2", "peer0")

			network.CreateAndJoinChannel(orderer, "testchannel")

			By("attempting to deploy with an unsatisfied instantiation policy")

			goodDeploy := LSCCOperation{
				Operation: "deploy",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "badip",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org2"),
				},
			}

			ordererclient.Broadcast(network, orderer, goodDeploy.Tx(network.PeerUserSigner(org1Peer, "Admin")))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 2, org1Peer)

			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: badip"))

			By("attempting to deploy with a satisfied instantiation policy")

			badDeploy := LSCCOperation{
				Operation: "deploy",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "goodip",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org1"),
				},
			}

			ordererclient.Broadcast(network, orderer, badDeploy.Tx(network.PeerUserSigner(org1Peer, "Admin")))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 3, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: goodip"))

			By("upgrading without satisfying the previous instantiation policy")

			badUpgrade := LSCCOperation{
				Operation: "upgrade",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "wrongsubmitter",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org2"),
				},
			}

			ordererclient.Broadcast(network, orderer, badUpgrade.Tx(network.PeerUserSigner(org2Peer, "Admin")))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 4, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).NotTo(gbytes.Say("Name: fakecc, Version: wrongsubmitter"))

			By("upgrading while satisfying the previous instantiation policy")

			goodUpgrade := LSCCOperation{
				Operation: "upgrade",
				ChannelID: "testchannel",
				Name:      "fakecc",
				Version:   "rightsubmitter",
				InstantiationOrgs: []*nwo.Organization{
					network.Organization("Org1"),
				},
			}

			ordererclient.Broadcast(network, orderer, goodUpgrade.Tx(network.PeerUserSigner(org1Peer, "Admin")))

			nwo.WaitUntilEqualLedgerHeight(network, "testchannel", 5, org1Peer)
			Expect(ListInstantiatedLegacy(network, org1Peer, "testchannel")).To(gbytes.Say("Name: fakecc, Version: rightsubmitter"))
		})
	})
})

func ListInstantiatedLegacy(n *nwo.Network, p *nwo.Peer, channel string) *gbytes.Buffer {
	sess, err := n.PeerAdminSession(p, commands.ChaincodeListInstantiatedLegacy{
		ChannelID:  channel,
		ClientAuth: n.ClientAuthRequired,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	return sess.Buffer()
}

type LSCCOperation struct {
	Operation         string
	ChannelID         string
	Name              string
	Version           string
	InstantiationOrgs []*nwo.Organization
}

func (lo *LSCCOperation) Tx(signer *nwo.SigningIdentity) *common.Envelope {
	creatorBytes, err := signer.Serialize()
	Expect(err).NotTo(HaveOccurred())

	var instantiationMSPIDs []string
	for _, org := range lo.InstantiationOrgs {
		instantiationMSPIDs = append(instantiationMSPIDs, org.MSPID)
	}
	instantiationPolicy := policydsl.SignedByAnyMember(instantiationMSPIDs)

	nonce, err := time.Now().MarshalBinary()
	Expect(err).NotTo(HaveOccurred())

	timestamp, err := ptypes.TimestampProto(time.Now().UTC())
	Expect(err).NotTo(HaveOccurred())

	hasher := sha256.New()
	hasher.Write(nonce)
	hasher.Write(creatorBytes)
	txid := hex.EncodeToString(hasher.Sum(nil))

	signatureHeaderBytes := protoMarshal(&common.SignatureHeader{
		Creator: creatorBytes,
		Nonce:   nonce,
	})

	channelHeaderBytes := protoMarshal(&common.ChannelHeader{
		ChannelId: lo.ChannelID,
		Extension: protoMarshal(&peer.ChaincodeHeaderExtension{
			ChaincodeId: &peer.ChaincodeID{
				Name: "lscc",
			},
		}),
		Timestamp: timestamp,
		TxId:      txid,
		Type:      int32(common.HeaderType_ENDORSER_TRANSACTION),
	})

	chaincodeDataBytes := protoMarshal(&peer.ChaincodeData{
		Data:                []byte("a-big-bunch-of-fakery"),
		Id:                  []byte("a-friendly-fake-chaincode"),
		Escc:                "escc",
		Vscc:                "vscc",
		Name:                lo.Name,
		Version:             lo.Version,
		InstantiationPolicy: instantiationPolicy,
		Policy:              nil, // EndorsementPolicy deliberately left nil
	})

	proposalPayloadBytes := protoMarshal(&peer.ChaincodeProposalPayload{
		Input: protoMarshal(&peer.ChaincodeInvocationSpec{
			ChaincodeSpec: &peer.ChaincodeSpec{
				Input: &peer.ChaincodeInput{
					Args: [][]byte{
						[]byte(lo.Operation),
						[]byte(lo.ChannelID),
						protoMarshal(&peer.ChaincodeDeploymentSpec{
							ChaincodeSpec: &peer.ChaincodeSpec{
								ChaincodeId: &peer.ChaincodeID{
									Name:    lo.Name,
									Version: lo.Version,
								},
								Input: &peer.ChaincodeInput{
									Args: [][]byte{[]byte("bogus-init-arg")},
								},
								Type: peer.ChaincodeSpec_GOLANG,
							},
						}),
						{}, // Endorsement policy bytes deliberately empty
						[]byte("escc"),
						[]byte("vscc"),
					},
				},
				Type: peer.ChaincodeSpec_GOLANG,
			},
		}),
	})

	propHash := sha256.New()
	propHash.Write(channelHeaderBytes)
	propHash.Write(signatureHeaderBytes)
	propHash.Write(proposalPayloadBytes)
	proposalHash := propHash.Sum(nil)[:]

	proposalResponsePayloadBytes := protoMarshal(&peer.ProposalResponsePayload{
		ProposalHash: proposalHash,
		Extension: protoMarshal(&peer.ChaincodeAction{
			ChaincodeId: &peer.ChaincodeID{
				Name:    "lscc",
				Version: "syscc",
			},
			Events: protoMarshal(&peer.ChaincodeEvent{
				ChaincodeId: "lscc",
				EventName:   lo.Operation,
				Payload: protoMarshal(&peer.LifecycleEvent{
					ChaincodeName: lo.Name,
				}),
			}),
			Response: &peer.Response{
				Payload: chaincodeDataBytes,
				Status:  200,
			},
			Results: protoMarshal(&rwset.TxReadWriteSet{
				DataModel: rwset.TxReadWriteSet_KV,
				NsRwset: []*rwset.NsReadWriteSet{
					{
						Namespace: "lscc",
						Rwset: protoMarshal(&kvrwset.KVRWSet{
							Writes: []*kvrwset.KVWrite{
								{
									Key:   lo.Name,
									Value: chaincodeDataBytes,
								},
							},
						}),
					},
					{
						Namespace: lo.Name,
						Rwset: protoMarshal(&kvrwset.KVRWSet{
							Writes: []*kvrwset.KVWrite{
								{
									Key:   "bogus-key",
									Value: []byte("bogus-value"),
								},
							},
						}),
					},
				},
			}),
		}),
	})

	endorsementSignature, err := signer.Sign(append(proposalResponsePayloadBytes, creatorBytes...))
	Expect(err).NotTo(HaveOccurred())

	payloadBytes := protoMarshal(&common.Payload{
		Header: &common.Header{
			ChannelHeader:   channelHeaderBytes,
			SignatureHeader: signatureHeaderBytes,
		},
		Data: protoMarshal(&peer.Transaction{
			Actions: []*peer.TransactionAction{
				{
					Header: signatureHeaderBytes,
					Payload: protoMarshal(&peer.ChaincodeActionPayload{
						ChaincodeProposalPayload: proposalPayloadBytes,
						Action: &peer.ChaincodeEndorsedAction{
							ProposalResponsePayload: proposalResponsePayloadBytes,
							Endorsements: []*peer.Endorsement{
								{
									Endorser:  creatorBytes,
									Signature: endorsementSignature,
								},
							},
						},
					}),
				},
			},
		}),
	})

	envSignature, err := signer.Sign(payloadBytes)
	Expect(err).NotTo(HaveOccurred())

	return &common.Envelope{
		Payload:   payloadBytes,
		Signature: envSignature,
	}
}

func protoMarshal(msg proto.Message) []byte {
	b, err := proto.Marshal(msg)
	Expect(err).NotTo(HaveOccurred())
	return b
}
<|endoftext|>


<commit_before>package ui

import (
	"encoding/json"
	"image"
	"math"
	"strings"
	"sync"
	"time"

	"github.com/ninjasphere/go-gestic"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"

	"github.com/ninjasphere/go-ninja/logger"
)

// How long after the last airwheel before we hide the volume display
const volumeModeReset = time.Second
const ignoreTap = time.Millisecond * 300

const volumeRate = 2 // Max number of volume calls per second

type MediaPane struct {
	log  *logger.Logger
	conn *ninja.Connection

	volumeMode      bool
	volumeModeReset *time.Timer

	lastAirWheelTime time.Time
	lastAirWheel     *uint8

	volume         float64
	volumeImage    *Image
	muteImage      *Image
	lastVolumeTime time.Time
	lastSentVolume float64

	ignoringTap    bool
	ignoreTapTimer *time.Timer
	playingState   string
	playImage      *Image
	pauseImage     *Image
	stopImage      *Image
	nextImage      *Image

	gestureSync *sync.Mutex

	controlDevices []*ninja.ServiceClient
	volumeDevices  []*ninja.ServiceClient
}

type MediaPaneImages struct {
	Volume string
	Mute   string
	Play   string
	Pause  string
	Stop   string
	Next   string
}

func NewMediaPane(images *MediaPaneImages, conn *ninja.Connection) *MediaPane {
	log := logger.GetLogger("MediaPane")

	controlDevices, err := getChannelServices("mediaplayer", "media-control", conn)
	if err != nil {
		log.Fatalf("Failed to get media-control devices: %s", err)
	}
	log.Infof("Pane got %d media-control devices", len(controlDevices))

	if len(controlDevices) > 1 {
		log.Infof("WARNING... MORE THAN ONE MEDIA CONTROL DEVICE.... IT WILL ACT WEIRD")
	}

	volumeDevices, err := getChannelServices("mediaplayer", "volume", conn)
	if err != nil {
		log.Fatalf("Failed to get volume devices: %s", err)
	}
	log.Infof("Pane got %d volume devices", len(volumeDevices))

	pane := &MediaPane{
		log:            log,
		volumeDevices:  volumeDevices,
		controlDevices: controlDevices,
		conn:           conn,
		gestureSync:    &sync.Mutex{},

		volumeImage: loadImage(images.Volume),
		muteImage:   loadImage(images.Mute),
		playImage:   loadImage(images.Play),
		pauseImage:  loadImage(images.Pause),
		stopImage:   loadImage(images.Stop),
		nextImage:   loadImage(images.Next),

		playingState: "stopped",

		lastVolumeTime: time.Now(),
		//lastAirWheelTime: time.Now(),
	}

	e := func(state string) func(params *json.RawMessage, values map[string]string) bool {
		return func(params *json.RawMessage, values map[string]string) bool {
			if !pane.ignoringTap {
				pane.log.Infof("Received control event. Setting playing state to %s", state)
				pane.playingState = state
			}
			return true
		}
	}

	for _, device := range controlDevices {
		device.OnEvent("playing", e("playing"))
		device.OnEvent("buffering", e("playing"))
		device.OnEvent("paused", e("paused"))
		device.OnEvent("stopped", e("stopped"))
	}

	for _, device := range volumeDevices {
		device.OnEvent("state", func(params *json.RawMessage, values map[string]string) bool {
			if time.Since(pane.lastVolumeTime) > time.Millisecond*300 {

				var volume channels.VolumeState
				err := json.Unmarshal(*params, &volume)
				if err != nil {
					pane.log.Infof("Failed to unmarshal volume from %s error:%s", *params, err)
				}
				pane.volume = *volume.Volume
			}
			return true
		})
	}

	pane.volumeModeReset = time.AfterFunc(0, func() {
		pane.volumeMode = false
	})

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	return pane
}

func (p *MediaPane) Gesture(gesture *gestic.GestureData) {
	p.gestureSync.Lock()
	defer p.gestureSync.Unlock()

	if gesture.AirWheel.AirWheelVal > 0 {

		p.volumeMode = true
		p.volumeModeReset.Reset(volumeModeReset)

		if time.Since(p.lastAirWheelTime) > time.Millisecond*300 {
			p.lastAirWheel = nil
		}

		p.lastAirWheelTime = time.Now()

		//p.log.Debugf("Airwheel: %d", gesture.AirWheel.AirWheelVal)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.AirWheelVal) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			//p.log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.AirWheelVal, offset, *p.lastAirWheel)

			//p.log.Debugf("Current volume %f", p.volume)

			//p.log.Debugf("Volume offset %f", float64(offset)/255.0)

			var volume float64 = p.volume + float64(offset)/255.0

			volume = math.Max(volume, 0)
			volume = math.Min(volume, 1)

			p.log.Debugf("Adjusted volume %f:", volume)

			p.volume = volume

			if p.lastSentVolume != volume {
				if time.Since(p.lastVolumeTime) < time.Millisecond*500 {
					p.log.Debugf("Volume rate limited")
				} else {
					p.lastVolumeTime = time.Now()
					p.log.Debugf("Volume NOT rate limited")
					p.SendVolume()
				}
			}
		}

		val := gesture.AirWheel.AirWheelVal
		p.lastAirWheel = &val
		//spew.Dump("last2", p.lastAirWheel)
	}

	if !p.ignoringTap && strings.Contains(gesture.Touch.Name(), "Tap") {
		p.log.Infof("Tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(ignoreTap)

		switch p.playingState {
		case "stopped":
			p.SetControlState("playing")
		case "playing":
			p.SetControlState("paused")
		case "paused":
			p.SetControlState("playing")
		}

	}

}

func (p *MediaPane) SetControlState(state string) {

	p.log.Debugf("Setting playing state %s:", state)

	var method = ""
	switch state {
	case "stopped":
		method = "stop"
	case "playing":
		method = "play"
	case "paused":
		method = "pause"
	}

	for _, device := range p.controlDevices {
		device.Call(method, []interface{}{}, nil, time.Second)
	}
	p.playingState = state
}

func (p *MediaPane) SendVolume() {
	p.log.Debugf("New volume %f:", p.volume)
	//	p.volume = volume

	p.lastSentVolume = p.volume
	for _, device := range p.volumeDevices {
		go device.Call("set", []interface{}{p.volume}, nil, time.Second)
	}
	//p.onStateChange(state)
}

func (p *MediaPane) Render() (*image.RGBA, error) {

	if p.volumeMode {
		if p.volume > 0 {
			return p.volumeImage.GetPositionFrame(1 - p.volume), nil
		}

		return p.muteImage.GetNextFrame(), nil
	}

	switch p.playingState {
	case "stopped":
		return p.stopImage.GetNextFrame(), nil
	case "playing":
		return p.playImage.GetNextFrame(), nil
	case "paused":
		return p.pauseImage.GetNextFrame(), nil
	}

	return p.stopImage.GetNextFrame(), nil
}

func (p *MediaPane) IsDirty() bool {
	return true
}
<commit_msg>Update volumeState.Volume->volumeState.Level<commit_after>package ui

import (
	"encoding/json"
	"image"
	"math"
	"strings"
	"sync"
	"time"

	"github.com/ninjasphere/go-gestic"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"

	"github.com/ninjasphere/go-ninja/logger"
)

// How long after the last airwheel before we hide the volume display
const volumeModeReset = time.Second
const ignoreTap = time.Millisecond * 300

const volumeRate = 2 // Max number of volume calls per second

type MediaPane struct {
	log  *logger.Logger
	conn *ninja.Connection

	volumeMode      bool
	volumeModeReset *time.Timer

	lastAirWheelTime time.Time
	lastAirWheel     *uint8

	volume         float64
	volumeImage    *Image
	muteImage      *Image
	lastVolumeTime time.Time
	lastSentVolume float64

	ignoringTap    bool
	ignoreTapTimer *time.Timer
	playingState   string
	playImage      *Image
	pauseImage     *Image
	stopImage      *Image
	nextImage      *Image

	gestureSync *sync.Mutex

	controlDevices []*ninja.ServiceClient
	volumeDevices  []*ninja.ServiceClient
}

type MediaPaneImages struct {
	Volume string
	Mute   string
	Play   string
	Pause  string
	Stop   string
	Next   string
}

func NewMediaPane(images *MediaPaneImages, conn *ninja.Connection) *MediaPane {
	log := logger.GetLogger("MediaPane")

	controlDevices, err := getChannelServices("mediaplayer", "media-control", conn)
	if err != nil {
		log.Fatalf("Failed to get media-control devices: %s", err)
	}
	log.Infof("Pane got %d media-control devices", len(controlDevices))

	if len(controlDevices) > 1 {
		log.Infof("WARNING... MORE THAN ONE MEDIA CONTROL DEVICE.... IT WILL ACT WEIRD")
	}

	volumeDevices, err := getChannelServices("mediaplayer", "volume", conn)
	if err != nil {
		log.Fatalf("Failed to get volume devices: %s", err)
	}
	log.Infof("Pane got %d volume devices", len(volumeDevices))

	pane := &MediaPane{
		log:            log,
		volumeDevices:  volumeDevices,
		controlDevices: controlDevices,
		conn:           conn,
		gestureSync:    &sync.Mutex{},

		volumeImage: loadImage(images.Volume),
		muteImage:   loadImage(images.Mute),
		playImage:   loadImage(images.Play),
		pauseImage:  loadImage(images.Pause),
		stopImage:   loadImage(images.Stop),
		nextImage:   loadImage(images.Next),

		playingState: "stopped",

		lastVolumeTime: time.Now(),
		//lastAirWheelTime: time.Now(),
	}

	e := func(state string) func(params *json.RawMessage, values map[string]string) bool {
		return func(params *json.RawMessage, values map[string]string) bool {
			if !pane.ignoringTap {
				pane.log.Infof("Received control event. Setting playing state to %s", state)
				pane.playingState = state
			}
			return true
		}
	}

	for _, device := range controlDevices {
		device.OnEvent("playing", e("playing"))
		device.OnEvent("buffering", e("playing"))
		device.OnEvent("paused", e("paused"))
		device.OnEvent("stopped", e("stopped"))
	}

	for _, device := range volumeDevices {
		device.OnEvent("state", func(params *json.RawMessage, values map[string]string) bool {
			if time.Since(pane.lastVolumeTime) > time.Millisecond*300 {

				var volume channels.VolumeState
				err := json.Unmarshal(*params, &volume)
				if err != nil {
					pane.log.Infof("Failed to unmarshal volume from %s error:%s", *params, err)
				}
				pane.volume = *volume.Level
			}
			return true
		})
	}

	pane.volumeModeReset = time.AfterFunc(0, func() {
		pane.volumeMode = false
	})

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	return pane
}

func (p *MediaPane) Gesture(gesture *gestic.GestureData) {
	p.gestureSync.Lock()
	defer p.gestureSync.Unlock()

	if gesture.AirWheel.AirWheelVal > 0 {

		p.volumeMode = true
		p.volumeModeReset.Reset(volumeModeReset)

		if time.Since(p.lastAirWheelTime) > time.Millisecond*300 {
			p.lastAirWheel = nil
		}

		p.lastAirWheelTime = time.Now()

		//p.log.Debugf("Airwheel: %d", gesture.AirWheel.AirWheelVal)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.AirWheelVal) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			//p.log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.AirWheelVal, offset, *p.lastAirWheel)

			//p.log.Debugf("Current volume %f", p.volume)

			//p.log.Debugf("Volume offset %f", float64(offset)/255.0)

			var volume float64 = p.volume + float64(offset)/255.0

			volume = math.Max(volume, 0)
			volume = math.Min(volume, 1)

			p.log.Debugf("Adjusted volume %f:", volume)

			p.volume = volume

			if p.lastSentVolume != volume {
				if time.Since(p.lastVolumeTime) < time.Millisecond*500 {
					p.log.Debugf("Volume rate limited")
				} else {
					p.lastVolumeTime = time.Now()
					p.log.Debugf("Volume NOT rate limited")
					p.SendVolume()
				}
			}
		}

		val := gesture.AirWheel.AirWheelVal
		p.lastAirWheel = &val
		//spew.Dump("last2", p.lastAirWheel)
	}

	if !p.ignoringTap && strings.Contains(gesture.Touch.Name(), "Tap") {
		p.log.Infof("Tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(ignoreTap)

		switch p.playingState {
		case "stopped":
			p.SetControlState("playing")
		case "playing":
			p.SetControlState("paused")
		case "paused":
			p.SetControlState("playing")
		}

	}

}

func (p *MediaPane) SetControlState(state string) {

	p.log.Debugf("Setting playing state %s:", state)

	var method = ""
	switch state {
	case "stopped":
		method = "stop"
	case "playing":
		method = "play"
	case "paused":
		method = "pause"
	}

	for _, device := range p.controlDevices {
		device.Call(method, []interface{}{}, nil, time.Second)
	}
	p.playingState = state
}

func (p *MediaPane) SendVolume() {
	p.log.Debugf("New volume %f:", p.volume)
	//	p.volume = volume

	p.lastSentVolume = p.volume
	for _, device := range p.volumeDevices {
		go device.Call("set", []interface{}{p.volume}, nil, time.Second)
	}
	//p.onStateChange(state)
}

func (p *MediaPane) Render() (*image.RGBA, error) {

	if p.volumeMode {
		if p.volume > 0 {
			return p.volumeImage.GetPositionFrame(1 - p.volume), nil
		}

		return p.muteImage.GetNextFrame(), nil
	}

	switch p.playingState {
	case "stopped":
		return p.stopImage.GetNextFrame(), nil
	case "playing":
		return p.playImage.GetNextFrame(), nil
	case "paused":
		return p.pauseImage.GetNextFrame(), nil
	}

	return p.stopImage.GetNextFrame(), nil
}

func (p *MediaPane) IsDirty() bool {
	return true
}
<|endoftext|>


<commit_before>package ui

import(
	"fmt"
	"html/template"
	"time"

	"github.com/skypies/geo"
	fdb "github.com/skypies/flightdb2"
	"github.com/skypies/util/date"
)

// MapShapes is a single thing that contains all the things we want to render on a map
type MapShapes struct {
	Circles []MapCircle
	Lines []MapLine
	Points []MapPoint
}
func NewMapShapes() *MapShapes {
	ms := MapShapes{
		Circles: []MapCircle{},
		Lines: []MapLine{},
		Points: []MapPoint{},
	}
	return &ms
}
func (ms1 *MapShapes)Add(ms2 *MapShapes) {
	ms1.Circles = append(ms1.Circles, ms2.Circles...)
	ms1.Lines   = append(ms1.Lines,   ms2.Lines...)
	ms1.Points  = append(ms1.Points,  ms2.Points...)
}
func (ms1 *MapShapes)AddLine(ml MapLine) { ms1.Lines = append(ms1.Lines, ml) }
func (ms1 *MapShapes)AddPoint(mp MapPoint) { ms1.Points = append(ms1.Points, mp) }
func (ms1 *MapShapes)AddCircle(mc MapCircle) { ms1.Circles = append(ms1.Circles, mc) }

type MapCircle struct {
	C *geo.LatlongCircle
	Color string
}
func (mc MapCircle)ToJSStr(text string) string {
	color := mc.Color
	if color == "" { color = "#000000" }
	return fmt.Sprintf("center :{lat:%f, lng:%f}, radiusmeters: %.0f, color:%q",
		mc.C.Lat, mc.C.Long, mc.C.RadiusKM*1000.0, color)
}
func MapCirclesToJSVar(circles []MapCircle) template.JS {
	str := "{\n"
	for i,mc := range circles {
		str += fmt.Sprintf("    %d: {%s},\n", i, mc.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

type MapPoint struct {
	ITP   *fdb.InterpolatedTrackpoint
	TP    *fdb.Trackpoint
	Pos   *geo.Latlong

	Icon   string  // The <foo> in /static/dot-<foo>.png
	Text   string
}

func (mp MapPoint)ToJSStr(text string) string {
	if mp.Icon == "" { mp.Icon = "pink" }
	tp := fdb.Trackpoint{DataSource:"n/a"}

	if mp.ITP != nil {
		// Transform this into a tp with extra text
		tp = mp.ITP.Trackpoint
		tp.DataSource += "/interp"
		mp.Text = fmt.Sprintf("** Interpolated trackpoint\n"+
			" * Pre :%s\n * This:%s\n * Post:%s\n * Ratio: %.2f\n%s",
			mp.ITP.Pre, mp.ITP, mp.ITP.Post, mp.ITP.Ratio, mp.Text)
	} else if mp.TP != nil {
		tp = *mp.TP
		age := date.RoundDuration(time.Since(tp.TimestampUTC))
		times := fmt.Sprintf("%s (age:%s, epoch:%d)",
			date.InPdt(tp.TimestampUTC), age, tp.TimestampUTC.Unix())
		mp.Text = fmt.Sprintf("** %s\n* %s\n* DataSource: %s\n%s* %s",
			times, mp.TP, mp.TP.LongSource(), tp.AnalysisAnnotation, mp.Text)
		if tp.AnalysisMapIcon != "" {
			mp.Icon = tp.AnalysisMapIcon
			}
	} else {
		tp.Latlong = *mp.Pos
	}

	str := tp.ToJSString()
	str += fmt.Sprintf(", icon:%q, info:%q", mp.Icon, mp.Text)
	return str
}

type MapLine struct {
	Start geo.Latlong `json:"s"`
	End   geo.Latlong `json:"e"`

	Color        string  `json:"color"`    // A hex color value (e.g. "#ff8822")
	Opacity      float64 `json:"opacity"`
}

func (ml MapLine)ToJSStr(text string) string {
	color,op := ml.Color, ml.Opacity
	if color == "" { color = "#000000" }
	if op == 0.0 { op = 1.0 }
	return fmt.Sprintf("s:{lat:%f, lng:%f}, e:{lat:%f, lng:%f}, color:\"%s\", opacity:%.2f",
		ml.Start.Lat, ml.Start.Long, ml.End.Lat, ml.End.Long, color, op)
}

func MapPointsToJSVar(points []MapPoint) template.JS {
	str := "{\n"
	for i,mp := range points {
		str += fmt.Sprintf("    %d: {%s},\n", i, mp.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

func MapLinesToJSVar(lines []MapLine) template.JS {
	str := "{\n"
	for i,ml := range lines {
		str += fmt.Sprintf("    %d: {%s},\n", i, ml.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

func LatlongTimeBoxToMapLines(tb geo.LatlongTimeBox, color string) []MapLine {
	maplines := []MapLine{}
	for _,line := range tb.LatlongBox.ToLines() {
		mapline := MapLine{Start:line.From, End:line.To}
		mapline.Color = color
		maplines = append(maplines, mapline)
	}
	return maplines
}

// This is a kind of abuse of ColoringStrategy from colorscheme.go
func TrackToMapPoints(t *fdb.Track, icon, banner string, coloring ColoringStrategy) []MapPoint {
	sourceColors := map[string]string{
		"ADSB":  "yellow",
		"MLAT":  "red",
		"fr24":  "green",
		"FA:TA": "blue",
		"FA:TZ": "blue",
	}
	receiverColors := map[string]string{
		"ScottsValley":  "yellow",
		"NorthPi":       "pink",
		"LosAltosHills": "blue",
		"Saratoga":      "red",
	}

	points := []MapPoint{}
	if len(*t) == 0 { return points }

	for i,_ := range *t {
		color := icon
		tp := (*t)[i]
		if coloring == ByCandyStripe {
			if tp.ReceiverName == "NorthPi" {
				if color == "blue" { color = "red" } else { color = "green" }
			} else if tp.ReceiverName == "ScottsValley" {
				if color == "blue" { color = "pink" }
			}

		} else if icon == "" {
			if coloring == ByADSBReceiver {
				if c,exists := receiverColors[tp.ReceiverName]; exists {
					color = c
				} else {
					color = receiverColors["default"]
				}
				// Offset receivers, slightly
				//if tp.ReceiverName == "ScottsValley" {
				//	tp.Lat  += 0.0006
				//	tp.Long += 0.0006
				//}
			} else if coloring == ByData {
				if c,exists := sourceColors[tp.DataSource]; exists { color = c }
			}
		}

		pointText := fmt.Sprintf("Point %d/%d\n%s", i, len(*t), banner)

		points = append(points, MapPoint{
			TP: &tp, Icon:color,
			Text: pointText,
		})
	}
	return points
}
<commit_msg>Revamp trackpoint colors, for new receiver names<commit_after>package ui

import(
	"fmt"
	"html/template"
	"time"

	"github.com/skypies/geo"
	fdb "github.com/skypies/flightdb2"
	"github.com/skypies/util/date"
)

// MapShapes is a single thing that contains all the things we want to render on a map
type MapShapes struct {
	Circles []MapCircle
	Lines []MapLine
	Points []MapPoint
}
func NewMapShapes() *MapShapes {
	ms := MapShapes{
		Circles: []MapCircle{},
		Lines: []MapLine{},
		Points: []MapPoint{},
	}
	return &ms
}
func (ms1 *MapShapes)Add(ms2 *MapShapes) {
	ms1.Circles = append(ms1.Circles, ms2.Circles...)
	ms1.Lines   = append(ms1.Lines,   ms2.Lines...)
	ms1.Points  = append(ms1.Points,  ms2.Points...)
}
func (ms1 *MapShapes)AddLine(ml MapLine) { ms1.Lines = append(ms1.Lines, ml) }
func (ms1 *MapShapes)AddPoint(mp MapPoint) { ms1.Points = append(ms1.Points, mp) }
func (ms1 *MapShapes)AddCircle(mc MapCircle) { ms1.Circles = append(ms1.Circles, mc) }

type MapCircle struct {
	C *geo.LatlongCircle
	Color string
}
func (mc MapCircle)ToJSStr(text string) string {
	color := mc.Color
	if color == "" { color = "#000000" }
	return fmt.Sprintf("center :{lat:%f, lng:%f}, radiusmeters: %.0f, color:%q",
		mc.C.Lat, mc.C.Long, mc.C.RadiusKM*1000.0, color)
}
func MapCirclesToJSVar(circles []MapCircle) template.JS {
	str := "{\n"
	for i,mc := range circles {
		str += fmt.Sprintf("    %d: {%s},\n", i, mc.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

type MapPoint struct {
	ITP   *fdb.InterpolatedTrackpoint
	TP    *fdb.Trackpoint
	Pos   *geo.Latlong

	Icon   string  // The <foo> in /static/dot-<foo>.png
	Text   string
}

func (mp MapPoint)ToJSStr(text string) string {
	if mp.Icon == "" { mp.Icon = "pink" }
	tp := fdb.Trackpoint{DataSource:"n/a"}

	if mp.ITP != nil {
		// Transform this into a tp with extra text
		tp = mp.ITP.Trackpoint
		tp.DataSource += "/interp"
		mp.Text = fmt.Sprintf("** Interpolated trackpoint\n"+
			" * Pre :%s\n * This:%s\n * Post:%s\n * Ratio: %.2f\n%s",
			mp.ITP.Pre, mp.ITP, mp.ITP.Post, mp.ITP.Ratio, mp.Text)
	} else if mp.TP != nil {
		tp = *mp.TP
		age := date.RoundDuration(time.Since(tp.TimestampUTC))
		times := fmt.Sprintf("%s (age:%s, epoch:%d)",
			date.InPdt(tp.TimestampUTC), age, tp.TimestampUTC.Unix())
		mp.Text = fmt.Sprintf("** %s\n* %s\n* DataSource: %s\n%s* %s",
			times, mp.TP, mp.TP.LongSource(), tp.AnalysisAnnotation, mp.Text)
		if tp.AnalysisMapIcon != "" {
			mp.Icon = tp.AnalysisMapIcon
			}
	} else {
		tp.Latlong = *mp.Pos
	}

	str := tp.ToJSString()
	str += fmt.Sprintf(", icon:%q, info:%q", mp.Icon, mp.Text)
	return str
}

type MapLine struct {
	Start geo.Latlong `json:"s"`
	End   geo.Latlong `json:"e"`

	Color        string  `json:"color"`    // A hex color value (e.g. "#ff8822")
	Opacity      float64 `json:"opacity"`
}

func (ml MapLine)ToJSStr(text string) string {
	color,op := ml.Color, ml.Opacity
	if color == "" { color = "#000000" }
	if op == 0.0 { op = 1.0 }
	return fmt.Sprintf("s:{lat:%f, lng:%f}, e:{lat:%f, lng:%f}, color:\"%s\", opacity:%.2f",
		ml.Start.Lat, ml.Start.Long, ml.End.Lat, ml.End.Long, color, op)
}

func MapPointsToJSVar(points []MapPoint) template.JS {
	str := "{\n"
	for i,mp := range points {
		str += fmt.Sprintf("    %d: {%s},\n", i, mp.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

func MapLinesToJSVar(lines []MapLine) template.JS {
	str := "{\n"
	for i,ml := range lines {
		str += fmt.Sprintf("    %d: {%s},\n", i, ml.ToJSStr(""))
	}
	return template.JS(str + "  }\n")
}

func LatlongTimeBoxToMapLines(tb geo.LatlongTimeBox, color string) []MapLine {
	maplines := []MapLine{}
	for _,line := range tb.LatlongBox.ToLines() {
		mapline := MapLine{Start:line.From, End:line.To}
		mapline.Color = color
		maplines = append(maplines, mapline)
	}
	return maplines
}

// This is a kind of abuse of ColoringStrategy from colorscheme.go
func TrackToMapPoints(t *fdb.Track, icon, banner string, coloring ColoringStrategy) []MapPoint {
	sourceColors := map[string]string{
		"ADSB":  "yellow",
		"MLAT":  "red",
		"fr24":  "green",
		"FA:TA": "blue",
		"FA:TZ": "blue",
	}
	receiverColors := map[string]string{
		"NorthPi":       "green",
		"BlankPi":       "green",

		"ScottsValley":  "yellow",
		"ScottsValley3": "yellow",
		"ScottsValleyLite": "green",
		"Saratoga":      "red",
		"Saratoga2":     "blue",
	}

	points := []MapPoint{}
	if len(*t) == 0 { return points }

	for i,_ := range *t {
		color := icon
		tp := (*t)[i]
		if coloring == ByCandyStripe {
			if tp.ReceiverName == "NorthPi" {
				if color == "blue" { color = "red" } else { color = "green" }
			} else if tp.ReceiverName == "ScottsValley" {
				if color == "blue" { color = "pink" }
			}

		} else if icon == "" {
			if coloring == ByADSBReceiver {
				if c,exists := receiverColors[tp.ReceiverName]; exists {
					color = c
				} else {
					color = receiverColors["default"]
				}
				// Offset receivers, slightly
				//if tp.ReceiverName == "ScottsValley" {
				//	tp.Lat  += 0.0006
				//	tp.Long += 0.0006
				//}
			} else if coloring == ByData {
				if c,exists := sourceColors[tp.DataSource]; exists { color = c }
			}
		}

		pointText := fmt.Sprintf("Point %d/%d\n%s", i, len(*t), banner)

		points = append(points, MapPoint{
			TP: &tp, Icon:color,
			Text: pointText,
		})
	}
	return points
}
<|endoftext|>