<commit_before>package dag

import (
	"fmt"
	"strings"
	"testing"
)

func TestGraph_empty(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphEmptyStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_basic(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphBasicStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_remove(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))
	g.Remove(3)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphRemoveStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_replace(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 2))
	g.Connect(BasicEdge(2, 3))
	g.Replace(2, 42)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphReplaceStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_replaceSelf(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 2))
	g.Connect(BasicEdge(2, 3))
	g.Replace(2, 2)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphReplaceSelfStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

// This tests that connecting edges works based on custom Hashcode
// implementations for uniqueness.
func TestGraph_hashcode(t *testing.T) {
	var g Graph
	g.Add(&hashVertex{code: 1})
	g.Add(&hashVertex{code: 2})
	g.Add(&hashVertex{code: 3})
	g.Connect(BasicEdge(
		&hashVertex{code: 1},
		&hashVertex{code: 3}))

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphBasicStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraphHasVertex(t *testing.T) {
	var g Graph
	g.Add(1)

	if !g.HasVertex(1) {
		t.Fatal("should have 1")
	}
	if g.HasVertex(2) {
		t.Fatal("should not have 2")
	}
}

func TestGraphHasEdge(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Connect(BasicEdge(1, 2))

	if !g.HasEdge(BasicEdge(1, 2)) {
		t.Fatal("should have 1,2")
	}
	if g.HasVertex(BasicEdge(2, 3)) {
		t.Fatal("should not have 2,3")
	}
}

type hashVertex struct {
	code interface{}
}

func (v *hashVertex) Hashcode() interface{} {
	return v.code
}

func (v *hashVertex) Name() string {
	return fmt.Sprintf("%#v", v.code)
}

const testGraphBasicStr = `
1
  3
2
3
`

const testGraphEmptyStr = `
1
2
3
`

const testGraphRemoveStr = `
1
2
`

const testGraphReplaceStr = `
1
  42
3
42
  3
`

const testGraphReplaceSelfStr = `
1
  2
2
  3
3
`
<commit_msg>dag: test for EdgesFrom, EdgesTo<commit_after>package dag

import (
	"fmt"
	"strings"
	"testing"
)

func TestGraph_empty(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphEmptyStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_basic(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphBasicStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_remove(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))
	g.Remove(3)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphRemoveStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_replace(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 2))
	g.Connect(BasicEdge(2, 3))
	g.Replace(2, 42)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphReplaceStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraph_replaceSelf(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 2))
	g.Connect(BasicEdge(2, 3))
	g.Replace(2, 2)

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphReplaceSelfStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

// This tests that connecting edges works based on custom Hashcode
// implementations for uniqueness.
func TestGraph_hashcode(t *testing.T) {
	var g Graph
	g.Add(&hashVertex{code: 1})
	g.Add(&hashVertex{code: 2})
	g.Add(&hashVertex{code: 3})
	g.Connect(BasicEdge(
		&hashVertex{code: 1},
		&hashVertex{code: 3}))

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testGraphBasicStr)
	if actual != expected {
		t.Fatalf("bad: %s", actual)
	}
}

func TestGraphHasVertex(t *testing.T) {
	var g Graph
	g.Add(1)

	if !g.HasVertex(1) {
		t.Fatal("should have 1")
	}
	if g.HasVertex(2) {
		t.Fatal("should not have 2")
	}
}

func TestGraphHasEdge(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Connect(BasicEdge(1, 2))

	if !g.HasEdge(BasicEdge(1, 2)) {
		t.Fatal("should have 1,2")
	}
	if g.HasVertex(BasicEdge(2, 3)) {
		t.Fatal("should not have 2,3")
	}
}

func TestGraphEdgesFrom(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))
	g.Connect(BasicEdge(2, 3))

	edges := g.EdgesFrom(1)

	var expected Set
	expected.Add(BasicEdge(1, 3))

	var s Set
	for _, e := range edges {
		s.Add(e)
	}

	if s.Intersection(&expected).Len() != expected.Len() {
		t.Fatalf("bad: %#v", edges)
	}
}

func TestGraphEdgesTo(t *testing.T) {
	var g Graph
	g.Add(1)
	g.Add(2)
	g.Add(3)
	g.Connect(BasicEdge(1, 3))
	g.Connect(BasicEdge(1, 2))

	edges := g.EdgesTo(3)

	var expected Set
	expected.Add(BasicEdge(1, 3))

	var s Set
	for _, e := range edges {
		s.Add(e)
	}

	if s.Intersection(&expected).Len() != expected.Len() {
		t.Fatalf("bad: %#v", edges)
	}
}

type hashVertex struct {
	code interface{}
}

func (v *hashVertex) Hashcode() interface{} {
	return v.code
}

func (v *hashVertex) Name() string {
	return fmt.Sprintf("%#v", v.code)
}

const testGraphBasicStr = `
1
  3
2
3
`

const testGraphEmptyStr = `
1
2
3
`

const testGraphRemoveStr = `
1
2
`

const testGraphReplaceStr = `
1
  42
3
42
  3
`

const testGraphReplaceSelfStr = `
1
  2
2
  3
3
`
<|endoftext|>


<commit_before>package dagger

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"path"
	"sync"
	"time"

	"github.com/natefinch/pie"
)

type statefulComputation struct {
	streamID     StreamID
	plugin       ComputationPlugin
	groupHandler GroupHandler
	linearizer   *Linearizer
	persister    TaskPersister
	lwmTracker   LWMTracker
	dispatcher   *StreamDispatcher
	stopCh       chan struct{}

	sync.RWMutex // a reader/writer lock for blocking new records on sync request
	initialized  bool
}

func newStatefulComputation(streamID StreamID, coordinator Coordinator,
	persister Persister, plugin ComputationPlugin) (*statefulComputation, error) {
	groupHandler, err := coordinator.JoinGroup(streamID)
	if err != nil {
		return nil, err
	}
	stopCh := make(chan struct{})

	lwmTracker := NewLWMTracker()
	// notify both LWM tracker and persister when a record is successfuly sent
	// multiSentTracker := MultiSentTracker{[]SentTracker{lwmTracker, persister}}

	linearizer := NewLinearizer(streamID, persister, lwmTracker)
	// bufferedDispatcher := StartBufferedDispatcher(streamID, dispatcher, multiSentTracker, lwmTracker, stopCh)
	dispatcher := NewStreamDispatcher(streamID, coordinator, persister, lwmTracker, groupHandler)

	computation := &statefulComputation{
		streamID:     streamID,
		plugin:       plugin,
		groupHandler: groupHandler,
		persister:    persister,
		lwmTracker:   lwmTracker,
		linearizer:   linearizer,
		dispatcher:   dispatcher,
		stopCh:       stopCh,
		RWMutex:      sync.RWMutex{},
		initialized:  false,
	}

	linearizer.SetProcessor(computation)

	return computation, nil
}

func (comp *statefulComputation) GetSnapshot() ([]byte, error) {
	log.Println("[computations] trying to acquire sync lock...")
	comp.Lock()
	log.Println("[computations] ... sync lock acquired!")
	defer comp.Unlock()
	snapshot := make(map[string][]byte)
	persisterSnapshot, err := comp.persister.GetSnapshot(comp.streamID)
	if err != nil {
		return nil, errors.New("stateful computation: " + err.Error())
	}
	pluginSnapshot, err := comp.plugin.GetSnapshot()
	if err != nil {
		return nil, errors.New("stateful computation: " + err.Error())
	}
	snapshot["persister"] = persisterSnapshot
	snapshot["plugin"] = pluginSnapshot
	return json.Marshal(snapshot)
}

func (comp *statefulComputation) Sync() (Timestamp, error) {
	comp.Lock()
	defer comp.Unlock()
	var from Timestamp
	for !comp.initialized {
		log.Printf("[computations] Computation %s not initialized, syncing with group",
			comp.streamID)
		areWeLeader, currentLeader, err := comp.groupHandler.GetStatus()
		if err != nil {
			return from, err
		}
		if currentLeader == "" {
			// wait until a leader is chosen
			log.Println("[computations] Leader of ", comp.streamID, "not yet chosen, waiting")
			time.Sleep(time.Second) // FIXME do this as a blocking call
			continue
		}
		if !areWeLeader {
			if err != nil {
				return from, err
			}
			leaderHandler, err := newMasterHandler(currentLeader)
			if err != nil {
				return from, fmt.Errorf("[computations] Error creating master handler: %s", err)
			}
			snapshot, err := leaderHandler.Sync(comp.streamID)
			if err != nil {
				return from, fmt.Errorf("[computations] Error syncing computation with master: %s", err)
			}

			snapshotMap := make(map[string][]byte)
			err = json.Unmarshal(snapshot, &snapshotMap)
			if err != nil {
				return from, fmt.Errorf("cannot unmarshal snapshot: %s:", err.Error())
			}

			err = comp.plugin.ApplySnapshot(snapshotMap["plugin"])
			if err != nil {
				return from, fmt.Errorf("[computations] Error setting computation plugin state: %s", err)
			}
			err = comp.persister.ApplySnapshot(comp.streamID, snapshotMap["persister"])
			if err != nil {
				return from, fmt.Errorf("[computations] Error applying computation snapshot: %s", err)
			}

			from, err = comp.persister.GetLastTimestamp(comp.streamID)
			if err != nil {
				return from, fmt.Errorf("[computations] error getting last timestamp")
			}
			// add one nanosecond so we don't take the last processed record again
			from++
			comp.linearizer.SetStartLWM(from)
			// // recreate deduplicator from newest received info
			// deduplicator, err := NewDeduplicator(comp.streamID, comp.persister)
			// if err != nil {
			// 	return fmt.Errorf("[computations] Error recreating deduplicator after sync: %s", err)
			// }
			// comp.deduplicator = deduplicator
		}
		comp.initialized = true
	}

	return from, nil
}

func (comp *statefulComputation) Run(errc chan error) {
	go comp.dispatcher.Run(errc)
	go comp.linearizer.Run(errc)
}

func (comp *statefulComputation) Stop() {
	comp.linearizer.Stop()
	comp.plugin.Stop()
	comp.dispatcher.Stop()
}

func (comp *statefulComputation) ProcessRecord(r *Record) error {
	log.Println("[linearizer] processing", r)
	err := comp.linearizer.ProcessRecord(r)
	if err != nil {
		return err
	}
	return nil
}

func (comp *statefulComputation) ProcessRecordLinearized(t *Record) error {
	// acquire a lock, so we wait in case there's synchronization with
	// a slave going on
	comp.Lock()
	defer comp.Unlock()

	// send it to the plugin for processing, but through the linearizer so
	// the plugin receives the records in order
	response, err := comp.plugin.SubmitRecord(t)
	if err != nil {
		log.Println("ERROR:", err)
		return err
	}

	// persist info about received and produced records
	err = comp.persister.CommitComputation(comp.streamID, t, response.Records)
	if err != nil {
		return err
	}

	areWeLeader, _, err := comp.groupHandler.GetStatus()
	if err != nil {
		return err
	}

	if areWeLeader {
		log.Println("WE ARE LEADER")
		comp.lwmTracker.BeforeDispatching(response.Records)
		// send to asynchronous dispatcher and return immediately
		ProcessMultipleRecords(comp.dispatcher, response.Records)
	} else {
		log.Println("WE ARE NOT LEADER", t)
	}
	// don't send downstream if we're not the leader of our group
	return nil
}

// StartComputationPlugin starts the plugin process
func StartComputationPlugin(name string, compID StreamID) (ComputationPlugin, error) {
	log.Printf("[computations] Launching computation plugin '%s'", name)
	path := path.Join(os.Getenv("DAGGER_PLUGIN_PATH"), "computation-"+name)
	client, err := pie.StartProviderCodec(jsonrpc.NewClientCodec,
		os.Stderr,
		path,
	)
	if err != nil {
		return nil, fmt.Errorf("Error starting plugin %s: %s", name, err)
	}
	plugin := &computationPlugin{
		name:   name,
		compID: compID,
		client: client,
	}
	return plugin, nil
}

// ComputationPlugin handles the running and interacting with a computation
// plugin process
type ComputationPlugin interface {
	GetInfo(definition string) (*ComputationPluginInfo, error)
	SubmitRecord(t *Record) (*ComputationPluginResponse, error)
	GetSnapshot() ([]byte, error)
	ApplySnapshot([]byte) error
	Stop() error
}

type computationPlugin struct {
	client *rpc.Client
	name   string
	compID StreamID
}

func (p *computationPlugin) GetInfo(definition string) (*ComputationPluginInfo, error) {
	var result ComputationPluginInfo
	err := p.client.Call("Computation.GetInfo", definition, &result)
	return &result, err
}

func (p *computationPlugin) GetSnapshot() ([]byte, error) {
	var result []byte
	err := p.client.Call("Computation.GetState", struct{}{}, &result)
	if err != nil {
		return nil, errors.New("plugin snapshot: " + err.Error())
	}
	return result, err
}

func (p *computationPlugin) ApplySnapshot(state []byte) error {
	var result string
	err := p.client.Call("Computation.SetState", state, &result)
	return err
}

func (p *computationPlugin) SubmitRecord(r *Record) (*ComputationPluginResponse, error) {
	log.Println("[plugin] processing", r)
	var result ComputationPluginResponse
	err := p.client.Call("Computation.SubmitRecord", r, &result)
	if err != nil {
		return nil, fmt.Errorf("Error submitting record to plugin %s: %s",
			p.name, err)
	}
	for _, r := range result.Records {
		r.StreamID = p.compID
	}
	return &result, err
}

func (p *computationPlugin) Stop() error {
	log.Println("[plugin] stopping", p.name)
	return p.client.Close()
}

type masterHandler struct {
	client *rpc.Client
}

func newMasterHandler(addr string) (*masterHandler, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	client := jsonrpc.NewClient(conn)
	return &masterHandler{client}, nil
}

func (mh *masterHandler) Sync(compID StreamID) ([]byte, error) {
	var reply []byte
	log.Println("[computations] issuing a sync request for computation", compID)
	err := mh.client.Call("RPCHandler.Sync", compID, &reply)
	return reply, err
}
<commit_msg>timestamp doesnt exist when no samples have been processed by computation yet<commit_after>package dagger

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"path"
	"sync"
	"time"

	"github.com/natefinch/pie"
)

type statefulComputation struct {
	streamID     StreamID
	plugin       ComputationPlugin
	groupHandler GroupHandler
	linearizer   *Linearizer
	persister    TaskPersister
	lwmTracker   LWMTracker
	dispatcher   *StreamDispatcher
	stopCh       chan struct{}

	sync.RWMutex // a reader/writer lock for blocking new records on sync request
	initialized  bool
}

func newStatefulComputation(streamID StreamID, coordinator Coordinator,
	persister Persister, plugin ComputationPlugin) (*statefulComputation, error) {
	groupHandler, err := coordinator.JoinGroup(streamID)
	if err != nil {
		return nil, err
	}
	stopCh := make(chan struct{})

	lwmTracker := NewLWMTracker()
	// notify both LWM tracker and persister when a record is successfuly sent
	// multiSentTracker := MultiSentTracker{[]SentTracker{lwmTracker, persister}}

	linearizer := NewLinearizer(streamID, persister, lwmTracker)
	// bufferedDispatcher := StartBufferedDispatcher(streamID, dispatcher, multiSentTracker, lwmTracker, stopCh)
	dispatcher := NewStreamDispatcher(streamID, coordinator, persister, lwmTracker, groupHandler)

	computation := &statefulComputation{
		streamID:     streamID,
		plugin:       plugin,
		groupHandler: groupHandler,
		persister:    persister,
		lwmTracker:   lwmTracker,
		linearizer:   linearizer,
		dispatcher:   dispatcher,
		stopCh:       stopCh,
		RWMutex:      sync.RWMutex{},
		initialized:  false,
	}

	linearizer.SetProcessor(computation)

	return computation, nil
}

func (comp *statefulComputation) GetSnapshot() ([]byte, error) {
	log.Println("[computations] trying to acquire sync lock...")
	comp.Lock()
	log.Println("[computations] ... sync lock acquired!")
	defer comp.Unlock()
	snapshot := make(map[string][]byte)
	persisterSnapshot, err := comp.persister.GetSnapshot(comp.streamID)
	if err != nil {
		return nil, errors.New("stateful computation: " + err.Error())
	}
	pluginSnapshot, err := comp.plugin.GetSnapshot()
	if err != nil {
		return nil, errors.New("stateful computation: " + err.Error())
	}
	snapshot["persister"] = persisterSnapshot
	snapshot["plugin"] = pluginSnapshot
	return json.Marshal(snapshot)
}

func (comp *statefulComputation) Sync() (Timestamp, error) {
	comp.Lock()
	defer comp.Unlock()
	var from Timestamp
	for !comp.initialized {
		log.Printf("[computations] Computation %s not initialized, syncing with group",
			comp.streamID)
		areWeLeader, currentLeader, err := comp.groupHandler.GetStatus()
		if err != nil {
			return from, err
		}
		if currentLeader == "" {
			// wait until a leader is chosen
			log.Println("[computations] Leader of ", comp.streamID, "not yet chosen, waiting")
			time.Sleep(time.Second) // FIXME do this as a blocking call
			continue
		}
		if !areWeLeader {
			if err != nil {
				return from, err
			}
			leaderHandler, err := newMasterHandler(currentLeader)
			if err != nil {
				return from, fmt.Errorf("[computations] Error creating master handler: %s", err)
			}
			snapshot, err := leaderHandler.Sync(comp.streamID)
			if err != nil {
				return from, fmt.Errorf("[computations] Error syncing computation with master: %s", err)
			}

			snapshotMap := make(map[string][]byte)
			err = json.Unmarshal(snapshot, &snapshotMap)
			if err != nil {
				return from, fmt.Errorf("cannot unmarshal snapshot: %s:", err.Error())
			}

			err = comp.plugin.ApplySnapshot(snapshotMap["plugin"])
			if err != nil {
				return from, fmt.Errorf("[computations] Error setting computation plugin state: %s", err)
			}
			err = comp.persister.ApplySnapshot(comp.streamID, snapshotMap["persister"])
			if err != nil {
				return from, fmt.Errorf("[computations] Error applying computation snapshot: %s", err)
			}

			from, err = comp.persister.GetLastTimestamp(comp.streamID)
			if err != nil {
				from = Timestamp(0)
				// return from, fmt.Errorf("[computations] error getting last timestamp")
			}
			// add one nanosecond so we don't take the last processed record again
			from++
			comp.linearizer.SetStartLWM(from)
			// // recreate deduplicator from newest received info
			// deduplicator, err := NewDeduplicator(comp.streamID, comp.persister)
			// if err != nil {
			// 	return fmt.Errorf("[computations] Error recreating deduplicator after sync: %s", err)
			// }
			// comp.deduplicator = deduplicator
		}
		comp.initialized = true
	}

	return from, nil
}

func (comp *statefulComputation) Run(errc chan error) {
	go comp.dispatcher.Run(errc)
	go comp.linearizer.Run(errc)
}

func (comp *statefulComputation) Stop() {
	comp.linearizer.Stop()
	comp.plugin.Stop()
	comp.dispatcher.Stop()
}

func (comp *statefulComputation) ProcessRecord(r *Record) error {
	log.Println("[linearizer] processing", r)
	err := comp.linearizer.ProcessRecord(r)
	if err != nil {
		return err
	}
	return nil
}

func (comp *statefulComputation) ProcessRecordLinearized(t *Record) error {
	// acquire a lock, so we wait in case there's synchronization with
	// a slave going on
	comp.Lock()
	defer comp.Unlock()

	// send it to the plugin for processing, but through the linearizer so
	// the plugin receives the records in order
	response, err := comp.plugin.SubmitRecord(t)
	if err != nil {
		log.Println("ERROR:", err)
		return err
	}

	// persist info about received and produced records
	err = comp.persister.CommitComputation(comp.streamID, t, response.Records)
	if err != nil {
		return err
	}

	areWeLeader, _, err := comp.groupHandler.GetStatus()
	if err != nil {
		return err
	}

	if areWeLeader {
		log.Println("WE ARE LEADER")
		comp.lwmTracker.BeforeDispatching(response.Records)
		// send to asynchronous dispatcher and return immediately
		ProcessMultipleRecords(comp.dispatcher, response.Records)
	} else {
		log.Println("WE ARE NOT LEADER", t)
	}
	// don't send downstream if we're not the leader of our group
	return nil
}

// StartComputationPlugin starts the plugin process
func StartComputationPlugin(name string, compID StreamID) (ComputationPlugin, error) {
	log.Printf("[computations] Launching computation plugin '%s'", name)
	path := path.Join(os.Getenv("DAGGER_PLUGIN_PATH"), "computation-"+name)
	client, err := pie.StartProviderCodec(jsonrpc.NewClientCodec,
		os.Stderr,
		path,
	)
	if err != nil {
		return nil, fmt.Errorf("Error starting plugin %s: %s", name, err)
	}
	plugin := &computationPlugin{
		name:   name,
		compID: compID,
		client: client,
	}
	return plugin, nil
}

// ComputationPlugin handles the running and interacting with a computation
// plugin process
type ComputationPlugin interface {
	GetInfo(definition string) (*ComputationPluginInfo, error)
	SubmitRecord(t *Record) (*ComputationPluginResponse, error)
	GetSnapshot() ([]byte, error)
	ApplySnapshot([]byte) error
	Stop() error
}

type computationPlugin struct {
	client *rpc.Client
	name   string
	compID StreamID
}

func (p *computationPlugin) GetInfo(definition string) (*ComputationPluginInfo, error) {
	var result ComputationPluginInfo
	err := p.client.Call("Computation.GetInfo", definition, &result)
	return &result, err
}

func (p *computationPlugin) GetSnapshot() ([]byte, error) {
	var result []byte
	err := p.client.Call("Computation.GetState", struct{}{}, &result)
	if err != nil {
		return nil, errors.New("plugin snapshot: " + err.Error())
	}
	return result, err
}

func (p *computationPlugin) ApplySnapshot(state []byte) error {
	var result string
	err := p.client.Call("Computation.SetState", state, &result)
	return err
}

func (p *computationPlugin) SubmitRecord(r *Record) (*ComputationPluginResponse, error) {
	log.Println("[plugin] processing", r)
	var result ComputationPluginResponse
	err := p.client.Call("Computation.SubmitRecord", r, &result)
	if err != nil {
		return nil, fmt.Errorf("Error submitting record to plugin %s: %s",
			p.name, err)
	}
	for _, r := range result.Records {
		r.StreamID = p.compID
	}
	return &result, err
}

func (p *computationPlugin) Stop() error {
	log.Println("[plugin] stopping", p.name)
	return p.client.Close()
}

type masterHandler struct {
	client *rpc.Client
}

func newMasterHandler(addr string) (*masterHandler, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	client := jsonrpc.NewClient(conn)
	return &masterHandler{client}, nil
}

func (mh *masterHandler) Sync(compID StreamID) ([]byte, error) {
	var reply []byte
	log.Println("[computations] issuing a sync request for computation", compID)
	err := mh.client.Call("RPCHandler.Sync", compID, &reply)
	return reply, err
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package storage

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

type ResourceEncodingConfig interface {
	// StorageEncoding returns the serialization format for the resource.
	// TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds
	// For now, it returns just the GroupVersion for consistency with old behavior
	StorageEncodingFor(schema.GroupResource) (schema.GroupVersion, error)

	// InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to.
	InMemoryEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
}

type DefaultResourceEncodingConfig struct {
	groups map[string]*GroupResourceEncodingConfig
	scheme *runtime.Scheme
}

type GroupResourceEncodingConfig struct {
	ExternalResourceEncodings map[string]schema.GroupVersion
	InternalResourceEncodings map[string]schema.GroupVersion
}

var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{}

func NewDefaultResourceEncodingConfig(scheme *runtime.Scheme) *DefaultResourceEncodingConfig {
	return &DefaultResourceEncodingConfig{groups: map[string]*GroupResourceEncodingConfig{}, scheme: scheme}
}

func newGroupResourceEncodingConfig() *GroupResourceEncodingConfig {
	return &GroupResourceEncodingConfig{
		ExternalResourceEncodings: map[string]schema.GroupVersion{},
		InternalResourceEncodings: map[string]schema.GroupVersion{},
	}
}

func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) {
	group := resourceBeingStored.Group
	_, groupExists := o.groups[group]
	if !groupExists {
		o.groups[group] = newGroupResourceEncodingConfig()
	}

	o.groups[group].ExternalResourceEncodings[resourceBeingStored.Resource] = externalEncodingVersion
	o.groups[group].InternalResourceEncodings[resourceBeingStored.Resource] = internalVersion
}

func (o *DefaultResourceEncodingConfig) StorageEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
	if !o.scheme.IsGroupRegistered(resource.Group) {
		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
	}

	groupEncoding, groupExists := o.groups[resource.Group]

	if !groupExists {
		// return the most preferred external version for the group
		return o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], nil
	}

	resourceOverride, resourceExists := groupEncoding.ExternalResourceEncodings[resource.Resource]
	if !resourceExists {
		// return the most preferred external version for the group
		return o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], nil
	}

	return resourceOverride, nil
}

func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
	if !o.scheme.IsGroupRegistered(resource.Group) {
		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
	}

	groupEncoding, groupExists := o.groups[resource.Group]
	if !groupExists {
		return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
	}

	resourceOverride, resourceExists := groupEncoding.InternalResourceEncodings[resource.Resource]
	if !resourceExists {
		return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
	}

	return resourceOverride, nil
}
<commit_msg>Simplify the resource_encoding_config.go, since we don't need per group override at all<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package storage

import (
	"fmt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

type ResourceEncodingConfig interface {
	// StorageEncoding returns the serialization format for the resource.
	// TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds
	// For now, it returns just the GroupVersion for consistency with old behavior
	StorageEncodingFor(schema.GroupResource) (schema.GroupVersion, error)

	// InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to.
	InMemoryEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
}

type DefaultResourceEncodingConfig struct {
	// resources records the overriding encoding configs for individual resources.
	resources map[schema.GroupResource]*OverridingResourceEncoding
	scheme    *runtime.Scheme
}

type OverridingResourceEncoding struct {
	ExternalResourceEncoding schema.GroupVersion
	InternalResourceEncoding schema.GroupVersion
}

var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{}

func NewDefaultResourceEncodingConfig(scheme *runtime.Scheme) *DefaultResourceEncodingConfig {
	return &DefaultResourceEncodingConfig{resources: map[schema.GroupResource]*OverridingResourceEncoding{}, scheme: scheme}
}

func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) {
	o.resources[resourceBeingStored] = &OverridingResourceEncoding{
		ExternalResourceEncoding: externalEncodingVersion,
		InternalResourceEncoding: internalVersion,
	}
}

func (o *DefaultResourceEncodingConfig) StorageEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
	if !o.scheme.IsGroupRegistered(resource.Group) {
		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
	}

	resourceOverride, resourceExists := o.resources[resource]
	if resourceExists {
		return resourceOverride.ExternalResourceEncoding, nil
	}

	// return the most preferred external version for the group
	return o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], nil
}

func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
	if !o.scheme.IsGroupRegistered(resource.Group) {
		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
	}

	resourceOverride, resourceExists := o.resources[resource]
	if resourceExists {
		return resourceOverride.InternalResourceEncoding, nil
	}
	return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package openstack

import (
	"errors"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

var ErrNoRouterId = errors.New("router-id not set in cloud provider config")

type Routes struct {
	compute *gophercloud.ServiceClient
	network *gophercloud.ServiceClient
	opts    RouterOpts
}

func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) {
	if opts.RouterId == "" {
		return nil, ErrNoRouterId
	}

	return &Routes{
		compute: compute,
		network: network,
		opts:    opts,
	}, nil
}

func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	glog.V(4).Infof("ListRoutes(%v)", clusterName)

	nodeNamesByAddr := make(map[string]types.NodeName)
	err := foreachServer(r.compute, servers.ListOpts{Status: "ACTIVE"}, func(srv *servers.Server) (bool, error) {
		addrs, err := nodeAddresses(srv)
		if err != nil {
			return false, err
		}

		name := mapServerToNodeName(srv)
		for _, addr := range addrs {
			nodeNamesByAddr[addr.Address] = name
		}

		return true, nil
	})
	if err != nil {
		return nil, err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return nil, err
	}

	var routes []*cloudprovider.Route
	for _, item := range router.Routes {
		nodeName, ok := nodeNamesByAddr[item.NextHop]
		if !ok {
			// Not one of our routes?
			glog.V(4).Infof("Skipping route with unknown nexthop %v", item.NextHop)
			continue
		}
		route := cloudprovider.Route{
			Name:            item.DestinationCIDR,
			TargetNode:      nodeName,
			DestinationCIDR: item.DestinationCIDR,
		}
		routes = append(routes, &route)
	}

	return routes, nil
}

func updateRoutes(network *gophercloud.ServiceClient, router *routers.Router, newRoutes []routers.Route) (func(), error) {
	origRoutes := router.Routes // shallow copy

	_, err := routers.Update(network, router.ID, routers.UpdateOpts{
		Routes: newRoutes,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting routes change to router ", router.ID)
		_, err := routers.Update(network, router.ID, routers.UpdateOpts{
			Routes: origRoutes,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset routes during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *neutronports.Port, newPairs []neutronports.AddressPair) (func(), error) {
	origPairs := port.AllowedAddressPairs // shallow copy

	_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
		AllowedAddressPairs: newPairs,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting allowed-address-pairs change to port ", port.ID)
		_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
			AllowedAddressPairs: origPairs,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset allowed-address-pairs during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode)

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes

	for _, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			glog.V(4).Infof("Skipping existing route: %v", route)
			return nil
		}
	}

	routes = append(routes, routers.Route{
		DestinationCIDR: route.DestinationCIDR,
		NextHop:         addr,
	})

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	found := false
	for _, item := range port.AllowedAddressPairs {
		if item.IPAddress == route.DestinationCIDR {
			glog.V(4).Info("Found existing allowed-address-pair: ", item)
			found = true
			break
		}
	}

	if !found {
		newPairs := append(port.AllowedAddressPairs, neutronports.AddressPair{
			IPAddress: route.DestinationCIDR,
		})
		unwind, err := updateAllowedAddressPairs(r.network, &port, newPairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route created: %v", route)
	onFailure.Disarm()
	return nil
}

func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes
	index := -1
	for i, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			index = i
			break
		}
	}

	if index == -1 {
		glog.V(4).Infof("Skipping non-existent route: %v", route)
		return nil
	}

	// Delete element `index`
	routes[index] = routes[len(routes)-1]
	routes = routes[:len(routes)-1]

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	addr_pairs := port.AllowedAddressPairs
	index = -1
	for i, item := range addr_pairs {
		if item.IPAddress == route.DestinationCIDR {
			index = i
			break
		}
	}

	if index != -1 {
		// Delete element `index`
		addr_pairs[index] = addr_pairs[len(routes)-1]
		addr_pairs = addr_pairs[:len(routes)-1]

		unwind, err := updateAllowedAddressPairs(r.network, &port, addr_pairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route deleted: %v", route)
	onFailure.Disarm()
	return nil
}
<commit_msg>Fix panic of DeleteRoute()<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package openstack

import (
	"errors"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

var ErrNoRouterId = errors.New("router-id not set in cloud provider config")

type Routes struct {
	compute *gophercloud.ServiceClient
	network *gophercloud.ServiceClient
	opts    RouterOpts
}

func NewRoutes(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, opts RouterOpts) (cloudprovider.Routes, error) {
	if opts.RouterId == "" {
		return nil, ErrNoRouterId
	}

	return &Routes{
		compute: compute,
		network: network,
		opts:    opts,
	}, nil
}

func (r *Routes) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) {
	glog.V(4).Infof("ListRoutes(%v)", clusterName)

	nodeNamesByAddr := make(map[string]types.NodeName)
	err := foreachServer(r.compute, servers.ListOpts{Status: "ACTIVE"}, func(srv *servers.Server) (bool, error) {
		addrs, err := nodeAddresses(srv)
		if err != nil {
			return false, err
		}

		name := mapServerToNodeName(srv)
		for _, addr := range addrs {
			nodeNamesByAddr[addr.Address] = name
		}

		return true, nil
	})
	if err != nil {
		return nil, err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return nil, err
	}

	var routes []*cloudprovider.Route
	for _, item := range router.Routes {
		nodeName, ok := nodeNamesByAddr[item.NextHop]
		if !ok {
			// Not one of our routes?
			glog.V(4).Infof("Skipping route with unknown nexthop %v", item.NextHop)
			continue
		}
		route := cloudprovider.Route{
			Name:            item.DestinationCIDR,
			TargetNode:      nodeName,
			DestinationCIDR: item.DestinationCIDR,
		}
		routes = append(routes, &route)
	}

	return routes, nil
}

func updateRoutes(network *gophercloud.ServiceClient, router *routers.Router, newRoutes []routers.Route) (func(), error) {
	origRoutes := router.Routes // shallow copy

	_, err := routers.Update(network, router.ID, routers.UpdateOpts{
		Routes: newRoutes,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting routes change to router ", router.ID)
		_, err := routers.Update(network, router.ID, routers.UpdateOpts{
			Routes: origRoutes,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset routes during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *neutronports.Port, newPairs []neutronports.AddressPair) (func(), error) {
	origPairs := port.AllowedAddressPairs // shallow copy

	_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
		AllowedAddressPairs: newPairs,
	}).Extract()
	if err != nil {
		return nil, err
	}

	unwinder := func() {
		glog.V(4).Info("Reverting allowed-address-pairs change to port ", port.ID)
		_, err := neutronports.Update(network, port.ID, neutronports.UpdateOpts{
			AllowedAddressPairs: origPairs,
		}).Extract()
		if err != nil {
			glog.Warning("Unable to reset allowed-address-pairs during error unwind: ", err)
		}
	}

	return unwinder, nil
}

func (r *Routes) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error {
	glog.V(4).Infof("CreateRoute(%v, %v, %v)", clusterName, nameHint, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Using nexthop %v for node %v", addr, route.TargetNode)

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes

	for _, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			glog.V(4).Infof("Skipping existing route: %v", route)
			return nil
		}
	}

	routes = append(routes, routers.Route{
		DestinationCIDR: route.DestinationCIDR,
		NextHop:         addr,
	})

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	found := false
	for _, item := range port.AllowedAddressPairs {
		if item.IPAddress == route.DestinationCIDR {
			glog.V(4).Info("Found existing allowed-address-pair: ", item)
			found = true
			break
		}
	}

	if !found {
		newPairs := append(port.AllowedAddressPairs, neutronports.AddressPair{
			IPAddress: route.DestinationCIDR,
		})
		unwind, err := updateAllowedAddressPairs(r.network, &port, newPairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route created: %v", route)
	onFailure.Disarm()
	return nil
}

func (r *Routes) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
	glog.V(4).Infof("DeleteRoute(%v, %v)", clusterName, route)

	onFailure := NewCaller()

	addr, err := getAddressByName(r.compute, route.TargetNode)
	if err != nil {
		return err
	}

	router, err := routers.Get(r.network, r.opts.RouterId).Extract()
	if err != nil {
		return err
	}

	routes := router.Routes
	index := -1
	for i, item := range routes {
		if item.DestinationCIDR == route.DestinationCIDR && item.NextHop == addr {
			index = i
			break
		}
	}

	if index == -1 {
		glog.V(4).Infof("Skipping non-existent route: %v", route)
		return nil
	}

	// Delete element `index`
	routes[index] = routes[len(routes)-1]
	routes = routes[:len(routes)-1]

	unwind, err := updateRoutes(r.network, router, routes)
	if err != nil {
		return err
	}
	defer onFailure.Call(unwind)

	port, err := getPortByIP(r.network, addr)
	if err != nil {
		return err
	}

	addr_pairs := port.AllowedAddressPairs
	index = -1
	for i, item := range addr_pairs {
		if item.IPAddress == route.DestinationCIDR {
			index = i
			break
		}
	}

	if index != -1 {
		// Delete element `index`
		addr_pairs[index] = addr_pairs[len(addr_pairs)-1]
		addr_pairs = addr_pairs[:len(addr_pairs)-1]

		unwind, err := updateAllowedAddressPairs(r.network, &port, addr_pairs)
		if err != nil {
			return err
		}
		defer onFailure.Call(unwind)
	}

	glog.V(4).Infof("Route deleted: %v", route)
	onFailure.Disarm()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"context"
	"net"
	"sync"
	"time"

	"google.golang.org/protobuf/proto"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/metrics"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/pkg/gateway/pathhealth"
	gatewaypb "github.com/scionproto/scion/go/pkg/proto/gateway"
	"github.com/scionproto/scion/go/pkg/worker"
)

const (
	defaultProbeInterval    = 500 * time.Millisecond
	defaultHealthExpiration = 2 * time.Second
)

// Event describes a health check event.
type Event int

// The different Event values.
const (
	EventDown Event = iota
	EventUp
)

// PathMonitorRegistration provides access to the paths.
type PathMonitorRegistration interface {
	Get() pathhealth.Selection
	Close()
}

// SessionMonitorMetrics contains the metrics for the session monitor.
type SessionMonitorMetrics struct {
	// Probes is the number of sent probes.
	Probes metrics.Counter
	// ProbeReplies is the number of probe replies received.
	ProbeReplies metrics.Counter
	// IsHealthy is a binary gauge showing a sessions healthiness.
	IsHealthy metrics.Gauge
}

func safeInc(counter metrics.Counter) {
	if counter != nil {
		counter.Add(1)
	}
}

// SessionMonitor monitors a session with a remote gateway instance.
type SessionMonitor struct {
	// ID is the ID of the session. It's used in the probe packet and for
	// diagnostics.
	ID uint8
	// RemoteIA is the remote RemoteIA the gateway to monitor is in.
	RemoteIA addr.IA
	// ProbeAddr is the probe address of the remote gateway instance.
	ProbeAddr *net.UDPAddr
	// Events is the channel where the monitor events are published to. Note
	// that an event is only published on change. The SessionMonitor will
	// close the events channel when it shuts down.
	Events chan<- SessionEvent
	// Paths is used to access paths from the path monitor.
	Paths PathMonitorRegistration
	// ProbeConn is the connection that is used to send and receive probe
	// packets.
	ProbeConn net.PacketConn
	// ProbeInterval is the interval at which the remote is probed. Can be left
	// zero and a default value will be used.
	ProbeInterval time.Duration
	// HealthExpiration is the duration after the last successful probe after
	// which a remote is considered unhealthy.
	HealthExpiration time.Duration
	// Metrics are the metrics which are modified during the operation of the
	// monitor. If empty no metrics are reported.
	Metrics SessionMonitorMetrics

	// stateMtx protects the state from concurrent access.
	stateMtx sync.RWMutex
	// state is the current state the monitor is in.
	state Event
	// expirationTimer is used to trigger expiration.
	expirationTimer *time.Timer
	// receivedProbe indicates a probe was received.
	receivedProbe chan struct{}

	// rawProbe is the raw probe to send.
	rawProbe []byte

	workerBase worker.Base
}

func (m *SessionMonitor) initDefaults() {
	if m.ProbeInterval == 0 {
		m.ProbeInterval = defaultProbeInterval
	}
	if m.HealthExpiration == 0 {
		m.HealthExpiration = defaultHealthExpiration
	}
}

// Run runs the session monitor. It blocks until Close is called..
func (m *SessionMonitor) Run(ctx context.Context) error {
	return m.workerBase.RunWrapper(ctx, m.setupInternalState, m.run)
}

func (m *SessionMonitor) run(ctx context.Context) error {
	defer close(m.Events)
	go func() {
		defer log.HandlePanic()
		m.drainConn(ctx)
	}()
	probeTicker := time.NewTicker(m.ProbeInterval)
	m.sendProbe(ctx)
	for {
		select {
		case <-probeTicker.C:
			m.sendProbe(ctx)
		case <-m.receivedProbe:
			m.handleProbeReply(ctx)
		case <-m.expirationTimer.C:
			m.handleExpiration(ctx)
		case <-m.workerBase.GetDoneChan():
			return nil
		}
	}
}

func (m *SessionMonitor) notification(e Event) SessionEvent {
	return SessionEvent{SessionID: m.ID, Event: m.state}
}

// Close stops the session monitor.
func (m *SessionMonitor) Close(ctx context.Context) error {
	return m.workerBase.CloseWrapper(ctx, nil)
}

// sessionState is for diagnostics and indicates the healthiness of a session.
type sessionState struct {
	// ID is the ID of the session.
	ID uint8
	// Healthy indicates whether this session receveived probes recently and is
	// thus seen as healthy.
	Healthy bool
}

func (m *SessionMonitor) sessionState() sessionState {
	m.stateMtx.RLock()
	defer m.stateMtx.RUnlock()
	return sessionState{
		ID:      m.ID,
		Healthy: m.state == EventUp,
	}
}

func (m *SessionMonitor) setupInternalState(ctx context.Context) error {
	m.initDefaults()
	m.state = EventDown
	probe := &gatewaypb.ControlRequest{
		Request: &gatewaypb.ControlRequest_Probe{
			Probe: &gatewaypb.ProbeRequest{
				SessionId: uint32(m.ID),
			},
		},
	}
	raw, err := proto.Marshal(probe)
	if err != nil {
		return serrors.WrapStr("marshaling probe", err)
	}
	m.rawProbe = raw
	m.receivedProbe = make(chan struct{})
	m.expirationTimer = time.NewTimer(m.HealthExpiration)
	return nil
}

func (m *SessionMonitor) sendProbe(ctx context.Context) {
	logger := log.FromCtx(ctx)
	paths := m.Paths.Get().Paths
	if len(paths) == 0 {
		// no path nothing we can do.
		return
	}
	remote := &snet.UDPAddr{
		IA:      m.RemoteIA,
		Host:    m.ProbeAddr,
		NextHop: paths[0].UnderlayNextHop(),
		Path:    paths[0].Path(),
	}
	// TODO(sustrik): This should not block. Use SetWriteDeadline.
	// Do so when creating the connection.
	_, err := m.ProbeConn.WriteTo(m.rawProbe, remote)
	if err != nil {
		logger.Error("Error sending probe", "err", err)
		return
	}
	safeInc(m.Metrics.Probes)
}

func (m *SessionMonitor) handleProbeReply(ctx context.Context) {
	m.stateMtx.Lock()
	defer m.stateMtx.Unlock()

	logger := log.FromCtx(ctx)
	if m.state != EventUp {
		m.state = EventUp
		metrics.GaugeSet(m.Metrics.IsHealthy, 1)

		select {
		case <-m.workerBase.GetDoneChan():
		case m.Events <- m.notification(m.state):
			logger.Debug("Sent UP event", "session_id", m.ID)
		}
	}
	m.expirationTimer.Reset(m.HealthExpiration)
}

func (m *SessionMonitor) handleExpiration(ctx context.Context) {
	m.stateMtx.Lock()
	defer m.stateMtx.Unlock()

	logger := log.FromCtx(ctx)
	// ignore if the state is already down.
	if m.state == EventDown {
		return
	}

	m.state = EventDown
	metrics.GaugeSet(m.Metrics.IsHealthy, 0)

	select {
	case <-m.workerBase.GetDoneChan():
	case m.Events <- m.notification(m.state):
		logger.Debug("Sent DOWN event", "session_id", m.ID)
	}
}

func (m *SessionMonitor) drainConn(ctx context.Context) {
	logger := log.FromCtx(ctx)
	buf := make([]byte, common.SupportedMTU)
	for {
		n, _, err := m.ProbeConn.ReadFrom(buf)
		// XXX(karampok): The .ReadFrom(buf) is a blocking action and when
		// gracefully close the SessionMonitor it unblocks because the ProbeConn
		// closed. In that there is an error which we can ignore.
		select {
		case <-m.workerBase.GetDoneChan():
			return
		default:
		}
		if err != nil {
			logger.Error("Reading from probe conn", "err", err)
			continue
		}
		if err := m.handlePkt(buf[:n]); err != nil {
			logger.Error("Handling probe reply", "err", err)
		}
	}
}

func (m *SessionMonitor) handlePkt(raw []byte) error {
	var ctrl gatewaypb.ControlResponse
	if err := proto.Unmarshal(raw, &ctrl); err != nil {
		return serrors.WrapStr("parsing control response", err)
	}
	probe, ok := ctrl.Response.(*gatewaypb.ControlResponse_Probe)
	if !ok {
		return serrors.New("unexpected control response", "type", common.TypeOf(ctrl.Response))
	}
	if probe.Probe.SessionId != uint32(m.ID) {
		return serrors.New("unexpected session ID in response",
			"response_id", probe.Probe.SessionId, "expected_id", m.ID)
	}
	safeInc(m.Metrics.ProbeReplies)
	m.receivedProbe <- struct{}{}
	return nil
}
<commit_msg>gateway: fix session monitor expiration reset<commit_after>// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"context"
	"net"
	"sync"
	"time"

	"google.golang.org/protobuf/proto"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/metrics"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/pkg/gateway/pathhealth"
	gatewaypb "github.com/scionproto/scion/go/pkg/proto/gateway"
	"github.com/scionproto/scion/go/pkg/worker"
)

const (
	defaultProbeInterval    = 500 * time.Millisecond
	defaultHealthExpiration = 2 * time.Second
)

// Event describes a health check event.
type Event int

// The different Event values.
const (
	EventDown Event = iota
	EventUp
)

// PathMonitorRegistration provides access to the paths.
type PathMonitorRegistration interface {
	Get() pathhealth.Selection
	Close()
}

// SessionMonitorMetrics contains the metrics for the session monitor.
type SessionMonitorMetrics struct {
	// Probes is the number of sent probes.
	Probes metrics.Counter
	// ProbeReplies is the number of probe replies received.
	ProbeReplies metrics.Counter
	// IsHealthy is a binary gauge showing a sessions healthiness.
	IsHealthy metrics.Gauge
}

func safeInc(counter metrics.Counter) {
	if counter != nil {
		counter.Add(1)
	}
}

// SessionMonitor monitors a session with a remote gateway instance.
type SessionMonitor struct {
	// ID is the ID of the session. It's used in the probe packet and for
	// diagnostics.
	ID uint8
	// RemoteIA is the remote RemoteIA the gateway to monitor is in.
	RemoteIA addr.IA
	// ProbeAddr is the probe address of the remote gateway instance.
	ProbeAddr *net.UDPAddr
	// Events is the channel where the monitor events are published to. Note
	// that an event is only published on change. The SessionMonitor will
	// close the events channel when it shuts down.
	Events chan<- SessionEvent
	// Paths is used to access paths from the path monitor.
	Paths PathMonitorRegistration
	// ProbeConn is the connection that is used to send and receive probe
	// packets.
	ProbeConn net.PacketConn
	// ProbeInterval is the interval at which the remote is probed. Can be left
	// zero and a default value will be used.
	ProbeInterval time.Duration
	// HealthExpiration is the duration after the last successful probe after
	// which a remote is considered unhealthy.
	HealthExpiration time.Duration
	// Metrics are the metrics which are modified during the operation of the
	// monitor. If empty no metrics are reported.
	Metrics SessionMonitorMetrics

	// stateMtx protects the state from concurrent access.
	stateMtx sync.RWMutex
	// state is the current state the monitor is in.
	state Event
	// expirationTimer is used to trigger expiration.
	expirationTimer *time.Timer
	// receivedProbe indicates a probe was received.
	receivedProbe chan struct{}

	// rawProbe is the raw probe to send.
	rawProbe []byte

	workerBase worker.Base
}

func (m *SessionMonitor) initDefaults() {
	if m.ProbeInterval == 0 {
		m.ProbeInterval = defaultProbeInterval
	}
	if m.HealthExpiration == 0 {
		m.HealthExpiration = defaultHealthExpiration
	}
}

// Run runs the session monitor. It blocks until Close is called..
func (m *SessionMonitor) Run(ctx context.Context) error {
	return m.workerBase.RunWrapper(ctx, m.setupInternalState, m.run)
}

func (m *SessionMonitor) run(ctx context.Context) error {
	defer close(m.Events)
	defer m.expirationTimer.Stop()
	go func() {
		defer log.HandlePanic()
		m.drainConn(ctx)
	}()
	probeTicker := time.NewTicker(m.ProbeInterval)
	defer probeTicker.Stop()
	m.sendProbe(ctx)
	for {
		select {
		case <-probeTicker.C:
			m.sendProbe(ctx)
		case <-m.receivedProbe:
			m.handleProbeReply(ctx)
		case <-m.expirationTimer.C:
			select {
			case <-m.receivedProbe:
				m.handleProbeReply(ctx)
			default:
				m.handleExpiration(ctx)
			}
		case <-m.workerBase.GetDoneChan():
			return nil
		}
	}
}

func (m *SessionMonitor) notification(e Event) SessionEvent {
	return SessionEvent{SessionID: m.ID, Event: m.state}
}

// Close stops the session monitor.
func (m *SessionMonitor) Close(ctx context.Context) error {
	return m.workerBase.CloseWrapper(ctx, nil)
}

// sessionState is for diagnostics and indicates the healthiness of a session.
type sessionState struct {
	// ID is the ID of the session.
	ID uint8
	// Healthy indicates whether this session receveived probes recently and is
	// thus seen as healthy.
	Healthy bool
}

func (m *SessionMonitor) sessionState() sessionState {
	m.stateMtx.RLock()
	defer m.stateMtx.RUnlock()
	return sessionState{
		ID:      m.ID,
		Healthy: m.state == EventUp,
	}
}

func (m *SessionMonitor) setupInternalState(ctx context.Context) error {
	m.initDefaults()
	m.state = EventDown
	probe := &gatewaypb.ControlRequest{
		Request: &gatewaypb.ControlRequest_Probe{
			Probe: &gatewaypb.ProbeRequest{
				SessionId: uint32(m.ID),
			},
		},
	}
	raw, err := proto.Marshal(probe)
	if err != nil {
		return serrors.WrapStr("marshaling probe", err)
	}
	m.rawProbe = raw
	m.receivedProbe = make(chan struct{})
	m.expirationTimer = time.NewTimer(m.HealthExpiration)
	return nil
}

func (m *SessionMonitor) sendProbe(ctx context.Context) {
	logger := log.FromCtx(ctx)
	paths := m.Paths.Get().Paths
	if len(paths) == 0 {
		// no path nothing we can do.
		return
	}
	remote := &snet.UDPAddr{
		IA:      m.RemoteIA,
		Host:    m.ProbeAddr,
		NextHop: paths[0].UnderlayNextHop(),
		Path:    paths[0].Path(),
	}
	// TODO(sustrik): This should not block. Use SetWriteDeadline.
	// Do so when creating the connection.
	_, err := m.ProbeConn.WriteTo(m.rawProbe, remote)
	if err != nil {
		logger.Error("Error sending probe", "err", err)
		return
	}
	safeInc(m.Metrics.Probes)
}

func (m *SessionMonitor) handleProbeReply(ctx context.Context) {
	m.stateMtx.Lock()
	defer m.stateMtx.Unlock()

	logger := log.FromCtx(ctx)
	if m.state != EventUp {
		m.state = EventUp
		metrics.GaugeSet(m.Metrics.IsHealthy, 1)

		select {
		case <-m.workerBase.GetDoneChan():
		case m.Events <- m.notification(m.state):
			logger.Debug("Sent UP event", "session_id", m.ID)
		}
	}
	// proper reset sequence (https://pkg.go.dev/time#Timer.Reset)
	if !m.expirationTimer.Stop() {
		// The channel could be empty if we were previously in the down state
		// and now received a new reply. The important bit is that the channel
		// is drained.
		select {
		case <-m.expirationTimer.C:
		default:
		}

	}
	m.expirationTimer.Reset(m.HealthExpiration)
}

func (m *SessionMonitor) handleExpiration(ctx context.Context) {
	m.stateMtx.Lock()
	defer m.stateMtx.Unlock()

	logger := log.FromCtx(ctx)
	// ignore if the state is already down.
	if m.state == EventDown {
		return
	}

	m.state = EventDown
	metrics.GaugeSet(m.Metrics.IsHealthy, 0)

	select {
	case <-m.workerBase.GetDoneChan():
	case m.Events <- m.notification(m.state):
		logger.Debug("Sent DOWN event", "session_id", m.ID)
	}
}

func (m *SessionMonitor) drainConn(ctx context.Context) {
	logger := log.FromCtx(ctx)
	buf := make([]byte, common.SupportedMTU)
	for {
		n, _, err := m.ProbeConn.ReadFrom(buf)
		// XXX(karampok): The .ReadFrom(buf) is a blocking action and when
		// gracefully close the SessionMonitor it unblocks because the ProbeConn
		// closed. In that there is an error which we can ignore.
		select {
		case <-m.workerBase.GetDoneChan():
			return
		default:
		}
		if err != nil {
			logger.Error("Reading from probe conn", "err", err)
			continue
		}
		if err := m.handlePkt(buf[:n]); err != nil {
			logger.Error("Handling probe reply", "err", err)
		}
	}
}

func (m *SessionMonitor) handlePkt(raw []byte) error {
	var ctrl gatewaypb.ControlResponse
	if err := proto.Unmarshal(raw, &ctrl); err != nil {
		return serrors.WrapStr("parsing control response", err)
	}
	probe, ok := ctrl.Response.(*gatewaypb.ControlResponse_Probe)
	if !ok {
		return serrors.New("unexpected control response", "type", common.TypeOf(ctrl.Response))
	}
	if probe.Probe.SessionId != uint32(m.ID) {
		return serrors.New("unexpected session ID in response",
			"response_id", probe.Probe.SessionId, "expected_id", m.ID)
	}
	safeInc(m.Metrics.ProbeReplies)
	m.receivedProbe <- struct{}{}
	return nil
}
<|endoftext|>


<commit_before>package sampleworld

import (
	"fmt"
	"log"
	"time"

	"sample"
	"sample/mocks"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/security"
	"v.io/v23/services/security/access"
	"v.io/x/ref/lib/signals"
)

func makeServerAlarm() interface{} {
	return sample.AlarmServer(mocks.NewAlarm())
}
func makeServerLightSwitch() interface{} {
	return sample.LightSwitchServer(mocks.NewLightSwitch())
}
func makeServerPoolHeater() interface{} {
	return sample.PoolHeaterServer(mocks.NewPoolHeater())
}
func makeServerSmokeDetector() interface{} {
	return sample.SmokeDetectorServer(mocks.NewSmokeDetector())
}
func makeServerSpeaker() interface{} {
	return sample.SpeakerServer(mocks.NewSpeaker())
}
func makeServerSprinkler() interface{} {
	return sample.SprinklerServer(mocks.NewSprinkler())
}
func makePetFeederAndRoboDog() (interface{}, interface{}) {
	p := mocks.NewPetFeeder()
	r := mocks.NewRoboDog(p)
	return sample.PetFeederServer(p), sample.RoboDogServer(r)
}

// openAuthorizer allows RPCs from all clients.
// TODO(aghassemi): Write a more strict authorizer with proper ACLs and
// identity setup
type openAuthorizer struct{}

func (o openAuthorizer) Authorize(_ security.Call) error {
	return nil
}

func RunSampleWorld(ctx *context.T) {
	// Create new server and publish the given server under the given name
	var listenAndServe = func(name string, server interface{}) func() {

		// Create a new server instance.
		s, err := v23.NewServer(ctx)
		if err != nil {
			log.Fatal("failure creating server: ", err)
		}

		// Create an endpoint and begin listening.
		if endpoint, err := s.Listen(v23.GetListenSpec(ctx)); err == nil {
			fmt.Printf("Listening at: %v\n", endpoint)
		} else {
			log.Fatal("error listening to service: ", err)
		}

		// Serve these services at the given name.
		if err := s.Serve(name, server, openAuthorizer{}); err != nil {
			log.Fatal("error serving service: ", err)
		}

		return func() {
			s.Stop()
		}
	}

	// Serve bunch of mock services under different names
	defer listenAndServe("house/alarm", makeServerAlarm())()
	defer listenAndServe("house/living-room/lights", makeServerLightSwitch())()
	defer listenAndServe("house/living-room/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("house/living-room/blast-speaker", makeServerSpeaker())()
	defer listenAndServe("house/living-room/soundbar", makeServerSpeaker())()
	defer listenAndServe("house/master-bedroom/desk-lamp", makeServerLightSwitch())()
	defer listenAndServe("house/master-bedroom/lights", makeServerLightSwitch())()
	defer listenAndServe("house/master-bedroom/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("house/master-bedroom/speaker", makeServerSpeaker())()
	defer listenAndServe("house/kitchen/lights", makeServerLightSwitch())()
	defer listenAndServe("house/kitchen/smoke-detector", makeServerSmokeDetector())()

	petfeeder, robodog := makePetFeederAndRoboDog()
	defer listenAndServe("house/pet-feeder", petfeeder)()
	defer listenAndServe("house/robo-dog", robodog)()

	defer listenAndServe("cottage/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("cottage/alarm", makeServerAlarm())()
	defer listenAndServe("cottage/lights", makeServerLightSwitch())()
	defer listenAndServe("cottage/pool/heater", makeServerPoolHeater())()
	defer listenAndServe("cottage/pool/speaker", makeServerSpeaker())()
	defer listenAndServe("cottage/pool/pool-lights", makeServerLightSwitch())()
	defer listenAndServe("cottage/lawn/front/sprinkler", makeServerSprinkler())()
	defer listenAndServe("cottage/lawn/back/sprinkler", makeServerSprinkler())()
	defer listenAndServe("cottage/lawn/master-sprinkler", makeServerSprinkler())()

	// Add bunch of inaccessible names
	var nobody = []security.BlessingPattern{""}
	var everybody = []security.BlessingPattern{"..."}
	var nobodyCanResolve = access.TaggedACLMap{
		"Resolve": access.ACL{
			In: nobody,
		},
		"Read": access.ACL{
			In: nobody,
		},
		"Admin": access.ACL{
			In: nobody,
		},
		"Create": access.ACL{
			In: nobody,
		},
		"Mount": access.ACL{
			In: everybody,
		},
	}
	var everybodyCanList = access.TaggedACLMap{
		"Resolve": access.ACL{
			In: everybody,
		},
		"Read": access.ACL{
			In: everybody,
		},
		"Admin": access.ACL{
			In: everybody,
		},
		"Create": access.ACL{
			In: everybody,
		},
		"Mount": access.ACL{
			In: everybody,
		},
	}

	ns := v23.GetNamespace(ctx)
	// Make everyone see stuff in house/master-bedroom/personal.
	ns.SetACL(ctx, "house/master-bedroom/personal", everybodyCanList, "")

	// Toothbrush is inaccessible because of bad endpoint.
	nextYear := time.Now().AddDate(1, 0, 0)
	ttl := nextYear.Sub(time.Now())
	ns.Mount(ctx, "house/master-bedroom/personal/toothbrush", "/does.not.exist.v.io:9898", ttl)

	// Hairbrush is inaccessible because of mounttable ACLs on it do not allow anyone to resolve the name.
	ns.SetACL(ctx, "house/master-bedroom/personal/hairbrush", nobodyCanResolve, "")
	defer listenAndServe("house/master-bedroom/personal/hairbrush", makeServerSprinkler())()

	// Wait forever.
	<-signals.ShutdownOnSignals(ctx)
}
<commit_msg>TBR namespace_browser: Fix tests (ACL rename to AccessList)<commit_after>package sampleworld

import (
	"fmt"
	"log"
	"time"

	"sample"
	"sample/mocks"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/security"
	"v.io/v23/services/security/access"
	"v.io/x/ref/lib/signals"
)

func makeServerAlarm() interface{} {
	return sample.AlarmServer(mocks.NewAlarm())
}
func makeServerLightSwitch() interface{} {
	return sample.LightSwitchServer(mocks.NewLightSwitch())
}
func makeServerPoolHeater() interface{} {
	return sample.PoolHeaterServer(mocks.NewPoolHeater())
}
func makeServerSmokeDetector() interface{} {
	return sample.SmokeDetectorServer(mocks.NewSmokeDetector())
}
func makeServerSpeaker() interface{} {
	return sample.SpeakerServer(mocks.NewSpeaker())
}
func makeServerSprinkler() interface{} {
	return sample.SprinklerServer(mocks.NewSprinkler())
}
func makePetFeederAndRoboDog() (interface{}, interface{}) {
	p := mocks.NewPetFeeder()
	r := mocks.NewRoboDog(p)
	return sample.PetFeederServer(p), sample.RoboDogServer(r)
}

// openAuthorizer allows RPCs from all clients.
// TODO(aghassemi): Write a more strict authorizer with proper ACLs and
// identity setup
type openAuthorizer struct{}

func (o openAuthorizer) Authorize(_ security.Call) error {
	return nil
}

func RunSampleWorld(ctx *context.T) {
	// Create new server and publish the given server under the given name
	var listenAndServe = func(name string, server interface{}) func() {

		// Create a new server instance.
		s, err := v23.NewServer(ctx)
		if err != nil {
			log.Fatal("failure creating server: ", err)
		}

		// Create an endpoint and begin listening.
		if endpoint, err := s.Listen(v23.GetListenSpec(ctx)); err == nil {
			fmt.Printf("Listening at: %v\n", endpoint)
		} else {
			log.Fatal("error listening to service: ", err)
		}

		// Serve these services at the given name.
		if err := s.Serve(name, server, openAuthorizer{}); err != nil {
			log.Fatal("error serving service: ", err)
		}

		return func() {
			s.Stop()
		}
	}

	// Serve bunch of mock services under different names
	defer listenAndServe("house/alarm", makeServerAlarm())()
	defer listenAndServe("house/living-room/lights", makeServerLightSwitch())()
	defer listenAndServe("house/living-room/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("house/living-room/blast-speaker", makeServerSpeaker())()
	defer listenAndServe("house/living-room/soundbar", makeServerSpeaker())()
	defer listenAndServe("house/master-bedroom/desk-lamp", makeServerLightSwitch())()
	defer listenAndServe("house/master-bedroom/lights", makeServerLightSwitch())()
	defer listenAndServe("house/master-bedroom/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("house/master-bedroom/speaker", makeServerSpeaker())()
	defer listenAndServe("house/kitchen/lights", makeServerLightSwitch())()
	defer listenAndServe("house/kitchen/smoke-detector", makeServerSmokeDetector())()

	petfeeder, robodog := makePetFeederAndRoboDog()
	defer listenAndServe("house/pet-feeder", petfeeder)()
	defer listenAndServe("house/robo-dog", robodog)()

	defer listenAndServe("cottage/smoke-detector", makeServerSmokeDetector())()
	defer listenAndServe("cottage/alarm", makeServerAlarm())()
	defer listenAndServe("cottage/lights", makeServerLightSwitch())()
	defer listenAndServe("cottage/pool/heater", makeServerPoolHeater())()
	defer listenAndServe("cottage/pool/speaker", makeServerSpeaker())()
	defer listenAndServe("cottage/pool/pool-lights", makeServerLightSwitch())()
	defer listenAndServe("cottage/lawn/front/sprinkler", makeServerSprinkler())()
	defer listenAndServe("cottage/lawn/back/sprinkler", makeServerSprinkler())()
	defer listenAndServe("cottage/lawn/master-sprinkler", makeServerSprinkler())()

	// Add bunch of inaccessible names
	var nobody = []security.BlessingPattern{""}
	var everybody = []security.BlessingPattern{"..."}
	var nobodyCanResolve = access.Permissions{
		"Resolve": access.AccessList{
			In: nobody,
		},
		"Read": access.AccessList{
			In: nobody,
		},
		"Admin": access.AccessList{
			In: nobody,
		},
		"Create": access.AccessList{
			In: nobody,
		},
		"Mount": access.AccessList{
			In: everybody,
		},
	}
	var everybodyCanList = access.Permissions{
		"Resolve": access.AccessList{
			In: everybody,
		},
		"Read": access.AccessList{
			In: everybody,
		},
		"Admin": access.AccessList{
			In: everybody,
		},
		"Create": access.AccessList{
			In: everybody,
		},
		"Mount": access.AccessList{
			In: everybody,
		},
	}

	ns := v23.GetNamespace(ctx)
	// Make everyone see stuff in house/master-bedroom/personal.
	ns.SetPermissions(ctx, "house/master-bedroom/personal", everybodyCanList, "")

	// Toothbrush is inaccessible because of bad endpoint.
	nextYear := time.Now().AddDate(1, 0, 0)
	ttl := nextYear.Sub(time.Now())
	ns.Mount(ctx, "house/master-bedroom/personal/toothbrush", "/does.not.exist.v.io:9898", ttl)

	// Hairbrush is inaccessible because of mounttable ACLs on it do not allow anyone to resolve the name.
	ns.SetPermissions(ctx, "house/master-bedroom/personal/hairbrush", nobodyCanResolve, "")
	defer listenAndServe("house/master-bedroom/personal/hairbrush", makeServerSprinkler())()

	// Wait forever.
	<-signals.ShutdownOnSignals(ctx)
}
<|endoftext|>


<commit_before>// Copyright 2018 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package certs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/crypto/curve25519"
	"golang.org/x/crypto/ed25519"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/crypto/cert"
	"github.com/scionproto/scion/go/lib/crypto/trc"
	"github.com/scionproto/scion/go/lib/trust"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/base"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/conf"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/pkicmn"
)

func runGenCert(args []string) {
	asMap, err := pkicmn.ProcessSelector(args[0])
	if err != nil {
		base.ErrorAndExit("Error: %s\n", err)
	}
	for isd, ases := range asMap {
		iconf, err := conf.LoadIsdConf(pkicmn.GetIsdPath(isd))
		if err != nil {
			base.ErrorAndExit("Error reading isd.ini: %s\n", err)
		}
		// Process cores.
		for _, ia := range ases {
			if !pkicmn.Contains(iconf.Trc.CoreIAs, ia) {
				continue
			}
			if err = genCert(ia, true); err != nil {
				base.ErrorAndExit("Error generating cert for %s: %s\n", ia, err)
			}
		}
		// Process non-cores.
		for _, ia := range ases {
			if pkicmn.Contains(iconf.Trc.CoreIAs, ia) {
				continue
			}
			if err = genCert(ia, false); err != nil {
				base.ErrorAndExit("Error generating cert for %s: %s\n", ia, err)
			}
		}
	}
	os.Exit(0)
}

func genCert(ia addr.IA, isIssuer bool) error {
	var err error
	dir := pkicmn.GetAsPath(ia)
	// Check that as.ini exists, otherwise skip directory.
	cpath := filepath.Join(dir, conf.AsConfFileName)
	if _, err = os.Stat(cpath); os.IsNotExist(err) {
		fmt.Printf("Skipping %s. Missing %s\n", dir, conf.AsConfFileName)
		return nil
	}
	a, err := conf.LoadAsConf(dir)
	if err != nil {
		return common.NewBasicError("Error loading as.ini", err, "path", cpath)
	}
	if isIssuer && a.IssuerCert == nil {
		return common.NewBasicError(fmt.Sprintf("'%s' section missing from as.ini",
			conf.IssuerSectionName), nil, "path", cpath)
	}
	// Check if file already exists.
	fname := fmt.Sprintf(pkicmn.CertNameFmt, ia.I, ia.A.FileFmt(), a.AsCert.Version)
	if _, err := os.Stat(filepath.Join(dir, pkicmn.CertsDir, fname)); err == nil && !pkicmn.Force {
		fmt.Printf("%s already exists. Use -f to overwrite.\n", fname)
		return nil
	}
	fmt.Println("Generating Certificate Chain for", ia)
	// If we are an issuer then we need to generate an issuer cert first.
	var issuerCert *cert.Certificate
	if isIssuer {
		issuerCert, err = genIssuerCert(a.IssuerCert, ia)
		if err != nil {
			return common.NewBasicError("Error generating issuer cert", err, "subject", ia)
		}
	} else {
		issuerCert, err = getIssuerCert(a.AsCert.IssuerIA)
		if err != nil {
			return common.NewBasicError("Error loading issuer cert", err, "subject", ia)
		}
	}
	if issuerCert == nil {
		return common.NewBasicError("Issuer cert not found", nil, "issuer", a.AsCert.Issuer)
	}
	// Generate the AS certificate chain.
	chain, err := genASCert(a.AsCert, ia, issuerCert)
	if err != nil {
		return common.NewBasicError("Error generating cert", err, "subject", ia)
	}
	// Check if out directory exists and if not create it.
	out := filepath.Join(dir, pkicmn.CertsDir)
	if _, err = os.Stat(out); os.IsNotExist(err) {
		if err = os.MkdirAll(out, 0755); err != nil {
			return common.NewBasicError("Cannot create output dir", err, "dir", out)
		}
	}
	// Write the cert to disk.
	raw, err := chain.JSON(true)
	if err != nil {
		return common.NewBasicError("Error json-encoding cert", err, "subject", ia)
	}
	if err = pkicmn.WriteToFile(raw, filepath.Join(out, fname), 0644); err != nil {
		return common.NewBasicError("Error writing cert", err, "subject", ia)
	}
	return nil
}

// genIssuerCert generates a new issuer certificate according to conf.
func genIssuerCert(issuerConf *conf.IssuerCert, s addr.IA) (*cert.Certificate, error) {
	c, err := genCertCommon(issuerConf.BaseCert, s, trust.IssSigKeyFile)
	if err != nil {
		return nil, err
	}
	c.CanIssue = true
	c.Issuer = s
	if c.Comment == "" {
		c.Comment = fmt.Sprintf("Issuer Certificate for %s version %d.", c.Subject, c.Version)
	}
	issuerKeyPath := filepath.Join(pkicmn.GetAsPath(c.Issuer), pkicmn.KeysDir, trust.OnKeyFile)
	// Load online root key to sign the certificate.
	issuerKey, err := trust.LoadKey(issuerKeyPath)
	if err != nil {
		return nil, err
	}
	// Sign the certificate.
	currTrcPath := filepath.Join(pkicmn.GetIsdPath(s.I), pkicmn.TRCsDir,
		fmt.Sprintf(pkicmn.TrcNameFmt, s.I, c.TRCVersion))
	currTrc, err := trc.TRCFromFile(currTrcPath, false)

	if err != nil {
		return nil, common.NewBasicError("Error reading TRC", err, "path: ", currTrcPath)
	}

	coreAs, ok := currTrc.CoreASes[s]
	if !ok {
		return nil, common.NewBasicError("Issuer of IssuerCert not found in Core ASes of TRC",
			nil, "issuer", s)
	}

	if err = c.Sign(issuerKey, coreAs.OnlineKeyAlg); err != nil {
		return nil, err
	}

	return c, nil
}

// genASCert generates a new AS certificate according to 'conf'.
func genASCert(conf *conf.AsCert, s addr.IA, issuerCert *cert.Certificate) (*cert.Chain, error) {
	c, err := genCertCommon(conf.BaseCert, s, trust.SigKeyFile)
	if err != nil {
		return nil, err
	}
	c.CanIssue = false
	c.Issuer = conf.IssuerIA
	if c.Comment == "" {
		c.Comment = fmt.Sprintf("AS Certificate for %s version %d.", c.Subject, c.Version)
	}
	// Ensure issuer can issue certificates.
	if !issuerCert.CanIssue {
		return nil, common.NewBasicError("Issuer cert not authorized to issue certs.", nil,
			"issuer", c.Issuer, "subject", c.Subject)
	}
	issuerKeyPath := filepath.Join(pkicmn.GetAsPath(conf.IssuerIA), pkicmn.KeysDir, trust.IssSigKeyFile)
	issuerKey, err := trust.LoadKey(issuerKeyPath)
	if err != nil {
		return nil, err
	}
	// Sign the certificate.
	if err = c.Sign(issuerKey, issuerCert.SignAlgorithm); err != nil {
		return nil, err
	}
	// Create certificate chain.
	chain := &cert.Chain{
		Leaf:   c,
		Issuer: issuerCert,
	}
	if verify {
		err = verifyChain(chain, c.Subject)
		if err != nil {
			fname := fmt.Sprintf(pkicmn.CertNameFmt, c.Subject.I, c.Subject.A, c.Version)
			return nil, common.NewBasicError("Verification FAILED", err, "cert", fname)
		}
	}
	// Write the cert to disk.
	return chain, nil
}

func genCertCommon(bc *conf.BaseCert, s addr.IA, signKeyFname string) (*cert.Certificate, error) {
	// Load signing and decryption keys that will be in the certificate.
	keyDir := filepath.Join(pkicmn.GetAsPath(s), pkicmn.KeysDir)
	signKey, err := trust.LoadKey(filepath.Join(keyDir, signKeyFname))
	if err != nil {
		return nil, err
	}
	signPub := common.RawBytes(ed25519.PrivateKey(signKey).Public().(ed25519.PublicKey))
	decKey, err := trust.LoadKey(filepath.Join(keyDir, trust.DecKeyFile))
	if err != nil {
		return nil, err
	}
	var decKeyFixed, decPub [32]byte
	copy(decKeyFixed[:], decKey)
	curve25519.ScalarBaseMult(&decPub, &decKeyFixed)
	// Determine issuingTime and calculate expiration time from validity.
	issuingTime := bc.IssuingTime
	if issuingTime == 0 {
		issuingTime = uint64(time.Now().Unix())
	}
	expirationTime := issuingTime + uint64(bc.Validity.Seconds())
	return &cert.Certificate{
		Comment:        bc.Comment,
		SubjectSignKey: signPub,
		SignAlgorithm:  bc.SignAlgorithm,
		SubjectEncKey:  decPub[:],
		EncAlgorithm:   bc.EncAlgorithm,
		Subject:        s,
		IssuingTime:    issuingTime,
		ExpirationTime: expirationTime,
		Version:        bc.Version,
		TRCVersion:     bc.TRCVersion,
	}, nil
}

// getIssuerCert returns the newest issuer certificate (if any).
func getIssuerCert(issuer addr.IA) (*cert.Certificate, error) {
	fnames, err := filepath.Glob(fmt.Sprintf("%s/*.crt",
		filepath.Join(pkicmn.GetAsPath(issuer), pkicmn.CertsDir)))
	if err != nil {
		return nil, err
	}
	var issuerCert *cert.Certificate
	for _, fname := range fnames {
		raw, err := ioutil.ReadFile(fname)
		if err != nil {
			return nil, err
		}
		chain, err := cert.ChainFromRaw(raw, false)
		if err != nil {
			return nil, err
		}
		if issuerCert == nil || chain.Issuer.Version > issuerCert.Version {
			issuerCert = chain.Issuer
		}
	}
	return issuerCert, nil
}
<commit_msg>Fixing minor formatting issues in scion-pki (#1532)<commit_after>// Copyright 2018 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package certs

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/crypto/curve25519"
	"golang.org/x/crypto/ed25519"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/crypto/cert"
	"github.com/scionproto/scion/go/lib/crypto/trc"
	"github.com/scionproto/scion/go/lib/trust"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/base"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/conf"
	"github.com/scionproto/scion/go/tools/scion-pki/internal/pkicmn"
)

func runGenCert(args []string) {
	asMap, err := pkicmn.ProcessSelector(args[0])
	if err != nil {
		base.ErrorAndExit("Error: %s\n", err)
	}
	for isd, ases := range asMap {
		iconf, err := conf.LoadIsdConf(pkicmn.GetIsdPath(isd))
		if err != nil {
			base.ErrorAndExit("Error reading isd.ini: %s\n", err)
		}
		// Process cores.
		for _, ia := range ases {
			if !pkicmn.Contains(iconf.Trc.CoreIAs, ia) {
				continue
			}
			if err = genCert(ia, true); err != nil {
				base.ErrorAndExit("Error generating cert for %s: %s\n", ia, err)
			}
		}
		// Process non-cores.
		for _, ia := range ases {
			if pkicmn.Contains(iconf.Trc.CoreIAs, ia) {
				continue
			}
			if err = genCert(ia, false); err != nil {
				base.ErrorAndExit("Error generating cert for %s: %s\n", ia, err)
			}
		}
	}
	os.Exit(0)
}

func genCert(ia addr.IA, isIssuer bool) error {
	var err error
	dir := pkicmn.GetAsPath(ia)
	// Check that as.ini exists, otherwise skip directory.
	cpath := filepath.Join(dir, conf.AsConfFileName)
	if _, err = os.Stat(cpath); os.IsNotExist(err) {
		fmt.Printf("Skipping %s. Missing %s\n", dir, conf.AsConfFileName)
		return nil
	}
	a, err := conf.LoadAsConf(dir)
	if err != nil {
		return common.NewBasicError("Error loading as.ini", err, "path", cpath)
	}
	if isIssuer && a.IssuerCert == nil {
		return common.NewBasicError(fmt.Sprintf("'%s' section missing from as.ini",
			conf.IssuerSectionName), nil, "path", cpath)
	}
	// Check if file already exists.
	fname := fmt.Sprintf(pkicmn.CertNameFmt, ia.I, ia.A.FileFmt(), a.AsCert.Version)
	if _, err := os.Stat(filepath.Join(dir, pkicmn.CertsDir, fname)); err == nil && !pkicmn.Force {
		fmt.Printf("%s already exists. Use -f to overwrite.\n", fname)
		return nil
	}
	fmt.Println("Generating Certificate Chain for", ia)
	// If we are an issuer then we need to generate an issuer cert first.
	var issuerCert *cert.Certificate
	if isIssuer {
		issuerCert, err = genIssuerCert(a.IssuerCert, ia)
		if err != nil {
			return common.NewBasicError("Error generating issuer cert", err, "subject", ia)
		}
	} else {
		issuerCert, err = getIssuerCert(a.AsCert.IssuerIA)
		if err != nil {
			return common.NewBasicError("Error loading issuer cert", err, "subject", ia)
		}
	}
	if issuerCert == nil {
		return common.NewBasicError("Issuer cert not found", nil, "issuer", a.AsCert.Issuer)
	}
	// Generate the AS certificate chain.
	chain, err := genASCert(a.AsCert, ia, issuerCert)
	if err != nil {
		return common.NewBasicError("Error generating cert", err, "subject", ia)
	}
	// Check if out directory exists and if not create it.
	out := filepath.Join(dir, pkicmn.CertsDir)
	if _, err = os.Stat(out); os.IsNotExist(err) {
		if err = os.MkdirAll(out, 0755); err != nil {
			return common.NewBasicError("Cannot create output dir", err, "dir", out)
		}
	}
	// Write the cert to disk.
	raw, err := chain.JSON(true)
	if err != nil {
		return common.NewBasicError("Error json-encoding cert", err, "subject", ia)
	}
	if err = pkicmn.WriteToFile(raw, filepath.Join(out, fname), 0644); err != nil {
		return common.NewBasicError("Error writing cert", err, "subject", ia)
	}
	return nil
}

// genIssuerCert generates a new issuer certificate according to conf.
func genIssuerCert(issuerConf *conf.IssuerCert, s addr.IA) (*cert.Certificate, error) {
	c, err := genCertCommon(issuerConf.BaseCert, s, trust.IssSigKeyFile)
	if err != nil {
		return nil, err
	}
	c.CanIssue = true
	c.Issuer = s
	if c.Comment == "" {
		c.Comment = fmt.Sprintf("Issuer Certificate for %s version %d.", c.Subject, c.Version)
	}
	issuerKeyPath := filepath.Join(pkicmn.GetAsPath(c.Issuer), pkicmn.KeysDir, trust.OnKeyFile)
	// Load online root key to sign the certificate.
	issuerKey, err := trust.LoadKey(issuerKeyPath)
	if err != nil {
		return nil, err
	}
	// Sign the certificate.
	currTrcPath := filepath.Join(pkicmn.GetIsdPath(s.I), pkicmn.TRCsDir,
		fmt.Sprintf(pkicmn.TrcNameFmt, s.I, c.TRCVersion))
	currTrc, err := trc.TRCFromFile(currTrcPath, false)
	if err != nil {
		return nil, common.NewBasicError("Error reading TRC", err, "path: ", currTrcPath)
	}
	coreAs, ok := currTrc.CoreASes[s]
	if !ok {
		return nil, common.NewBasicError("Issuer of IssuerCert not found in Core ASes of TRC",
			nil, "issuer", s)
	}
	if err = c.Sign(issuerKey, coreAs.OnlineKeyAlg); err != nil {
		return nil, err
	}

	return c, nil
}

// genASCert generates a new AS certificate according to 'conf'.
func genASCert(conf *conf.AsCert, s addr.IA, issuerCert *cert.Certificate) (*cert.Chain, error) {
	c, err := genCertCommon(conf.BaseCert, s, trust.SigKeyFile)
	if err != nil {
		return nil, err
	}
	c.CanIssue = false
	c.Issuer = conf.IssuerIA
	if c.Comment == "" {
		c.Comment = fmt.Sprintf("AS Certificate for %s version %d.", c.Subject, c.Version)
	}
	// Ensure issuer can issue certificates.
	if !issuerCert.CanIssue {
		return nil, common.NewBasicError("Issuer cert not authorized to issue certs.", nil,
			"issuer", c.Issuer, "subject", c.Subject)
	}
	issuerKeyPath := filepath.Join(pkicmn.GetAsPath(conf.IssuerIA), pkicmn.KeysDir,
		trust.IssSigKeyFile)
	issuerKey, err := trust.LoadKey(issuerKeyPath)
	if err != nil {
		return nil, err
	}
	// Sign the certificate.
	if err = c.Sign(issuerKey, issuerCert.SignAlgorithm); err != nil {
		return nil, err
	}
	// Create certificate chain.
	chain := &cert.Chain{
		Leaf:   c,
		Issuer: issuerCert,
	}
	if verify {
		err = verifyChain(chain, c.Subject)
		if err != nil {
			fname := fmt.Sprintf(pkicmn.CertNameFmt, c.Subject.I, c.Subject.A, c.Version)
			return nil, common.NewBasicError("Verification FAILED", err, "cert", fname)
		}
	}
	// Write the cert to disk.
	return chain, nil
}

func genCertCommon(bc *conf.BaseCert, s addr.IA, signKeyFname string) (*cert.Certificate, error) {
	// Load signing and decryption keys that will be in the certificate.
	keyDir := filepath.Join(pkicmn.GetAsPath(s), pkicmn.KeysDir)
	signKey, err := trust.LoadKey(filepath.Join(keyDir, signKeyFname))
	if err != nil {
		return nil, err
	}
	signPub := common.RawBytes(ed25519.PrivateKey(signKey).Public().(ed25519.PublicKey))
	decKey, err := trust.LoadKey(filepath.Join(keyDir, trust.DecKeyFile))
	if err != nil {
		return nil, err
	}
	var decKeyFixed, decPub [32]byte
	copy(decKeyFixed[:], decKey)
	curve25519.ScalarBaseMult(&decPub, &decKeyFixed)
	// Determine issuingTime and calculate expiration time from validity.
	issuingTime := bc.IssuingTime
	if issuingTime == 0 {
		issuingTime = uint64(time.Now().Unix())
	}
	expirationTime := issuingTime + uint64(bc.Validity.Seconds())
	return &cert.Certificate{
		Comment:        bc.Comment,
		SubjectSignKey: signPub,
		SignAlgorithm:  bc.SignAlgorithm,
		SubjectEncKey:  decPub[:],
		EncAlgorithm:   bc.EncAlgorithm,
		Subject:        s,
		IssuingTime:    issuingTime,
		ExpirationTime: expirationTime,
		Version:        bc.Version,
		TRCVersion:     bc.TRCVersion,
	}, nil
}

// getIssuerCert returns the newest issuer certificate (if any).
func getIssuerCert(issuer addr.IA) (*cert.Certificate, error) {
	fnames, err := filepath.Glob(fmt.Sprintf("%s/*.crt",
		filepath.Join(pkicmn.GetAsPath(issuer), pkicmn.CertsDir)))
	if err != nil {
		return nil, err
	}
	var issuerCert *cert.Certificate
	for _, fname := range fnames {
		raw, err := ioutil.ReadFile(fname)
		if err != nil {
			return nil, err
		}
		chain, err := cert.ChainFromRaw(raw, false)
		if err != nil {
			return nil, err
		}
		if issuerCert == nil || chain.Issuer.Version > issuerCert.Version {
			issuerCert = chain.Issuer
		}
	}
	return issuerCert, nil
}
<|endoftext|>


<commit_before>package db

// Get is the same as sqlx.Get() but do not returns an error on empty results
// func Get(q DB, dest interface{}, query string, args ...interface{}) error {
// 	err := q.Get(dest, query, args...)
// 	if IsNotFound(err) {
// 		return nil
// 	}
// 	return err
// }
<commit_msg>chore: Remove file no longer used<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package strings

import (
	"math/rand"
	"testing"

	"github.com/mrekucci/epi/internal/epiutil"
)

type indexFn func(s, p string) int

func testIndexFn(t *testing.T, fn indexFn, fnName string) {
	for _, test := range []struct {
		s, p string
		want int
	}{
		{"", "", 0},
		{"", "x", -1},
		{"", "xxx", -1},
		{"xx", "xxx", -1},
		{"xxx", "xxx", 0},
		{"xxYxYxxYxxx", "Y", 2},
		{"xxYxYxxYxxx", "Yxx", 4},
		{"xxxyy", "xxyy", 1},
		{"xx☺yy", "x☺y", 1},
		{"xx世界yy", "世界", 2},
		{"xxxyyyzzz", "yyy", 3},
		{"xxYxYYxYxxYxxx", "Yxx", 7},
		{"xyz", "", 0},
		{"xyz", "y", 1},
		{"x", "y", -1},
		{"x", "x", 0},
		{"xyz", "x", 0},
		{"xyz", "y", 1},
		{"xyz", "z", 2},
		{"xyz", "-", -1},
	} {
		if got := fn(test.s, test.p); got != test.want {
			t.Errorf("%s(%q, %q) = %d; want %d", fnName, test.s, test.p, got, test.want)
		}
	}
}

func TestIndexNaive(t *testing.T) { testIndexFn(t, IndexNaive, "IndexNaive") }
func TestIndexRK(t *testing.T)    { testIndexFn(t, IndexRK, "IndexRK") }

func benchIndexFn(b *testing.B, size int, fn indexFn, fnName string) {
	b.StopTimer()
	for i := 0; i < b.N; i++ {
		s := epiutil.RandStr(size, "☺ abcdefghijklmnopqrstuvwxyz 世界", rand.NewSource(int64(i)))
		o, c := size/3, size*2/3
		p := s[o:c]
		b.StartTimer()
		r := fn(s, p)
		b.StopTimer()
		if r != o {
			b.Errorf("%s did not find the index of the substring", fnName)
		}
	}
}

func BenchmarkIndexNaive1e4(b *testing.B) { benchIndexFn(b, 1e4, IndexNaive, "IndexNaive") }
func BenchmarkIndexRK1e4(b *testing.B)    { benchIndexFn(b, 1e4, IndexRK, "IndexRK") }
func BenchmarkIndexNaive1e6(b *testing.B) { benchIndexFn(b, 1e6, IndexNaive, "IndexNaive") }
func BenchmarkIndexRK1e6(b *testing.B)    { benchIndexFn(b, 1e6, IndexRK, "IndexRK") }
func BenchmarkIndexNaive1e8(b *testing.B) { benchIndexFn(b, 1e8, IndexNaive, "IndexNaive") }
func BenchmarkIndexRK1e8(b *testing.B)    { benchIndexFn(b, 1e8, IndexRK, "IndexRK") }
<commit_msg>Speedup benchmark for strings.Index* functions<commit_after>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package strings

import (
	"math/rand"
	"testing"

	"github.com/mrekucci/epi/internal/epiutil"
)

type indexFn func(s, p string) int

func testIndexFn(t *testing.T, fn indexFn, fnName string) {
	for _, test := range []struct {
		s, p string
		want int
	}{
		{"", "", 0},
		{"", "x", -1},
		{"", "xxx", -1},
		{"xx", "xxx", -1},
		{"xxx", "xxx", 0},
		{"xxYxYxxYxxx", "Y", 2},
		{"xxYxYxxYxxx", "Yxx", 4},
		{"xxxyy", "xxyy", 1},
		{"xx☺yy", "x☺y", 1},
		{"xx世界yy", "世界", 2},
		{"xxxyyyzzz", "yyy", 3},
		{"xxYxYYxYxxYxxx", "Yxx", 7},
		{"xyz", "", 0},
		{"xyz", "y", 1},
		{"x", "y", -1},
		{"x", "x", 0},
		{"xyz", "x", 0},
		{"xyz", "y", 1},
		{"xyz", "z", 2},
		{"xyz", "-", -1},
	} {
		if got := fn(test.s, test.p); got != test.want {
			t.Errorf("%s(%q, %q) = %d; want %d", fnName, test.s, test.p, got, test.want)
		}
	}
}

func TestIndexNaive(t *testing.T) { testIndexFn(t, IndexNaive, "IndexNaive") }
func TestIndexRK(t *testing.T)    { testIndexFn(t, IndexRK, "IndexRK") }

func benchIndexFn(b *testing.B, size int, fn indexFn) {
	s := epiutil.RandStr(size, "☺ abcdefghijklmnopqrstuvwxyz 世界", rand.NewSource(int64(size)))
	p := s[size/3 : size*2/3]
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		fn(s, p)
	}
}

func BenchmarkIndexNaive1e4(b *testing.B) { benchIndexFn(b, 1e4, IndexNaive) }
func BenchmarkIndexRK1e4(b *testing.B)    { benchIndexFn(b, 1e4, IndexRK) }
func BenchmarkIndexNaive1e6(b *testing.B) { benchIndexFn(b, 1e6, IndexNaive) }
func BenchmarkIndexRK1e6(b *testing.B)    { benchIndexFn(b, 1e6, IndexRK) }
func BenchmarkIndexNaive1e8(b *testing.B) { benchIndexFn(b, 1e8, IndexNaive) }
func BenchmarkIndexRK1e8(b *testing.B)    { benchIndexFn(b, 1e8, IndexRK) }
<|endoftext|>


<commit_before><commit_msg>csi: create volume from snapshot---remove a blank line<commit_after><|endoftext|>