<commit_before>package usergrid

import (
	"testing"
	"encoding/json"
	"strconv"
)
func TestPost(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"name":"go_dog", "status":"good dog"}
	resp, err:= client.Post("dogs", nil, data)
	if(err!=nil){
		t.Logf("TestPost failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestPostDuplicate(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"name":"go_dog", "status":"good dog"}
	resp, err:= client.Post("dogs", nil, data)
	if(err==nil){
		t.Logf("TestPost failed. Should have received a duplicate entity error.\n")
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestGet(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dog", nil)
	if(err!=nil){
		t.Logf("TestGet failed: %s\n", err)
		t.Fail()
	}else{
		if (resp["entities"]==nil) {
			t.Logf("No entities returned")
			t.Fail()
		}else if(len(resp["entities"].([]interface{}))!=1){
			t.Logf("Incorrect number of entities")
			t.Fail()
		}
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestGetBadEntity(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dogizzle", nil)
	if(err==nil){
		t.Logf("TestGet failed. dog should not exist: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestPut(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"status":"flat out on the highway and drying in the sun"}
	resp, err:= client.Put("dogs/go_dog", nil, data)
	if(err!=nil){
		t.Logf("TestPut failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Delete("dogs/go_dog", nil)
	if(err!=nil){
		t.Logf("Test failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete2(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dog", nil)
	if(err==nil){
		t.Logf("Test failed. Dog should be deleted: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete3(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Delete("dogs/go_dog", nil)
	if(err==nil){
		t.Logf("Test failed. Should return service_resource_not_found error\n")
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
// func TestMassDelete(t *testing.T){
// 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
// 	params := map[string]string{"limit":strconv.Itoa(500)}
// 	resp, err:= client.Get("benchmark", params)
// 	if(err!=nil){
// 		t.Logf("Test failed: %s\n", err)
// 		t.Fail()
// 	}
// 	if (resp["entities"]!=nil && len(resp["entities"].([]interface{}))>0) {
// 		for k,v := range resp["entities"].([]interface{}) {
// 			if(v == nil){
// 				t.Logf("could not delete %d: %v\n", k, v)
// 				t.Fail()
// 			}else{
// 				t.Logf(fmt.Sprintf("yay %v",v))
// 				entity := v.(map[string]interface{})
// 				t.Logf(fmt.Sprintf("entity %v",entity["uuid"]))
// 				_, err:= client.Delete("benchmark/"+entity["uuid"].(string), nil)
// 				if(err!=nil){
// 					t.Logf("could not delete %s: %s\n", entity["uuid"].(string), err)
// 					t.Fail()
// 				}
// 			}
// 		}
// 	}
// }

func BenchmarkPost(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"index":"0", "description":"golang benchmark"}
	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	data["index"]=strconv.Itoa(i);
		resp, err:= client.Post("benchmark", nil, data)
		if(err!=nil){
			b.Logf("BenchmarkPost failed: %s\n", err)
			b.Fail()
		}
		if(b.Failed()){
			str,_:=json.MarshalIndent(resp,"","  ")
			b.Logf("RESPONSE: %s", str)
		}
   }
}
func BenchmarkGet(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Get("benchmark/"+entity["uuid"].(string), nil)
		if(err!=nil){
			b.Logf("BenchmarkGet failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}
func BenchmarkPut(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Put("benchmark/"+entity["uuid"].(string), nil, map[string]string{"updated":"true"})
		if(err!=nil){
			b.Logf("BenchmarkPut failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}

func BenchmarkDelete(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Delete("benchmark/"+entity["uuid"].(string), nil)
		if(err!=nil){
			b.Logf("BenchmarkDelete failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}

<commit_msg>Added benchmarks for raw requests<commit_after>package usergrid

import (
	"encoding/json"
	"strconv"
	"testing"
	"time"
)
func TestPost(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"name":"go_dog", "status":"good dog"}
	resp, err:= client.Post("dogs", nil, data)
	if(err!=nil){
		t.Logf("TestPost failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestPostDuplicate(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"name":"go_dog", "status":"good dog"}
	resp, err:= client.Post("dogs", nil, data)
	if(err==nil){
		t.Logf("TestPost failed. Should have received a duplicate entity error.\n")
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestGet(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dog", nil)
	if(err!=nil){
		t.Logf("TestGet failed: %s\n", err)
		t.Fail()
	}else{
		if (resp["entities"]==nil) {
			t.Logf("No entities returned")
			t.Fail()
		}else if(len(resp["entities"].([]interface{}))!=1){
			t.Logf("Incorrect number of entities")
			t.Fail()
		}
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestGetBadEntity(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dogizzle", nil)
	if(err==nil){
		t.Logf("TestGet failed. dog should not exist: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestPut(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"status":"flat out on the highway and drying in the sun"}
	resp, err:= client.Put("dogs/go_dog", nil, data)
	if(err!=nil){
		t.Logf("TestPut failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Delete("dogs/go_dog", nil)
	if(err!=nil){
		t.Logf("Test failed: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete2(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Get("dogs/go_dog", nil)
	if(err==nil){
		t.Logf("Test failed. Dog should be deleted: %s\n", err)
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}
func TestDelete3(t *testing.T){
	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	resp, err:= client.Delete("dogs/go_dog", nil)
	if(err==nil){
		t.Logf("Test failed. Should return service_resource_not_found error\n")
		t.Fail()
	}
	if(t.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		t.Logf("RESPONSE: %s", str)
	}
}

func BenchmarkPost(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	data := map[string]string{"index":"0", "description":"golang benchmark"}
	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	data["index"]=strconv.Itoa(i);
		resp, err:= client.Post("benchmark", nil, data)
		if(err!=nil){
			b.Logf("BenchmarkPost failed: %s\n", err)
			b.Fail()
		}
		if(b.Failed()){
			str,_:=json.MarshalIndent(resp,"","  ")
			b.Logf("RESPONSE: %s", str)
		}
   }
}
func BenchmarkRawRequests(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(10)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	requests:=0
	responses:=0
	errors:=0
	var objmap interface{}
	var entity map[string]interface{}
	responseChan := make(chan []byte)
	go func(){
		for {
	        select {
	        case v := <-responseChan:
	        	if err := json.Unmarshal(v, &objmap); err == nil{
					if err:=CheckForError(&objmap); err != nil {
						errors++
					}
				}else{
					errors++
				}

	        	responses++
	        	requests--
	        case <-time.After(time.Second * 10):
	        	return
	        }
	    }
	}()
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity = entities[i % len(entities)].(map[string]interface{})
    	for ;requests>=MAX_CONCURRENT_REQUESTS;{
        	// if we outpace GOMAXPROCS, we'll run out of threads
    		time.Sleep(60 * time.Millisecond)
    	}
		client.Request("GET", "http://api.usergrid.com/yourorgname/sandbox/benchmark/"+entity["uuid"].(string), nil, nil, responseChan)
		requests++
   }
   for ;requests>0;{
		// wait for the last few responses
		time.Sleep(60 * time.Millisecond)
   }
}
func BenchmarkGet(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Get("benchmark/"+entity["uuid"].(string), nil)
		if(err!=nil){
			b.Logf("BenchmarkGet failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}
func BenchmarkPut(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Put("benchmark/"+entity["uuid"].(string), nil, map[string]string{"updated":"true"})
		if(err!=nil){
			b.Logf("BenchmarkPut failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}

func BenchmarkDelete(b *testing.B) {
 	client := Client {Organization:"yourorgname",Application:"sandbox",Uri:"https://api.usergrid.com"}
	params := map[string]string{"limit":strconv.Itoa(500)}
	resp, err:= client.Get("benchmark", params)
	if(err!=nil){
		b.Logf("Test failed: %s\n", err)
		b.Fail()
	}
	if (resp["entities"]==nil || len(resp["entities"].([]interface{}))==0) {
		b.Logf("Test failed: no entities to delete\n")
		b.Fail()
	}
	entities:=resp["entities"].([]interface{})
	var entity map[string]interface{}
 	b.ResetTimer()
    for i := 0; i < b.N; i++ {
    	if(len(entities)==0){
			b.Logf("Test failed: we ran out of entities\n")
			b.Fail()
			continue
    	}
    	entity, entities = entities[len(entities)-1].(map[string]interface{}), entities[:len(entities)-1]
		_, err:= client.Delete("benchmark/"+entity["uuid"].(string), nil)
		if(err!=nil){
			b.Logf("BenchmarkDelete failed: %s\n", err)
			b.Fail()
		}
   }
	if(b.Failed()){
		str,_:=json.MarshalIndent(resp,"","  ")
		b.Logf("RESPONSE: %s", str)
	}
}

<|endoftext|>


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

package trust

import (
	"math"
	"sync"
	"time"
)

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

const (
	// The weight applied to the derivative when current behavior is >= previous behavior
	defaultDerivativeGamma1 = 0

	// The weight applied to the derivative when current behavior is less than previous behavior
	defaultDerivativeGamma2 = 1.0

	// The weight applied to history data values when calculating the history value
	defaultHistoryDataWeight = 0.8
)

// MetricHistoryJSON - history data necessary to save the trust metric
type MetricHistoryJSON struct {
	NumIntervals int       `json:"intervals"`
	History      []float64 `json:"history"`
}

// TrustMetric - keeps track of peer reliability
// See tendermint/docs/architecture/adr-006-trust-metric.md for details
type TrustMetric struct {
	// Mutex that protects the metric from concurrent access
	mtx sync.Mutex

	// Determines the percentage given to current behavior
	proportionalWeight float64

	// Determines the percentage given to prior behavior
	integralWeight float64

	// Count of how many time intervals this metric has been tracking
	numIntervals int

	// Size of the time interval window for this trust metric
	maxIntervals int

	// The time duration for a single time interval
	intervalLen time.Duration

	// Stores the trust history data for this metric
	history []float64

	// Weights applied to the history data when calculating the history value
	historyWeights []float64

	// The sum of the history weights used when calculating the history value
	historyWeightSum float64

	// The current number of history data elements
	historySize int

	// The maximum number of history data elements
	historyMaxSize int

	// The calculated history value for the current time interval
	historyValue float64

	// The number of recorded good and bad events for the current time interval
	bad, good float64

	// While true, history data is not modified
	paused bool

	// Signal channel for stopping the trust metric go-routine
	stop chan struct{}
}

// NewMetric returns a trust metric with the default configuration
func NewMetric() *TrustMetric {
	return NewMetricWithConfig(DefaultConfig())
}

// NewMetricWithConfig returns a trust metric with a custom configuration
func NewMetricWithConfig(tmc TrustMetricConfig) *TrustMetric {
	tm := new(TrustMetric)
	config := customConfig(tmc)

	// Setup using the configuration values
	tm.proportionalWeight = config.ProportionalWeight
	tm.integralWeight = config.IntegralWeight
	tm.intervalLen = config.IntervalLength
	// The maximum number of time intervals is the tracking window / interval length
	tm.maxIntervals = int(config.TrackingWindow / tm.intervalLen)
	// The history size will be determined by the maximum number of time intervals
	tm.historyMaxSize = intervalToHistoryOffset(tm.maxIntervals) + 1
	// This metric has a perfect history so far
	tm.historyValue = 1.0
	// Setup the stop channel
	tm.stop = make(chan struct{})

	go tm.processRequests()
	return tm
}

// Returns a snapshot of the trust metric history data
func (tm *TrustMetric) HistoryJSON() MetricHistoryJSON {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	return MetricHistoryJSON{
		NumIntervals: tm.numIntervals,
		History:      tm.history,
	}
}

// Instantiates a trust metric by loading the history data for a single peer.
// This is called only once and only right after creation, which is why the
// lock is not held while accessing the trust metric struct members
func (tm *TrustMetric) Init(hist MetricHistoryJSON) {
	// Restore the number of time intervals we have previously tracked
	if hist.NumIntervals > tm.maxIntervals {
		hist.NumIntervals = tm.maxIntervals
	}
	tm.numIntervals = hist.NumIntervals
	// Restore the history and its current size
	if len(hist.History) > tm.historyMaxSize {
		// Keep the history no larger than historyMaxSize
		last := len(hist.History) - tm.historyMaxSize
		hist.History = hist.History[last:]
	}
	tm.history = hist.History
	tm.historySize = len(tm.history)
	// Create the history weight values and weight sum
	for i := 1; i <= tm.numIntervals; i++ {
		x := math.Pow(defaultHistoryDataWeight, float64(i)) // Optimistic weight
		tm.historyWeights = append(tm.historyWeights, x)
	}

	for _, v := range tm.historyWeights {
		tm.historyWeightSum += v
	}
	// Calculate the history value based on the loaded history data
	tm.historyValue = tm.calcHistoryValue()
}

// Pause tells the metric to pause recording data over time intervals.
// All method calls that indicate events will unpause the metric
func (tm *TrustMetric) Pause() {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	// Pause the metric for now
	tm.paused = true
}

// Stop tells the metric to stop recording data over time intervals
func (tm *TrustMetric) Stop() {
	tm.stop <- struct{}{}
}

// BadEvents indicates that an undesirable event(s) took place
func (tm *TrustMetric) BadEvents(num int) {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	tm.unpause()
	tm.bad += float64(num)
}

// GoodEvents indicates that a desirable event(s) took place
func (tm *TrustMetric) GoodEvents(num int) {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	tm.unpause()
	tm.good += float64(num)
}

// TrustValue gets the dependable trust value; always between 0 and 1
func (tm *TrustMetric) TrustValue() float64 {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	return tm.calcTrustValue()
}

// TrustScore gets a score based on the trust value always between 0 and 100
func (tm *TrustMetric) TrustScore() int {
	score := tm.TrustValue() * 100

	return int(math.Floor(score))
}

// NextTimeInterval saves current time interval data and prepares for the following interval
func (tm *TrustMetric) NextTimeInterval() {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	if tm.paused {
		// Do not prepare for the next time interval while paused
		return
	}

	// Add the current trust value to the history data
	newHist := tm.calcTrustValue()
	tm.history = append(tm.history, newHist)

	// Update history and interval counters
	if tm.historySize < tm.historyMaxSize {
		tm.historySize++
	} else {
		// Keep the history no larger than historyMaxSize
		last := len(tm.history) - tm.historyMaxSize
		tm.history = tm.history[last:]
	}

	if tm.numIntervals < tm.maxIntervals {
		tm.numIntervals++
		// Add the optimistic weight for the new time interval
		wk := math.Pow(defaultHistoryDataWeight, float64(tm.numIntervals))
		tm.historyWeights = append(tm.historyWeights, wk)
		tm.historyWeightSum += wk
	}

	// Update the history data using Faded Memories
	tm.updateFadedMemory()
	// Calculate the history value for the upcoming time interval
	tm.historyValue = tm.calcHistoryValue()
	tm.good = 0
	tm.bad = 0
}

// Copy returns a new trust metric with members containing the same values
func (tm *TrustMetric) Copy() *TrustMetric {
	if tm == nil {
		return nil
	}

	return &TrustMetric{
		proportionalWeight: tm.proportionalWeight,
		integralWeight:     tm.integralWeight,
		numIntervals:       tm.numIntervals,
		maxIntervals:       tm.maxIntervals,
		intervalLen:        tm.intervalLen,
		history:            tm.history,
		historyWeights:     tm.historyWeights,
		historyWeightSum:   tm.historyWeightSum,
		historySize:        tm.historySize,
		historyMaxSize:     tm.historyMaxSize,
		historyValue:       tm.historyValue,
		good:               tm.good,
		bad:                tm.bad,
		paused:             tm.paused,
		stop:               make(chan struct{}),
	}
}

/* Private methods */

// This method is for a goroutine that handles all requests on the metric
func (tm *TrustMetric) processRequests() {
	t := time.NewTicker(tm.intervalLen)
	defer t.Stop()
loop:
	for {
		select {
		case <-t.C:
			tm.NextTimeInterval()
		case <-tm.stop:
			// Stop all further tracking for this metric
			break loop
		}
	}
}

// Wakes the trust metric up if it is currently paused
// This method needs to be called with the mutex locked
func (tm *TrustMetric) unpause() {
	// Check if this is the first experience with
	// what we are tracking since being paused
	if tm.paused {
		tm.good = 0
		tm.bad = 0
		// New events cause us to unpause the metric
		tm.paused = false
	}
}

// Calculates the trust value for the request processing
func (tm *TrustMetric) calcTrustValue() float64 {
	weightedP := tm.proportionalWeight * tm.proportionalValue()
	weightedI := tm.integralWeight * tm.historyValue
	weightedD := tm.weightedDerivative()

	tv := weightedP + weightedI + weightedD
	// Do not return a negative value.
	if tv < 0 {
		tv = 0
	}
	return tv
}

// Calculates the current score for good/bad experiences
func (tm *TrustMetric) proportionalValue() float64 {
	value := 1.0

	total := tm.good + tm.bad
	if total > 0 {
		value = tm.good / total
	}
	return value
}

// Strengthens the derivative component when the change is negative
func (tm *TrustMetric) weightedDerivative() float64 {
	var weight float64 = defaultDerivativeGamma1

	d := tm.derivativeValue()
	if d < 0 {
		weight = defaultDerivativeGamma2
	}
	return weight * d
}

// Calculates the derivative component
func (tm *TrustMetric) derivativeValue() float64 {
	return tm.proportionalValue() - tm.historyValue
}

// Calculates the integral (history) component of the trust value
func (tm *TrustMetric) calcHistoryValue() float64 {
	var hv float64

	for i := 0; i < tm.numIntervals; i++ {
		hv += tm.fadedMemoryValue(i) * tm.historyWeights[i]
	}

	return hv / tm.historyWeightSum
}

// Retrieves the actual history data value that represents the requested time interval
func (tm *TrustMetric) fadedMemoryValue(interval int) float64 {
	first := tm.historySize - 1

	if interval == 0 {
		// Base case
		return tm.history[first]
	}

	offset := intervalToHistoryOffset(interval)
	return tm.history[first-offset]
}

// Performs the update for our Faded Memories process, which allows the
// trust metric tracking window to be large while maintaining a small
// number of history data values
func (tm *TrustMetric) updateFadedMemory() {
	if tm.historySize < 2 {
		return
	}

	end := tm.historySize - 1
	// Keep the most recent history element
	for count := 1; count < tm.historySize; count++ {
		i := end - count
		// The older the data is, the more we spread it out
		x := math.Pow(2, float64(count))
		// Two history data values are merged into a single value
		tm.history[i] = ((tm.history[i] * (x - 1)) + tm.history[i+1]) / x
	}
}

// Map the interval value down to an offset from the beginning of history
func intervalToHistoryOffset(interval int) int {
	// The system maintains 2^m interval values in the form of m history
	// data values. Therefore, we access the ith interval by obtaining
	// the history data index = the floor of log2(i)
	return int(math.Floor(math.Log2(float64(interval))))
}
<commit_msg>p2p/trust: lock on Copy()<commit_after>// Copyright 2017 Tendermint. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package trust

import (
	"math"
	"sync"
	"time"
)

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

const (
	// The weight applied to the derivative when current behavior is >= previous behavior
	defaultDerivativeGamma1 = 0

	// The weight applied to the derivative when current behavior is less than previous behavior
	defaultDerivativeGamma2 = 1.0

	// The weight applied to history data values when calculating the history value
	defaultHistoryDataWeight = 0.8
)

// MetricHistoryJSON - history data necessary to save the trust metric
type MetricHistoryJSON struct {
	NumIntervals int       `json:"intervals"`
	History      []float64 `json:"history"`
}

// TrustMetric - keeps track of peer reliability
// See tendermint/docs/architecture/adr-006-trust-metric.md for details
type TrustMetric struct {
	// Mutex that protects the metric from concurrent access
	mtx sync.Mutex

	// Determines the percentage given to current behavior
	proportionalWeight float64

	// Determines the percentage given to prior behavior
	integralWeight float64

	// Count of how many time intervals this metric has been tracking
	numIntervals int

	// Size of the time interval window for this trust metric
	maxIntervals int

	// The time duration for a single time interval
	intervalLen time.Duration

	// Stores the trust history data for this metric
	history []float64

	// Weights applied to the history data when calculating the history value
	historyWeights []float64

	// The sum of the history weights used when calculating the history value
	historyWeightSum float64

	// The current number of history data elements
	historySize int

	// The maximum number of history data elements
	historyMaxSize int

	// The calculated history value for the current time interval
	historyValue float64

	// The number of recorded good and bad events for the current time interval
	bad, good float64

	// While true, history data is not modified
	paused bool

	// Signal channel for stopping the trust metric go-routine
	stop chan struct{}
}

// NewMetric returns a trust metric with the default configuration
func NewMetric() *TrustMetric {
	return NewMetricWithConfig(DefaultConfig())
}

// NewMetricWithConfig returns a trust metric with a custom configuration
func NewMetricWithConfig(tmc TrustMetricConfig) *TrustMetric {
	tm := new(TrustMetric)
	config := customConfig(tmc)

	// Setup using the configuration values
	tm.proportionalWeight = config.ProportionalWeight
	tm.integralWeight = config.IntegralWeight
	tm.intervalLen = config.IntervalLength
	// The maximum number of time intervals is the tracking window / interval length
	tm.maxIntervals = int(config.TrackingWindow / tm.intervalLen)
	// The history size will be determined by the maximum number of time intervals
	tm.historyMaxSize = intervalToHistoryOffset(tm.maxIntervals) + 1
	// This metric has a perfect history so far
	tm.historyValue = 1.0
	// Setup the stop channel
	tm.stop = make(chan struct{})

	go tm.processRequests()
	return tm
}

// Returns a snapshot of the trust metric history data
func (tm *TrustMetric) HistoryJSON() MetricHistoryJSON {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	return MetricHistoryJSON{
		NumIntervals: tm.numIntervals,
		History:      tm.history,
	}
}

// Instantiates a trust metric by loading the history data for a single peer.
// This is called only once and only right after creation, which is why the
// lock is not held while accessing the trust metric struct members
func (tm *TrustMetric) Init(hist MetricHistoryJSON) {
	// Restore the number of time intervals we have previously tracked
	if hist.NumIntervals > tm.maxIntervals {
		hist.NumIntervals = tm.maxIntervals
	}
	tm.numIntervals = hist.NumIntervals
	// Restore the history and its current size
	if len(hist.History) > tm.historyMaxSize {
		// Keep the history no larger than historyMaxSize
		last := len(hist.History) - tm.historyMaxSize
		hist.History = hist.History[last:]
	}
	tm.history = hist.History
	tm.historySize = len(tm.history)
	// Create the history weight values and weight sum
	for i := 1; i <= tm.numIntervals; i++ {
		x := math.Pow(defaultHistoryDataWeight, float64(i)) // Optimistic weight
		tm.historyWeights = append(tm.historyWeights, x)
	}

	for _, v := range tm.historyWeights {
		tm.historyWeightSum += v
	}
	// Calculate the history value based on the loaded history data
	tm.historyValue = tm.calcHistoryValue()
}

// Pause tells the metric to pause recording data over time intervals.
// All method calls that indicate events will unpause the metric
func (tm *TrustMetric) Pause() {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	// Pause the metric for now
	tm.paused = true
}

// Stop tells the metric to stop recording data over time intervals
func (tm *TrustMetric) Stop() {
	tm.stop <- struct{}{}
}

// BadEvents indicates that an undesirable event(s) took place
func (tm *TrustMetric) BadEvents(num int) {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	tm.unpause()
	tm.bad += float64(num)
}

// GoodEvents indicates that a desirable event(s) took place
func (tm *TrustMetric) GoodEvents(num int) {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	tm.unpause()
	tm.good += float64(num)
}

// TrustValue gets the dependable trust value; always between 0 and 1
func (tm *TrustMetric) TrustValue() float64 {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	return tm.calcTrustValue()
}

// TrustScore gets a score based on the trust value always between 0 and 100
func (tm *TrustMetric) TrustScore() int {
	score := tm.TrustValue() * 100

	return int(math.Floor(score))
}

// NextTimeInterval saves current time interval data and prepares for the following interval
func (tm *TrustMetric) NextTimeInterval() {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()

	if tm.paused {
		// Do not prepare for the next time interval while paused
		return
	}

	// Add the current trust value to the history data
	newHist := tm.calcTrustValue()
	tm.history = append(tm.history, newHist)

	// Update history and interval counters
	if tm.historySize < tm.historyMaxSize {
		tm.historySize++
	} else {
		// Keep the history no larger than historyMaxSize
		last := len(tm.history) - tm.historyMaxSize
		tm.history = tm.history[last:]
	}

	if tm.numIntervals < tm.maxIntervals {
		tm.numIntervals++
		// Add the optimistic weight for the new time interval
		wk := math.Pow(defaultHistoryDataWeight, float64(tm.numIntervals))
		tm.historyWeights = append(tm.historyWeights, wk)
		tm.historyWeightSum += wk
	}

	// Update the history data using Faded Memories
	tm.updateFadedMemory()
	// Calculate the history value for the upcoming time interval
	tm.historyValue = tm.calcHistoryValue()
	tm.good = 0
	tm.bad = 0
}

// Copy returns a new trust metric with members containing the same values
func (tm *TrustMetric) Copy() *TrustMetric {
	tm.mtx.Lock()
	defer tm.mtx.Unlock()
	if tm == nil {
		return nil
	}

	return &TrustMetric{
		proportionalWeight: tm.proportionalWeight,
		integralWeight:     tm.integralWeight,
		numIntervals:       tm.numIntervals,
		maxIntervals:       tm.maxIntervals,
		intervalLen:        tm.intervalLen,
		history:            tm.history,
		historyWeights:     tm.historyWeights,
		historyWeightSum:   tm.historyWeightSum,
		historySize:        tm.historySize,
		historyMaxSize:     tm.historyMaxSize,
		historyValue:       tm.historyValue,
		good:               tm.good,
		bad:                tm.bad,
		paused:             tm.paused,
		stop:               make(chan struct{}),
	}
}

/* Private methods */

// This method is for a goroutine that handles all requests on the metric
func (tm *TrustMetric) processRequests() {
	t := time.NewTicker(tm.intervalLen)
	defer t.Stop()
loop:
	for {
		select {
		case <-t.C:
			tm.NextTimeInterval()
		case <-tm.stop:
			// Stop all further tracking for this metric
			break loop
		}
	}
}

// Wakes the trust metric up if it is currently paused
// This method needs to be called with the mutex locked
func (tm *TrustMetric) unpause() {
	// Check if this is the first experience with
	// what we are tracking since being paused
	if tm.paused {
		tm.good = 0
		tm.bad = 0
		// New events cause us to unpause the metric
		tm.paused = false
	}
}

// Calculates the trust value for the request processing
func (tm *TrustMetric) calcTrustValue() float64 {
	weightedP := tm.proportionalWeight * tm.proportionalValue()
	weightedI := tm.integralWeight * tm.historyValue
	weightedD := tm.weightedDerivative()

	tv := weightedP + weightedI + weightedD
	// Do not return a negative value.
	if tv < 0 {
		tv = 0
	}
	return tv
}

// Calculates the current score for good/bad experiences
func (tm *TrustMetric) proportionalValue() float64 {
	value := 1.0

	total := tm.good + tm.bad
	if total > 0 {
		value = tm.good / total
	}
	return value
}

// Strengthens the derivative component when the change is negative
func (tm *TrustMetric) weightedDerivative() float64 {
	var weight float64 = defaultDerivativeGamma1

	d := tm.derivativeValue()
	if d < 0 {
		weight = defaultDerivativeGamma2
	}
	return weight * d
}

// Calculates the derivative component
func (tm *TrustMetric) derivativeValue() float64 {
	return tm.proportionalValue() - tm.historyValue
}

// Calculates the integral (history) component of the trust value
func (tm *TrustMetric) calcHistoryValue() float64 {
	var hv float64

	for i := 0; i < tm.numIntervals; i++ {
		hv += tm.fadedMemoryValue(i) * tm.historyWeights[i]
	}

	return hv / tm.historyWeightSum
}

// Retrieves the actual history data value that represents the requested time interval
func (tm *TrustMetric) fadedMemoryValue(interval int) float64 {
	first := tm.historySize - 1

	if interval == 0 {
		// Base case
		return tm.history[first]
	}

	offset := intervalToHistoryOffset(interval)
	return tm.history[first-offset]
}

// Performs the update for our Faded Memories process, which allows the
// trust metric tracking window to be large while maintaining a small
// number of history data values
func (tm *TrustMetric) updateFadedMemory() {
	if tm.historySize < 2 {
		return
	}

	end := tm.historySize - 1
	// Keep the most recent history element
	for count := 1; count < tm.historySize; count++ {
		i := end - count
		// The older the data is, the more we spread it out
		x := math.Pow(2, float64(count))
		// Two history data values are merged into a single value
		tm.history[i] = ((tm.history[i] * (x - 1)) + tm.history[i+1]) / x
	}
}

// Map the interval value down to an offset from the beginning of history
func intervalToHistoryOffset(interval int) int {
	// The system maintains 2^m interval values in the form of m history
	// data values. Therefore, we access the ith interval by obtaining
	// the history data index = the floor of log2(i)
	return int(math.Floor(math.Log2(float64(interval))))
}
<|endoftext|>


<commit_before>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"strconv"
)

type Slave struct {
	ID                   int64  `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistentStorage    bool   `json:"persistent_storage"`
	ConfiguredState      string `json:"configured_state"`
	RiskGroupID          *int64 `json:"risk_group_id"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {
	tx := m.DB.Begin()
	defer tx.Rollback()

	var slaves []*model.Slave
	err := tx.Order("id", false).Find(&slaves).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i] = ProjectModelSlaveToSlave(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	tx := m.DB.Begin()
	defer tx.Rollback()

	var slave model.Slave
	res := tx.First(&slave, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(&slave))
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the slave ID in PUT request")
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		tx.Rollback()
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created slave

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(modelSlave))

	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	if err = postSlave.assertNoZeroFieldsSet(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not POST JSON with zero values in any field: %s", err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	tx := m.DB.Begin()

	var modelSlave model.Slave
	modelSlaveRes := tx.First(&modelSlave, id)
	if modelSlaveRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = modelSlaveRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	updatedModelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, updatedModelSlave)
	if dbError != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		tx.Rollback()
		return
	}
	if permissionError != nil {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		tx.Rollback()
		return
	}

	// Persist to database
	err = tx.Save(&updatedModelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok {
		if driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprint(w, driverErr.Error())
			tx.Rollback()
			return
		}
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	tx := m.DB.Begin()

	// Can only delete disabled slaves
	var currentSlave model.Slave
	if err = tx.First(&currentSlave, id).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if len(currentSlave.Mongods) != 0 {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintf(w, "slave with id %d has active Mongods", currentSlave.ID)
		tx.Rollback()
		return
	}

	// Allow delete

	s := tx.Delete(&model.Slave{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if s.RowsAffected > 1 {
		log.Printf("inconsistency: slave DELETE affected more than one row. Slave.ID = %v", id)
	}

	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}

func changeToSlaveAllowed(tx *gorm.DB, currentSlave *model.Slave, updatedSlave *model.Slave) (permissionError, dbError error) {

	//Allow change of state if nothing else is changed
	if currentSlave.ID == updatedSlave.ID &&
		currentSlave.Hostname == updatedSlave.Hostname &&
		currentSlave.Port == updatedSlave.Port &&
		currentSlave.MongodPortRangeBegin == updatedSlave.MongodPortRangeBegin &&
		currentSlave.MongodPortRangeEnd == updatedSlave.MongodPortRangeEnd &&
		currentSlave.PersistentStorage == updatedSlave.PersistentStorage &&
		currentSlave.RiskGroupID == updatedSlave.RiskGroupID {
		return nil, nil
	}
	if currentSlave.ConfiguredState != model.SlaveStateDisabled {
		return fmt.Errorf("slave's desired state must be `disabled`"), nil
	}

	if err := tx.Model(&currentSlave).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		return nil, err
	}

	if len(currentSlave.Mongods) != 0 {
		return fmt.Errorf("slave has active Mongods"), nil
	}

	return nil, nil

}
<commit_msg>FIX: masterapi: tried to print wrong error object<commit_after>package masterapi

import (
	"encoding/json"
	"fmt"
	"github.com/KIT-MAMID/mamid/model"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"strconv"
)

type Slave struct {
	ID                   int64  `json:"id"`
	Hostname             string `json:"hostname"`
	Port                 uint   `json:"slave_port"`
	MongodPortRangeBegin uint   `json:"mongod_port_range_begin"` //inclusive
	MongodPortRangeEnd   uint   `json:"mongod_port_range_end"`   //exclusive
	PersistentStorage    bool   `json:"persistent_storage"`
	ConfiguredState      string `json:"configured_state"`
	RiskGroupID          *int64 `json:"risk_group_id"`
}

func (m *MasterAPI) SlaveIndex(w http.ResponseWriter, r *http.Request) {
	tx := m.DB.Begin()
	defer tx.Rollback()

	var slaves []*model.Slave
	err := tx.Order("id", false).Find(&slaves).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	out := make([]*Slave, len(slaves))
	for i, v := range slaves {
		out[i] = ProjectModelSlaveToSlave(v)
	}
	json.NewEncoder(w).Encode(out)
}

func (m *MasterAPI) SlaveById(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id64, err := strconv.ParseUint(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	id := uint(id64)

	if id == 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "id may not be 0")
		return
	}

	tx := m.DB.Begin()
	defer tx.Rollback()

	var slave model.Slave
	res := tx.First(&slave, id)

	if res.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if err = res.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		return
	}

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(&slave))
	return
}

func (m *MasterAPI) SlavePut(w http.ResponseWriter, r *http.Request) {
	var postSlave Slave
	err := json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != 0 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not specify the slave ID in PUT request")
		return
	}

	modelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err.Error())
		return
	}

	// Persist to database

	tx := m.DB.Begin()

	err = tx.Create(&modelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok && driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, driverErr.Error())
		tx.Rollback()
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

	// Return created slave

	json.NewEncoder(w).Encode(ProjectModelSlaveToSlave(modelSlave))

	return
}

func (m *MasterAPI) SlaveUpdate(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var postSlave Slave
	err = json.NewDecoder(r.Body).Decode(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "cannot parse object (%s)", err.Error())
		return
	}

	// Validation

	if postSlave.ID != id {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not change the id of an object")
		return
	}

	if err = postSlave.assertNoZeroFieldsSet(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "must not POST JSON with zero values in any field: %s", err.Error())
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	tx := m.DB.Begin()

	var modelSlave model.Slave
	modelSlaveRes := tx.First(&modelSlave, id)
	if modelSlaveRes.RecordNotFound() {
		w.WriteHeader(http.StatusNotFound)
		return
	} else if err = modelSlaveRes.Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	updatedModelSlave, err := ProjectSlaveToModelSlave(&postSlave)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	// Only allow changes to both observed and desired disabled slaves

	permissionError, dbError := changeToSlaveAllowed(tx, &modelSlave, updatedModelSlave)
	if dbError != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, dbError)
		tx.Rollback()
		return
	}
	if permissionError != nil {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprint(w, permissionError)
		tx.Rollback()
		return
	}

	// Persist to database
	err = tx.Save(&updatedModelSlave).Error

	//Check db specific errors
	if driverErr, ok := err.(sqlite3.Error); ok {
		if driverErr.ExtendedCode == sqlite3.ErrConstraintUnique {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprint(w, driverErr.Error())
			tx.Rollback()
			return
		}
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()

}

func (m *MasterAPI) SlaveDelete(w http.ResponseWriter, r *http.Request) {
	idStr := mux.Vars(r)["slaveId"]
	id, err := strconv.ParseInt(idStr, 10, 0)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	tx := m.DB.Begin()

	// Can only delete disabled slaves
	var currentSlave model.Slave
	if err = tx.First(&currentSlave, id).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, err.Error())
		tx.Rollback()
		return
	}

	if len(currentSlave.Mongods) != 0 {
		w.WriteHeader(http.StatusForbidden)
		fmt.Fprintf(w, "slave with id %d has active Mongods", currentSlave.ID)
		tx.Rollback()
		return
	}

	// Allow delete

	s := tx.Delete(&model.Slave{ID: id})
	if s.Error != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, s.Error.Error())
		tx.Rollback()
		return
	}

	if s.RowsAffected > 1 {
		log.Printf("inconsistency: slave DELETE affected more than one row. Slave.ID = %v", id)
	}

	if s.RowsAffected == 0 {
		w.WriteHeader(http.StatusNotFound)
		tx.Rollback()
		return
	}

	// Trigger cluster allocator
	if err = m.attemptClusterAllocator(tx, w); err != nil {
		return
	}

	tx.Commit()
}

func changeToSlaveAllowed(tx *gorm.DB, currentSlave *model.Slave, updatedSlave *model.Slave) (permissionError, dbError error) {

	//Allow change of state if nothing else is changed
	if currentSlave.ID == updatedSlave.ID &&
		currentSlave.Hostname == updatedSlave.Hostname &&
		currentSlave.Port == updatedSlave.Port &&
		currentSlave.MongodPortRangeBegin == updatedSlave.MongodPortRangeBegin &&
		currentSlave.MongodPortRangeEnd == updatedSlave.MongodPortRangeEnd &&
		currentSlave.PersistentStorage == updatedSlave.PersistentStorage &&
		currentSlave.RiskGroupID == updatedSlave.RiskGroupID {
		return nil, nil
	}
	if currentSlave.ConfiguredState != model.SlaveStateDisabled {
		return fmt.Errorf("slave's desired state must be `disabled`"), nil
	}

	if err := tx.Model(&currentSlave).Related(&currentSlave.Mongods, "Mongods").Error; err != nil {
		return nil, err
	}

	if len(currentSlave.Mongods) != 0 {
		return fmt.Errorf("slave has active Mongods"), nil
	}

	return nil, nil

}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"os"
)

type Pprinter interface {
	Pprint(indent string) string
}

// PprintError pretty-prints an error if it implements Pprinter, and prints it
// in bold and red otherwise.
func PprintError(err error) {
	if pprinter, ok := err.(Pprinter); ok {
		fmt.Fprintln(os.Stderr, pprinter.Pprint(""))
	} else {
		fmt.Fprintln(os.Stderr, "\033[31;1m%s\033[m", err.Error())
	}
}
<commit_msg>use proper print in pprinter<commit_after>package util

import (
	"fmt"
	"os"
)

type Pprinter interface {
	Pprint(indent string) string
}

// PprintError pretty-prints an error if it implements Pprinter, and prints it
// in bold and red otherwise.
func PprintError(err error) {
	if pprinter, ok := err.(Pprinter); ok {
		fmt.Fprintln(os.Stderr, pprinter.Pprint(""))
	} else {
		fmt.Fprintf(os.Stderr, "\033[31;1m%s\033[m", err.Error())
	}
}
<|endoftext|>


<commit_before>package tlvparse

import (
	"encoding/binary"
	"unicode"
	"unicode/utf8"

	"github.com/pires/go-proxyproto"
)

const (
	// pp2_tlv_ssl.client  bit fields
	PP2_BITFIELD_CLIENT_SSL       uint8 = 0x01
	PP2_BITFIELD_CLIENT_CERT_CONN       = 0x02
	PP2_BITFIELD_CLIENT_CERT_SESS       = 0x04

	tlvSSLMinLen = 5 // len(pp2_tlv_ssl.client) + len(pp2_tlv_ssl.verify)
)

// 2.2.5. The PP2_TYPE_SSL type and subtypes
/*
   struct pp2_tlv_ssl {
           uint8_t  client;
           uint32_t verify;
           struct pp2_tlv sub_tlv[0];
   };
*/
type PP2SSL struct {
	Client uint8 // The <client> field is made of a bit field from the following values,
	// indicating which element is present: PP2_BITFIELD_CLIENT_SSL,
	// PP2_BITFIELD_CLIENT_CERT_CONN, PP2_BITFIELD_CLIENT_CERT_SESS
	Verify uint32 // Verify will be zero if the client presented a certificate
	// and it was successfully verified, and non-zero otherwise.
	TLV []proxyproto.TLV
}

// Verified is true if the client presented a certificate and it was successfully verified
func (s PP2SSL) Verified() bool {
	return s.Verify == 0
}

// ClientSSL indicates that the client connected over SSL/TLS.  When true, SSLVersion will return the version.
func (s PP2SSL) ClientSSL() bool {
	return s.Client&PP2_BITFIELD_CLIENT_SSL == PP2_BITFIELD_CLIENT_SSL
}

// ClientCertConn indicates that the client provided a certificate over the current connection.
func (s PP2SSL) ClientCertConn() bool {
	return s.Client&PP2_BITFIELD_CLIENT_CERT_CONN == PP2_BITFIELD_CLIENT_CERT_CONN
}

// ClientCertSess indicates that the client provided a certificate at least once over the TLS session this
// connection belongs to.
func (s PP2SSL) ClientCertSess() bool {
	return s.Client&PP2_BITFIELD_CLIENT_CERT_SESS == PP2_BITFIELD_CLIENT_CERT_SESS
}

// SSLVersion returns the US-ASCII string representation of the TLS version and whether that extension exists.
func (s PP2SSL) SSLVersion() (string, bool) {
	for _, tlv := range s.TLV {
		if tlv.Type == proxyproto.PP2_SUBTYPE_SSL_VERSION {
			return string(tlv.Value), true
		}
	}
	return "", false
}

// Marshal formats the PP2SSL structure as a TLV.
func (s PP2SSL) Marshal() (proxyproto.TLV, error) {
	v := make([]byte, 5)
	v[0] = s.Client
	binary.BigEndian.PutUint32(v[1:5], s.Verify)

	tlvs, err := proxyproto.JoinTLVs(s.TLV)
	if err != nil {
		return proxyproto.TLV{}, err
	}
	v = append(v, tlvs...)

	return proxyproto.TLV{
		Type:  proxyproto.PP2_TYPE_SSL,
		Value: v,
	}, nil
}

// ClientCN returns the string representation (in UTF8) of the Common Name field (OID: 2.5.4.3) of the client
// certificate's Distinguished Name and whether that extension exists.
func (s PP2SSL) ClientCN() (string, bool) {
	for _, tlv := range s.TLV {
		if tlv.Type == proxyproto.PP2_SUBTYPE_SSL_CN {
			return string(tlv.Value), true
		}
	}
	return "", false
}

// SSLType is true if the TLV is type SSL
func IsSSL(t proxyproto.TLV) bool {
	return t.Type == proxyproto.PP2_TYPE_SSL && len(t.Value) >= tlvSSLMinLen
}

// SSL returns the pp2_tlv_ssl from section 2.2.5 or errors with ErrIncompatibleTLV or ErrMalformedTLV
func SSL(t proxyproto.TLV) (PP2SSL, error) {
	ssl := PP2SSL{}
	if !IsSSL(t) {
		return ssl, proxyproto.ErrIncompatibleTLV
	}
	if len(t.Value) < tlvSSLMinLen {
		return ssl, proxyproto.ErrMalformedTLV
	}
	ssl.Client = t.Value[0]
	ssl.Verify = binary.BigEndian.Uint32(t.Value[1:5])
	var err error
	ssl.TLV, err = proxyproto.SplitTLVs(t.Value[5:])
	if err != nil {
		return PP2SSL{}, err
	}
	versionFound := !ssl.ClientSSL()
	var cnFound bool
	for _, tlv := range ssl.TLV {
		switch tlv.Type {
		case proxyproto.PP2_SUBTYPE_SSL_VERSION:
			/*
				The PP2_CLIENT_SSL flag indicates that the client connected over SSL/TLS. When
				this field is present, the US-ASCII string representation of the TLS version is
				appended at the end of the field in the TLV format using the type
				PP2_SUBTYPE_SSL_VERSION.
			*/
			if len(tlv.Value) == 0 || !isASCII(tlv.Value) {
				return PP2SSL{}, proxyproto.ErrMalformedTLV
			}
			versionFound = true
		case proxyproto.PP2_SUBTYPE_SSL_CN:
			/*
				In all cases, the string representation (in UTF8) of the Common Name field
				(OID: 2.5.4.3) of the client certificate's Distinguished Name, is appended
				using the TLV format and the type PP2_SUBTYPE_SSL_CN. E.g. "example.com".
			*/
			if len(tlv.Value) == 0 || !utf8.Valid(tlv.Value) {
				return PP2SSL{}, proxyproto.ErrMalformedTLV
			}
			cnFound = true
		}
	}
	if !(versionFound && cnFound) {
		return PP2SSL{}, proxyproto.ErrMalformedTLV
	}
	return ssl, nil
}

// SSL returns the first PP2SSL if it exists and is well formed as well as bool indicating if it was found.
func FindSSL(tlvs []proxyproto.TLV) (PP2SSL, bool) {
	for _, t := range tlvs {
		if ssl, err := SSL(t); err == nil {
			return ssl, true
		}
	}
	return PP2SSL{}, false
}

// isASCII checks whether a byte slice has all characters that fit in the ascii character set, including the null byte.
func isASCII(b []byte) bool {
	for _, c := range b {
		if c > unicode.MaxASCII {
			return false
		}
	}
	return true
}
<commit_msg>Don't make SSL_CN mandatory<commit_after>package tlvparse

import (
	"encoding/binary"
	"unicode"
	"unicode/utf8"

	"github.com/pires/go-proxyproto"
)

const (
	// pp2_tlv_ssl.client  bit fields
	PP2_BITFIELD_CLIENT_SSL       uint8 = 0x01
	PP2_BITFIELD_CLIENT_CERT_CONN       = 0x02
	PP2_BITFIELD_CLIENT_CERT_SESS       = 0x04

	tlvSSLMinLen = 5 // len(pp2_tlv_ssl.client) + len(pp2_tlv_ssl.verify)
)

// 2.2.5. The PP2_TYPE_SSL type and subtypes
/*
   struct pp2_tlv_ssl {
           uint8_t  client;
           uint32_t verify;
           struct pp2_tlv sub_tlv[0];
   };
*/
type PP2SSL struct {
	Client uint8 // The <client> field is made of a bit field from the following values,
	// indicating which element is present: PP2_BITFIELD_CLIENT_SSL,
	// PP2_BITFIELD_CLIENT_CERT_CONN, PP2_BITFIELD_CLIENT_CERT_SESS
	Verify uint32 // Verify will be zero if the client presented a certificate
	// and it was successfully verified, and non-zero otherwise.
	TLV []proxyproto.TLV
}

// Verified is true if the client presented a certificate and it was successfully verified
func (s PP2SSL) Verified() bool {
	return s.Verify == 0
}

// ClientSSL indicates that the client connected over SSL/TLS.  When true, SSLVersion will return the version.
func (s PP2SSL) ClientSSL() bool {
	return s.Client&PP2_BITFIELD_CLIENT_SSL == PP2_BITFIELD_CLIENT_SSL
}

// ClientCertConn indicates that the client provided a certificate over the current connection.
func (s PP2SSL) ClientCertConn() bool {
	return s.Client&PP2_BITFIELD_CLIENT_CERT_CONN == PP2_BITFIELD_CLIENT_CERT_CONN
}

// ClientCertSess indicates that the client provided a certificate at least once over the TLS session this
// connection belongs to.
func (s PP2SSL) ClientCertSess() bool {
	return s.Client&PP2_BITFIELD_CLIENT_CERT_SESS == PP2_BITFIELD_CLIENT_CERT_SESS
}

// SSLVersion returns the US-ASCII string representation of the TLS version and whether that extension exists.
func (s PP2SSL) SSLVersion() (string, bool) {
	for _, tlv := range s.TLV {
		if tlv.Type == proxyproto.PP2_SUBTYPE_SSL_VERSION {
			return string(tlv.Value), true
		}
	}
	return "", false
}

// Marshal formats the PP2SSL structure as a TLV.
func (s PP2SSL) Marshal() (proxyproto.TLV, error) {
	v := make([]byte, 5)
	v[0] = s.Client
	binary.BigEndian.PutUint32(v[1:5], s.Verify)

	tlvs, err := proxyproto.JoinTLVs(s.TLV)
	if err != nil {
		return proxyproto.TLV{}, err
	}
	v = append(v, tlvs...)

	return proxyproto.TLV{
		Type:  proxyproto.PP2_TYPE_SSL,
		Value: v,
	}, nil
}

// ClientCN returns the string representation (in UTF8) of the Common Name field (OID: 2.5.4.3) of the client
// certificate's Distinguished Name and whether that extension exists.
func (s PP2SSL) ClientCN() (string, bool) {
	for _, tlv := range s.TLV {
		if tlv.Type == proxyproto.PP2_SUBTYPE_SSL_CN {
			return string(tlv.Value), true
		}
	}
	return "", false
}

// SSLType is true if the TLV is type SSL
func IsSSL(t proxyproto.TLV) bool {
	return t.Type == proxyproto.PP2_TYPE_SSL && len(t.Value) >= tlvSSLMinLen
}

// SSL returns the pp2_tlv_ssl from section 2.2.5 or errors with ErrIncompatibleTLV or ErrMalformedTLV
func SSL(t proxyproto.TLV) (PP2SSL, error) {
	ssl := PP2SSL{}
	if !IsSSL(t) {
		return ssl, proxyproto.ErrIncompatibleTLV
	}
	if len(t.Value) < tlvSSLMinLen {
		return ssl, proxyproto.ErrMalformedTLV
	}
	ssl.Client = t.Value[0]
	ssl.Verify = binary.BigEndian.Uint32(t.Value[1:5])
	var err error
	ssl.TLV, err = proxyproto.SplitTLVs(t.Value[5:])
	if err != nil {
		return PP2SSL{}, err
	}
	versionFound := !ssl.ClientSSL()
	for _, tlv := range ssl.TLV {
		switch tlv.Type {
		case proxyproto.PP2_SUBTYPE_SSL_VERSION:
			/*
				The PP2_CLIENT_SSL flag indicates that the client connected over SSL/TLS. When
				this field is present, the US-ASCII string representation of the TLS version is
				appended at the end of the field in the TLV format using the type
				PP2_SUBTYPE_SSL_VERSION.
			*/
			if len(tlv.Value) == 0 || !isASCII(tlv.Value) {
				return PP2SSL{}, proxyproto.ErrMalformedTLV
			}
			versionFound = true
		case proxyproto.PP2_SUBTYPE_SSL_CN:
			/*
				In all cases, the string representation (in UTF8) of the Common Name field
				(OID: 2.5.4.3) of the client certificate's Distinguished Name, is appended
				using the TLV format and the type PP2_SUBTYPE_SSL_CN. E.g. "example.com".
			*/
			if len(tlv.Value) == 0 || !utf8.Valid(tlv.Value) {
				return PP2SSL{}, proxyproto.ErrMalformedTLV
			}
		}
	}
	if !versionFound {
		return PP2SSL{}, proxyproto.ErrMalformedTLV
	}
	return ssl, nil
}

// SSL returns the first PP2SSL if it exists and is well formed as well as bool indicating if it was found.
func FindSSL(tlvs []proxyproto.TLV) (PP2SSL, bool) {
	for _, t := range tlvs {
		if ssl, err := SSL(t); err == nil {
			return ssl, true
		}
	}
	return PP2SSL{}, false
}

// isASCII checks whether a byte slice has all characters that fit in the ascii character set, including the null byte.
func isASCII(b []byte) bool {
	for _, c := range b {
		if c > unicode.MaxASCII {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package utils

import (
"github.com/gonum/matrix/mat64"
"math"
)

// This package contains all the mathematical computations needed by the regression algorithms

func DotProduct(X *mat64.Dense, Y *mat64.Vector) *mat64.Dense {

	l, _ := X.Dims()
	P := mat64.NewDense(l, 1, nil)
	for i := 0; i < l; i++ {
		P.Set(i, 0, X.At(i, 0)*Y.At(i, 0))
	}
	return P
}

// Apply simoid function on every element of the matrix
func SigmoidMatrix(X *mat64.Dense) ( *mat64.Dense) {
	sigmat := &mat64.Dense{}
	sigmat.Apply(func(i, j int, v float64) float64 { return sigmoid(v) },X)
	return sigmat
}

func Subtract(x float64, A * mat64.Dense) (*mat64.Dense)  {
	S := &mat64.Dense{}
	S.Apply(func(i, j int, v float64) float64 { return x-v }, A)
	return S
}

func Log(X *mat64.Dense) (*mat64.Dense) {
	S := &mat64.Dense{}
	S.Apply(func(i, j int, v float64) float64 { return math.Log(v) }, X)
	return S
}

func sigmoid(x float64) (z float64) {
	return(1/(1+math.Exp(-x)))
}

func Accuracy(y, pred []float64) float64 {
	acc := 0.0
	for index, value := range y {
		if value == pred[index]{
			acc++
		}
	}
	return acc/float64(len(y)
}
<commit_msg>fixing algebra<commit_after>package utils

import (
"github.com/gonum/matrix/mat64"
"math"
)

// This package contains all the mathematical computations needed by the regression algorithms

func DotProduct(X *mat64.Dense, Y *mat64.Vector) *mat64.Dense {

	l, _ := X.Dims()
	P := mat64.NewDense(l, 1, nil)
	for i := 0; i < l; i++ {
		P.Set(i, 0, X.At(i, 0)*Y.At(i, 0))
	}
	return P
}

// Apply simoid function on every element of the matrix
func SigmoidMatrix(X *mat64.Dense) ( *mat64.Dense) {
	sigmat := &mat64.Dense{}
	sigmat.Apply(func(i, j int, v float64) float64 { return sigmoid(v) },X)
	return sigmat
}

func Subtract(x float64, A * mat64.Dense) (*mat64.Dense)  {
	S := &mat64.Dense{}
	S.Apply(func(i, j int, v float64) float64 { return x-v }, A)
	return S
}

func Log(X *mat64.Dense) (*mat64.Dense) {
	S := &mat64.Dense{}
	S.Apply(func(i, j int, v float64) float64 { return math.Log(v) }, X)
	return S
}

func sigmoid(x float64) (z float64) {
	return(1/(1+math.Exp(-x)))
}

func Accuracy(y, pred []float64) float64 {
	acc := 0.0
	for index, value := range y {
		if value == pred[index]{
			acc++
		}
	}
	return acc/float64(len(y))
}<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"io/ioutil"
	"runtime"
	"text/template"

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

var src = `<?xml version="1.0" standalone="no"?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
	<name replace-wildcards="yes">{{.Serial}}</name>
	{{if .Paired}}
	<service>
		<type>_ninja-homecloud-rest._tcp</type>
		<port>80</port>
		<txt-record>ninja.sphere.user_id={{.User}}</txt-record>
		<txt-record>ninja.sphere.node_id={{.Serial}}</txt-record>
		<txt-record>ninja.sphere.master={{.Master}}</txt-record>
	</service>
	<service>
		<type>_ninja-homecloud-mqtt._tcp</type>
		<port>1883</port>
		<txt-record>ninja.sphere.user_id={{.User}}</txt-record>
		<txt-record>ninja.sphere.node_id={{.Serial}}</txt-record>
		<txt-record>ninja.sphere.master={{.Master}}</txt-record>
	</service>
	{{else}}
	<service>
		<type>_ninja-serup-assistant-rest._tcp</type>
		<port>8888</port>
		<txt-record>ninja.sphere.node_id=%s</txt-record>
	</service>
	{{end}}
</service-group>`

func updateSphereAvahiService(isPaired, isMaster bool) error {

	tmpl, err := template.New("avahi").Parse(src)

	if err != nil {
		return err
	}

	serviceDefinition := new(bytes.Buffer)

	err = tmpl.Execute(serviceDefinition, map[string]interface{}{
		"Serial": config.Serial(),
		"Master": isMaster,
		"Paired": isPaired,
		"User":   config.String("", "userId"),
	})

	if err != nil {
		return err
	}

	log.Debugf("Saving service definition", serviceDefinition.String())

	if runtime.GOOS != "linux" {
		log.Warningf("Avahi service definition is not being saved, as platform != linux")
		return nil
	}

	return ioutil.WriteFile("/data/etc/avahi/services/ninjasphere.service", []byte(serviceDefinition.String()), 0644)
}
<commit_msg>Bad spelling.<commit_after>package client

import (
	"bytes"
	"io/ioutil"
	"runtime"
	"text/template"

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

var src = `<?xml version="1.0" standalone="no"?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
	<name replace-wildcards="yes">{{.Serial}}</name>
	{{if .Paired}}
	<service>
		<type>_ninja-homecloud-rest._tcp</type>
		<port>80</port>
		<txt-record>ninja.sphere.user_id={{.User}}</txt-record>
		<txt-record>ninja.sphere.node_id={{.Serial}}</txt-record>
		<txt-record>ninja.sphere.master={{.Master}}</txt-record>
	</service>
	<service>
		<type>_ninja-homecloud-mqtt._tcp</type>
		<port>1883</port>
		<txt-record>ninja.sphere.user_id={{.User}}</txt-record>
		<txt-record>ninja.sphere.node_id={{.Serial}}</txt-record>
		<txt-record>ninja.sphere.master={{.Master}}</txt-record>
	</service>
	{{else}}
	<service>
		<type>_ninja-setup-assistant-rest._tcp</type>
		<port>8888</port>
		<txt-record>ninja.sphere.node_id=%s</txt-record>
	</service>
	{{end}}
</service-group>`

func updateSphereAvahiService(isPaired, isMaster bool) error {

	tmpl, err := template.New("avahi").Parse(src)

	if err != nil {
		return err
	}

	serviceDefinition := new(bytes.Buffer)

	err = tmpl.Execute(serviceDefinition, map[string]interface{}{
		"Serial": config.Serial(),
		"Master": isMaster,
		"Paired": isPaired,
		"User":   config.String("", "userId"),
	})

	if err != nil {
		return err
	}

	log.Debugf("Saving service definition", serviceDefinition.String())

	if runtime.GOOS != "linux" {
		log.Warningf("Avahi service definition is not being saved, as platform != linux")
		return nil
	}

	return ioutil.WriteFile("/data/etc/avahi/services/ninjasphere.service", []byte(serviceDefinition.String()), 0644)
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"regexp"
	"sync"
	"time"
)

// a replyWaiter is used when a plugin is waiting for a reply
type replyWaiter struct {
	re           *regexp.Regexp // The regular expression the reply needs to match
	replyChannel chan reply     // The channel to send the reply to when it is received
}

// a reply matcher is used as the key in the replys map
type replyMatcher struct {
	user, channel string // Only one reply at a time can be requested for a given user/channel combination
}

// a reply is sent over the replyWaiter channel when a user replies
type reply struct {
	matched     bool   // true if the regex matched
	interrupted bool   // true if the user issued another command
	rep         string // text of the reply
}

var replies = struct {
	m map[replyMatcher]replyWaiter
	sync.Mutex
}{
	make(map[replyMatcher]replyWaiter),
	sync.Mutex{},
}

type stockReply struct {
	repTag   string
	repRegex string
}

var stockRepliesRegex = `^[A-Z]`
var stockRepliesRe *regexp.Regexp

var stockReplies = make(map[string]*regexp.Regexp)

var stockReplyList = []stockReply{
	{"Email", `[\w-\.]+@(?:[\w-]+\.)+[\w-]{2,4}`},
	{"Domain", `(?:[\w-]+\.)+[\w-]{2,4}`},
	{"OTP", `\d{6}`},
	//	{ "IPaddr", `[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}` }
	{"IPaddr", `(?:(?:0|1[0-9]{0,2}|2[0-9]?|2[0-4][0-9]|25[0-5]|[3-9][0-9]?)\.){3}(?:0|1[0-9]{0,2}|2[0-9]?|2[0-4][0-9]|25[0-5]|[3-9][0-9]?)`},
	{"SimpleString", `[-\w .,_'"?!]+`},
	{"YesNo", `(?i:yes|no|Y|N)`},
}

func init() {
	stockRepliesRe = regexp.MustCompile(stockRepliesRegex)
	for _, sr := range stockReplyList {
		stockReplies[sr.repTag] = regexp.MustCompile(`^\s*` + sr.repRegex + `\s*$`)
	}
}

// WaitForReply lets a plugin temporarily register a regex for a reply
// expected to an multi-step command when the robot needs more info. If
// the regular expression matches, it returns the matched text and
// RetVal = Ok.
// If the regular expression doesn't match, it returns an empty string
// with one of the following RetVals:
//	Interrupted - the user issued a new command that ran
//  UseDefaultValue - user supplied a single "=", meaning "use the default value"
//	ReplyNotMatched - didn't successfully match for any reason
//	MatcherNotFound - the regexId didn't correspond to a valid regex
//	TimeoutExpired - the user didn't respond within the timeout window given
//
// Plugin authors can define regex's for regexId's in the plugin's JSON config,
// with the restriction that the regexId must start with a lowercase letter.
// A pre-definied regex from the following list can also be used:
// 	Email
//	Domain - an alpha-numeric domain name
//	OTP - a 6-digit one-time password code
//	IPAddr
//	SimpleString - Characters commonly found in most english sentences, doesn't
//    include special characters like @, {, etc.
//	YesNo
func (r *Robot) WaitForReply(regexID string, timeout int) (string, RetVal) {
	matcher := replyMatcher{
		user:    r.User,
		channel: r.Channel,
	}
	var rep replyWaiter
	pluginlist.RLock()
	plugin := pluginlist.p[plugIDmap[r.pluginID]]
	pluginlist.RUnlock()
	if stockRepliesRe.MatchString(regexID) {
		rep.re = stockReplies[regexID]
	} else {
		for _, matcher := range plugin.ReplyMatchers {
			if matcher.Label == regexID {
				rep.re = matcher.re
				break
			} else if matcher.Command == regexID {
				rep.re = matcher.re
			}
		}
	}
	if rep.re == nil {
		r.Log(Error, fmt.Sprintf("Unable to resolve a reply matcher for plugin %s, regexID %s", plugin.name, regexID))
		return "", MatcherNotFound
	}
	rep.replyChannel = make(chan reply)

	replies.Lock()
	// See if there's already a continuation in progress for this Robot:user,channel,
	_, exists := replies.m[matcher]
	if exists { // this should never happen, and should eventually be removed
		panic(fmt.Sprintf("stale replyWaiter found for user %s in channel %s", r.User, r.Channel))
	}
	replies.m[matcher] = rep
	replies.Unlock()
	r.Log(Trace, fmt.Sprintf("Adding matcher to replies: %q", matcher))
	// Start a goroutine to delete the reply request if it still exists after a minute.
	// If it's matched in the meantime, it should get deleted at that point.
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		Log(Warn, fmt.Sprintf("Timed out waiting for a reply to regex \"%s\" in channel: %s", regexID, r.Channel))
		replies.Lock()
		// reply timed out, free up this matcher for later reply requests
		delete(replies.m, matcher)
		replies.Unlock()
		// matched=false, timedOut=true
		return "", TimeoutExpired
	case replied := <-rep.replyChannel:
		if replied.interrupted {
			return "", Interrupted
		}
		// Note: the replies.m[] entry is deleted in handleMessage
		if !replied.matched {
			if replied.rep == "=" {
				return "", UseDefaultValue
			} else {
				return "", ReplyNotMatched
			}
		} else {
			return replied.rep, Ok
		}
	}
}

// WaitForReplyRegex is identical to WaitForReply except that the first argument is
// the regex to compile and use. If the regex doesn't compile an error will be
// logged and ("", MatcherNotFound) will be returned.
func (r *Robot) WaitForReplyRegex(regex string, timeout int) (string, RetVal) {
	matcher := replyMatcher{
		user:    r.User,
		channel: r.Channel,
	}
	var rep replyWaiter
	re, err := regexp.Compile(regex)
	if err != nil {
		r.Log(Error, fmt.Sprintf("Unable to compile regex \"%s\" in WaitForReplyRegex", regex))
		return "", MatcherNotFound
	}
	rep.re = re
	rep.replyChannel = make(chan reply)

	replies.Lock()
	// See if there's already a continuation in progress for this Robot:user,channel,
	_, exists := replies.m[matcher]
	if exists { // this should never happen, and should eventually be removed
		panic(fmt.Sprintf("stale replyWaiter found for user %s in channel %s", r.User, r.Channel))
	}
	replies.m[matcher] = rep
	replies.Unlock()
	r.Log(Trace, fmt.Sprintf("Added matcher to replies: %q", matcher))
	// Start a goroutine to delete the reply request if it still exists after a minute.
	// If it's matched in the meantime, it should get deleted at that point.
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		Log(Warn, fmt.Sprintf("Timed out waiting for a reply to custom regex \"%s\" in channel: %s", regex, r.Channel))
		replies.Lock()
		// reply timed out, free up this matcher for later reply requests
		delete(replies.m, matcher)
		replies.Unlock()
		// matched=false, timedOut=true
		return "", TimeoutExpired
	case replied, _ := <-rep.replyChannel:
		if replied.interrupted {
			return "", Interrupted
		}
		// Note: the replies.m[] entry is deleted in handleMessage
		if !replied.matched {
			if replied.rep == "=" {
				return "", UseDefaultValue
			} else {
				return "", ReplyNotMatched
			}
		} else {
			return replied.rep, Ok
		}
	}
}
<commit_msg>Fix race condition in waitreply<commit_after>package bot

import (
	"fmt"
	"regexp"
	"sync"
	"time"
)

// a replyWaiter is used when a plugin is waiting for a reply
type replyWaiter struct {
	re           *regexp.Regexp // The regular expression the reply needs to match
	replyChannel chan reply     // The channel to send the reply to when it is received
}

// a reply matcher is used as the key in the replys map
type replyMatcher struct {
	user, channel string // Only one reply at a time can be requested for a given user/channel combination
}

// a reply is sent over the replyWaiter channel when a user replies
type reply struct {
	matched     bool   // true if the regex matched
	interrupted bool   // true if the user issued another command
	rep         string // text of the reply
}

var replies = struct {
	m map[replyMatcher]replyWaiter
	sync.Mutex
}{
	make(map[replyMatcher]replyWaiter),
	sync.Mutex{},
}

type stockReply struct {
	repTag   string
	repRegex string
}

var stockRepliesRegex = `^[A-Z]`
var stockRepliesRe *regexp.Regexp

var stockReplies = make(map[string]*regexp.Regexp)

var stockReplyList = []stockReply{
	{"Email", `[\w-\.]+@(?:[\w-]+\.)+[\w-]{2,4}`},
	{"Domain", `(?:[\w-]+\.)+[\w-]{2,4}`},
	{"OTP", `\d{6}`},
	//	{ "IPaddr", `[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}` }
	{"IPaddr", `(?:(?:0|1[0-9]{0,2}|2[0-9]?|2[0-4][0-9]|25[0-5]|[3-9][0-9]?)\.){3}(?:0|1[0-9]{0,2}|2[0-9]?|2[0-4][0-9]|25[0-5]|[3-9][0-9]?)`},
	{"SimpleString", `[-\w .,_'"?!]+`},
	{"YesNo", `(?i:yes|no|Y|N)`},
}

func init() {
	stockRepliesRe = regexp.MustCompile(stockRepliesRegex)
	for _, sr := range stockReplyList {
		stockReplies[sr.repTag] = regexp.MustCompile(`^\s*` + sr.repRegex + `\s*$`)
	}
}

// WaitForReply lets a plugin temporarily register a regex for a reply
// expected to an multi-step command when the robot needs more info. If
// the regular expression matches, it returns the matched text and
// RetVal = Ok.
// If the regular expression doesn't match, it returns an empty string
// with one of the following RetVals:
//	Interrupted - the user issued a new command that ran
//  UseDefaultValue - user supplied a single "=", meaning "use the default value"
//	ReplyNotMatched - didn't successfully match for any reason
//	MatcherNotFound - the regexId didn't correspond to a valid regex
//	TimeoutExpired - the user didn't respond within the timeout window given
//
// Plugin authors can define regex's for regexId's in the plugin's JSON config,
// with the restriction that the regexId must start with a lowercase letter.
// A pre-definied regex from the following list can also be used:
// 	Email
//	Domain - an alpha-numeric domain name
//	OTP - a 6-digit one-time password code
//	IPAddr
//	SimpleString - Characters commonly found in most english sentences, doesn't
//    include special characters like @, {, etc.
//	YesNo
func (r *Robot) WaitForReply(regexID string, timeout int) (string, RetVal) {
	matcher := replyMatcher{
		user:    r.User,
		channel: r.Channel,
	}
	var rep replyWaiter
	pluginlist.RLock()
	plugin := pluginlist.p[plugIDmap[r.pluginID]]
	pluginlist.RUnlock()
	if stockRepliesRe.MatchString(regexID) {
		rep.re = stockReplies[regexID]
	} else {
		for _, matcher := range plugin.ReplyMatchers {
			if matcher.Label == regexID {
				rep.re = matcher.re
				break
			} else if matcher.Command == regexID {
				rep.re = matcher.re
			}
		}
	}
	if rep.re == nil {
		r.Log(Error, fmt.Sprintf("Unable to resolve a reply matcher for plugin %s, regexID %s", plugin.name, regexID))
		return "", MatcherNotFound
	}
	rep.replyChannel = make(chan reply)

	replies.Lock()
	// See if there's already a continuation in progress for this Robot:user,channel,
	_, exists := replies.m[matcher]
	if exists { // this should never happen, and should eventually be removed
		panic(fmt.Sprintf("stale replyWaiter found for user %s in channel %s", r.User, r.Channel))
	}
	replies.m[matcher] = rep
	replies.Unlock()
	r.Log(Trace, fmt.Sprintf("Adding matcher to replies: %q", matcher))
	var replied reply
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		Log(Warn, fmt.Sprintf("Timed out waiting for a reply to regex \"%s\" in channel: %s", regexID, r.Channel))
		replies.Lock()
		_, found := replies.m[matcher]
		if found {
			// reply timed out, free up this matcher for later reply requests
			delete(replies.m, matcher)
			replies.Unlock()
		} else { // race: we got a reply at the timeout deadline
			replies.Unlock()
			replied = <-rep.replyChannel
		}
		// matched=false, timedOut=true
		return "", TimeoutExpired
	case replied = <-rep.replyChannel:
	}
	if replied.interrupted {
		return "", Interrupted
	}
	// Note: the replies.m[] entry is deleted in handleMessage
	if !replied.matched {
		if replied.rep == "=" {
			return "", UseDefaultValue
		}
		return "", ReplyNotMatched
	}
	return replied.rep, Ok
}

// WaitForReplyRegex is identical to WaitForReply except that the first argument is
// the regex to compile and use. If the regex doesn't compile an error will be
// logged and ("", MatcherNotFound) will be returned.
func (r *Robot) WaitForReplyRegex(regex string, timeout int) (string, RetVal) {
	matcher := replyMatcher{
		user:    r.User,
		channel: r.Channel,
	}
	var rep replyWaiter
	re, err := regexp.Compile(regex)
	if err != nil {
		r.Log(Error, fmt.Sprintf("Unable to compile regex \"%s\" in WaitForReplyRegex", regex))
		return "", MatcherNotFound
	}
	rep.re = re
	rep.replyChannel = make(chan reply)

	replies.Lock()
	// See if there's already a continuation in progress for this Robot:user,channel,
	_, exists := replies.m[matcher]
	if exists { // this should never happen, and should eventually be removed
		panic(fmt.Sprintf("stale replyWaiter found for user %s in channel %s", r.User, r.Channel))
	}
	replies.m[matcher] = rep
	replies.Unlock()
	r.Log(Trace, fmt.Sprintf("Added matcher to replies: %q", matcher))
	var replied reply
	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		Log(Warn, fmt.Sprintf("Timed out waiting for a reply to custom regex \"%s\" in channel: %s", regex, r.Channel))
		replies.Lock()
		_, found := replies.m[matcher]
		if found {
			// reply timed out, free up this matcher for later reply requests
			delete(replies.m, matcher)
			replies.Unlock()
		} else { // race: we got a reply at the timeout deadline, dispatch got the lock first
			replies.Unlock()
			replied = <-rep.replyChannel
		}
		// matched=false, timedOut=true
		return "", TimeoutExpired
	case replied = <-rep.replyChannel:
	}
	if replied.interrupted {
		return "", Interrupted
	}
	// Note: the replies.m[] entry is deleted in handleMessage
	if !replied.matched {
		if replied.rep == "=" {
			return "", UseDefaultValue
		}
		return "", ReplyNotMatched
	}
	return replied.rep, Ok
}
<|endoftext|>