<commit_before>package access_log

import (
	"github.com/cloudfoundry/gorouter/route"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/loggregatorlib/logmessage"

	. "launchpad.net/gocheck"

	"net/http"
	"net/url"
	"runtime"
	"time"
)

type AccessLoggerSuite struct{}

var _ = Suite(&AccessLoggerSuite{})

func (s *AccessLoggerSuite) CreateAccessLogRecord() *AccessLogRecord {
	u, err := url.Parse("http://foo.bar:1234/quz?wat")
	if err != nil {
		panic(err)
	}

	req := &http.Request{
		Method:     "GET",
		URL:        u,
		Proto:      "HTTP/1.1",
		Header:     make(http.Header),
		Host:       "foo.bar",
		RemoteAddr: "1.2.3.4:5678",
	}

	req.Header.Set("Referer", "referer")
	req.Header.Set("User-Agent", "user-agent")

	res := &http.Response{
		StatusCode: http.StatusOK,
	}

	b := &route.Endpoint{
		ApplicationId: "my_awesome_id",
		Host:          "127.0.0.1",
		Port:          4567,
	}

	r := AccessLogRecord{
		Request:       req,
		Response:      res,
		RouteEndpoint: b,
		StartedAt:     time.Unix(10, 100000000),
		FirstByteAt:   time.Unix(10, 200000000),
		FinishedAt:    time.Unix(10, 300000000),
		BodyBytesSent: 42,
	}

	return &r
}

type mockEmitter struct {
	emitted bool
	appId   string
	message string
}

func (m *mockEmitter) Emit(appid, message string) {
	m.emitted = true
	m.appId = appid
	m.message = message
}

func (m *mockEmitter) EmitError(appid, message string) {

}

func (m *mockEmitter) EmitLogMessage(l *logmessage.LogMessage) {

}

func (s *AccessLoggerSuite) TestEmittingOfLogRecords(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)
	testEmitter := &mockEmitter{emitted: false}
	accessLogger.emitter = testEmitter

	accessLogger.Log(*s.CreateAccessLogRecord())
	go accessLogger.Run()
	runtime.Gosched()

	c.Check(testEmitter.emitted, Equals, true)
	c.Check(testEmitter.appId, Equals, "my_awesome_id")
	c.Check(testEmitter.message, Matches, "^.*foo.bar.*\n")
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestNotEmittingLogRecordsWithNoAppId(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)
	testEmitter := &mockEmitter{emitted: false}
	accessLogger.emitter = testEmitter

	routeEndpoint := &route.Endpoint{
		ApplicationId: "",
		Host:          "127.0.0.1",
		Port:          4567,
	}

	accessLogRecord := s.CreateAccessLogRecord()
	accessLogRecord.RouteEndpoint = routeEndpoint
	accessLogger.Log(*accessLogRecord)
	go accessLogger.Run()
	runtime.Gosched()

	c.Check(testEmitter.emitted, Equals, false)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestWritingOfLogRecordsToTheFile(c *C) {
	var fakeFile = new(test_util.FakeFile)

	accessLogger := NewFileAndLoggregatorAccessLogger(fakeFile, "localhost:9843", "secret", 42)

	accessLogger.Log(*s.CreateAccessLogRecord())
	go accessLogger.Run()
	runtime.Gosched()

	c.Check(string(fakeFile.Payload), Matches, "^.*foo.bar.*\n")
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestRealEmitterIsNotNil(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)

	c.Assert(accessLogger.emitter, Not(IsNil))
}

func (s *AccessLoggerSuite) TestNotCreatingEmitterWhenNoValidUrlIsGiven(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "this_is_not_a_url", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "localhost", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "10.10.16.14", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestCreatingEmitterWithIPAddressAndPort(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "10.10.16.14:5432", "secret", 42)

	c.Assert(accessLogger.emitter, NotNil)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestCreatingEmitterWithLocalhostt(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:123", "secret", 42)

	c.Assert(accessLogger.emitter, NotNil)
	accessLogger.Stop()
}

type nullWriter struct{}

func (n nullWriter) Write(b []byte) (int, error) {
	return len(b), nil
}

func (s *AccessLoggerSuite) BenchmarkAccessLogRecordWriteTo(c *C) {
	r := s.CreateAccessLogRecord()
	w := nullWriter{}

	for i := 0; i < c.N; i++ {
		r.WriteTo(w)
	}
}
<commit_msg>Fix flaky test in access_log<commit_after>package access_log

import (
	"github.com/cloudfoundry/gorouter/route"
	"github.com/cloudfoundry/gorouter/test_util"
	"github.com/cloudfoundry/loggregatorlib/logmessage"

	. "launchpad.net/gocheck"

	"net/http"
	"net/url"
	"runtime"
	"time"
)

type AccessLoggerSuite struct{}

var _ = Suite(&AccessLoggerSuite{})

func (s *AccessLoggerSuite) CreateAccessLogRecord() *AccessLogRecord {
	u, err := url.Parse("http://foo.bar:1234/quz?wat")
	if err != nil {
		panic(err)
	}

	req := &http.Request{
		Method:     "GET",
		URL:        u,
		Proto:      "HTTP/1.1",
		Header:     make(http.Header),
		Host:       "foo.bar",
		RemoteAddr: "1.2.3.4:5678",
	}

	req.Header.Set("Referer", "referer")
	req.Header.Set("User-Agent", "user-agent")

	res := &http.Response{
		StatusCode: http.StatusOK,
	}

	b := &route.Endpoint{
		ApplicationId: "my_awesome_id",
		Host:          "127.0.0.1",
		Port:          4567,
	}

	r := AccessLogRecord{
		Request:       req,
		Response:      res,
		RouteEndpoint: b,
		StartedAt:     time.Unix(10, 100000000),
		FirstByteAt:   time.Unix(10, 200000000),
		FinishedAt:    time.Unix(10, 300000000),
		BodyBytesSent: 42,
	}

	return &r
}

type mockEmitter struct {
	emitted bool
	appId   string
	message string
	done    chan bool
}

func (m *mockEmitter) Emit(appid, message string) {
	m.emitted = true
	m.appId = appid
	m.message = message
	m.done <- true
}

func (m *mockEmitter) EmitError(appid, message string) {
}

func (m *mockEmitter) EmitLogMessage(l *logmessage.LogMessage) {
}

func (s *AccessLoggerSuite) TestEmittingOfLogRecords(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)
	testEmitter := &mockEmitter{emitted: false, done: make(chan bool)}
	accessLogger.emitter = testEmitter

	accessLogger.Log(*s.CreateAccessLogRecord())
	go accessLogger.Run()
	runtime.Gosched()

	timeout := make(chan bool, 1)
	go func() {
		time.Sleep(1*time.Second)
		timeout <- true
	}()

	select {
	case <-testEmitter.done:
			c.Check(testEmitter.emitted, Equals, true)
			c.Check(testEmitter.appId, Equals, "my_awesome_id")
			c.Check(testEmitter.message, Matches, "^.*foo.bar.*\n")
	case <-timeout:
			c.FailNow()
	}

	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestNotEmittingLogRecordsWithNoAppId(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)
	testEmitter := &mockEmitter{emitted: false, done: make(chan bool)}
	accessLogger.emitter = testEmitter

	routeEndpoint := &route.Endpoint{
		ApplicationId: "",
		Host:          "127.0.0.1",
		Port:          4567,
	}

	accessLogRecord := s.CreateAccessLogRecord()
	accessLogRecord.RouteEndpoint = routeEndpoint
	accessLogger.Log(*accessLogRecord)
	go accessLogger.Run()
	runtime.Gosched()

	c.Check(testEmitter.emitted, Equals, false)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestWritingOfLogRecordsToTheFile(c *C) {
	var fakeFile = new(test_util.FakeFile)

	accessLogger := NewFileAndLoggregatorAccessLogger(fakeFile, "localhost:9843", "secret", 42)

	accessLogger.Log(*s.CreateAccessLogRecord())
	go accessLogger.Run()
	runtime.Gosched()

	c.Check(string(fakeFile.Payload), Matches, "^.*foo.bar.*\n")
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestRealEmitterIsNotNil(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:9843", "secret", 42)

	c.Assert(accessLogger.emitter, Not(IsNil))
}

func (s *AccessLoggerSuite) TestNotCreatingEmitterWhenNoValidUrlIsGiven(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "this_is_not_a_url", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "localhost", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "10.10.16.14", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()

	accessLogger = NewFileAndLoggregatorAccessLogger(nil, "", "secret", 42)
	c.Assert(accessLogger.emitter, IsNil)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestCreatingEmitterWithIPAddressAndPort(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "10.10.16.14:5432", "secret", 42)

	c.Assert(accessLogger.emitter, NotNil)
	accessLogger.Stop()
}

func (s *AccessLoggerSuite) TestCreatingEmitterWithLocalhostt(c *C) {
	accessLogger := NewFileAndLoggregatorAccessLogger(nil, "localhost:123", "secret", 42)

	c.Assert(accessLogger.emitter, NotNil)
	accessLogger.Stop()
}

type nullWriter struct{}

func (n nullWriter) Write(b []byte) (int, error) {
	return len(b), nil
}

func (s *AccessLoggerSuite) BenchmarkAccessLogRecordWriteTo(c *C) {
	r := s.CreateAccessLogRecord()
	w := nullWriter{}

	for i := 0; i < c.N; i++ {
		r.WriteTo(w)
	}
}
<|endoftext|>


<commit_before>// +build ignore

// Universal LED blinker, works OOTB on a RPi / BBB.

package main

import (
	"flag"
	"os"
	"os/signal"
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
)

func main() {
	flag.Parse()

	panicIf(embd.InitLED())
	defer embd.CloseLED()

	led, err := embd.NewLED(ledToBlink())
	panicIf(err)
	defer led.Off()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	defer signal.Stop(quit)

	for {
		select {
		case <-time.After(250 * time.Millisecond):
			panicIf(led.Toggle())
		case <-quit:
			return
		}
	}
}

func ledToBlink() string {
	host, _, err := embd.DetectHost()
	panicIf(err)

	switch host {
	case embd.HostRPi:
		return "LED0"
	case embd.HostBBB:
		return "USR3"
	}

	panic("Unsupported host")
}

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>samples: reduce delay in universalblinker to 200 ms<commit_after>// +build ignore

// Universal LED blinker, works OOTB on a RPi / BBB.

package main

import (
	"flag"
	"os"
	"os/signal"
	"time"

	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
)

func main() {
	flag.Parse()

	panicIf(embd.InitLED())
	defer embd.CloseLED()

	led, err := embd.NewLED(ledToBlink())
	panicIf(err)
	defer led.Off()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	defer signal.Stop(quit)

	for {
		select {
		case <-time.After(200 * time.Millisecond):
			panicIf(led.Toggle())
		case <-quit:
			return
		}
	}
}

func ledToBlink() string {
	host, _, err := embd.DetectHost()
	panicIf(err)

	switch host {
	case embd.HostRPi:
		return "LED0"
	case embd.HostBBB:
		return "USR3"
	}

	panic("Unsupported host")
}

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package nats

import (
	"errors"
	"strings"
	"time"

	"github.com/nats-io/nats"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/guid"
	"github.com/weaveworks/flux/platform"
	fluxrpc "github.com/weaveworks/flux/platform/rpc"
)

const (
	// We give subscriptions an age limit, because if we have very
	// long-lived connections we don't get fine-enough-grained usage
	// metrics
	maxAge         = 2 * time.Hour
	defaultTimeout = 5 * time.Second
	// Apply can take minutes, simply because some deployments take a
	// while to roll out for whatever reason
	defaultApplyTimeout = 20 * time.Minute
	presenceTick        = 50 * time.Millisecond
	encoderType         = nats.JSON_ENCODER

	methodKick         = ".Platform.Kick"
	methodPing         = ".Platform.Ping"
	methodVersion      = ".Platform.Version"
	methodAllServices  = ".Platform.AllServices"
	methodSomeServices = ".Platform.SomeServices"
	methodApply        = ".Platform.Apply"
	methodExport       = ".Platform.Export"
	methodSync         = ".Platform.Sync"
)

var applyTimeout = defaultApplyTimeout
var timeout = defaultTimeout

type NATS struct {
	url string
	// It's convenient to send (or request) on an encoding connection,
	// since that'll do encoding work for us. When receiving though,
	// we want to decode based on the method as given in the subject,
	// so we use a regular connection and do the decoding ourselves.
	enc     *nats.EncodedConn
	raw     *nats.Conn
	metrics platform.BusMetrics
}

var _ platform.MessageBus = &NATS{}

func NewMessageBus(url string, metrics platform.BusMetrics) (*NATS, error) {
	conn, err := nats.Connect(url, nats.MaxReconnects(-1))
	if err != nil {
		return nil, err
	}
	encConn, err := nats.NewEncodedConn(conn, encoderType)
	if err != nil {
		return nil, err
	}
	return &NATS{
		url:     url,
		raw:     conn,
		enc:     encConn,
		metrics: metrics,
	}, nil
}

// Wait up to `timeout` for a particular instance to connect. Mostly
// useful for synchronising during testing.
func (n *NATS) AwaitPresence(instID flux.InstanceID, timeout time.Duration) error {
	timer := time.After(timeout)
	attempts := time.NewTicker(presenceTick)
	defer attempts.Stop()

	for {
		select {
		case <-attempts.C:
			if err := n.Ping(instID); err == nil {
				return nil
			}
		case <-timer:
			return platform.UnavailableError(errors.New("presence timeout"))
		}
	}
}

func (n *NATS) Ping(instID flux.InstanceID) error {
	var response PingResponse
	err := n.enc.Request(string(instID)+methodPing, ping{}, &response, timeout)
	if err == nil {
		err = extractError(response.ErrorResponse)
	}
	return err
}

// ErrorResponse is for dropping into responses so they have
// appropriate fields. The field `Error` carries either an empty
// string (no error), or the error message to be reconstituted as an
// error). The field `Fatal` indicates that the error resulted in the
// connection to the daemon being torn down.
type ErrorResponse struct {
	Error string
	Fatal bool
}

type AllServicesResponse struct {
	Services []platform.Service
	ErrorResponse
}

type SomeServicesResponse struct {
	Services []platform.Service
	ErrorResponse
}

type ApplyResponse struct {
	Result fluxrpc.ApplyResult
	ErrorResponse
}

type ping struct{}

type PingResponse struct {
	ErrorResponse
}

type version struct{}

type VersionResponse struct {
	Version string
	ErrorResponse
}

type export struct{}

type ExportResponse struct {
	Config []byte
	ErrorResponse
}

type SyncResponse struct {
	Result fluxrpc.SyncResult
	ErrorResponse
}

func extractError(resp ErrorResponse) error {
	if resp.Error != "" {
		if resp.Fatal {
			return platform.FatalError{errors.New(resp.Error)}
		}
		return platform.ClusterError(errors.New(resp.Error))
	}
	return nil
}

func makeErrorResponse(err error) (resp ErrorResponse) {
	if err == nil {
		return resp
	}
	if _, ok := err.(platform.FatalError); ok {
		resp.Fatal = true
	}
	resp.Error = err.Error()
	return resp
}

// natsPlatform collects the things you need to make a request via NATS
// together, and implements platform.Platform using that mechanism.
type natsPlatform struct {
	conn     *nats.EncodedConn
	instance string
}

func (r *natsPlatform) AllServices(ns string, ig flux.ServiceIDSet) ([]platform.Service, error) {
	var response AllServicesResponse
	if err := r.conn.Request(r.instance+methodAllServices, fluxrpc.AllServicesRequestV4{ns, ig}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Services, extractError(response.ErrorResponse)
}

func (r *natsPlatform) SomeServices(incl []flux.ServiceID) ([]platform.Service, error) {
	var response SomeServicesResponse
	if err := r.conn.Request(r.instance+methodSomeServices, incl, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Services, extractError(response.ErrorResponse)
}

// Call Apply on the remote platform. Note that we use a much longer
// timeout, because for now at least, Applys can take an arbitrary
// amount of time, and we don't want to return an error if it's simply
// taking a while. The downside is that if the platform is actually
// not present, this won't return at all. This is somewhat mitigated
// because applys are done after other RPCs which have the normal
// timeout, but better would be to split Applys into RPCs which can
// each have a short timeout.
func (r *natsPlatform) Apply(specs []platform.ServiceDefinition) error {
	var response ApplyResponse
	if err := r.conn.Request(r.instance+methodApply, specs, &response, applyTimeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	if len(response.Result) > 0 {
		errs := platform.ApplyError{}
		for s, e := range response.Result {
			errs[s] = errors.New(e)
		}
		return errs
	}
	return extractError(response.ErrorResponse)
}

func (r *natsPlatform) Ping() error {
	var response PingResponse
	if err := r.conn.Request(r.instance+methodPing, ping{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	return extractError(response.ErrorResponse)
}

func (r *natsPlatform) Version() (string, error) {
	var response VersionResponse
	if err := r.conn.Request(r.instance+methodVersion, version{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return "", err
	}
	return response.Version, extractError(response.ErrorResponse)
}

func (r *natsPlatform) Export() ([]byte, error) {
	var response ExportResponse
	if err := r.conn.Request(r.instance+methodExport, export{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Config, extractError(response.ErrorResponse)
}

func (r *natsPlatform) Sync(spec platform.SyncDef) error {
	var response SyncResponse
	// I use the applyTimeout here to be conservative; just applying
	// things should take much less time (though it'll still be in the
	// seconds)
	if err := r.conn.Request(r.instance+methodSync, spec, &response, applyTimeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	if len(response.Result) > 0 {
		errs := platform.SyncError{}
		for s, e := range response.Result {
			errs[s] = errors.New(e)
		}
		return errs
	}
	return extractError(response.ErrorResponse)
}

// --- end Platform implementation

// Connect returns a platform.Platform implementation that can be used
// to talk to a particular instance.
func (n *NATS) Connect(instID flux.InstanceID) (platform.Platform, error) {
	return &natsPlatform{
		conn:     n.enc,
		instance: string(instID),
	}, nil
}

// Subscribe registers a remote platform.Platform implementation as
// the daemon for an instance (identified by instID). Any
// platform.FatalError returned when processing requests will result
// in the platform being deregistered, with the error put on the
// channel `done`.
func (n *NATS) Subscribe(instID flux.InstanceID, remote platform.Platform, done chan<- error) {
	encoder := nats.EncoderForType(encoderType)

	requests := make(chan *nats.Msg)
	sub, err := n.raw.ChanSubscribe(string(instID)+".Platform.>", requests)
	if err != nil {
		done <- err
		return
	}

	// It's possible that more than one connection for a particular
	// instance will arrive at the service. To prevent confusion, when
	// a subscription arrives, it sends a "kick" message with a unique
	// ID (so it can recognise its own kick message). Any other
	// subscription for the instance _should_ then exit upon receipt
	// of the kick.
	myID := guid.New()
	n.raw.Publish(string(instID)+methodKick, []byte(myID))

	errc := make(chan error)

	processRequest := func(request *nats.Msg) {
		var err error
		switch {
		case strings.HasSuffix(request.Subject, methodKick):
			id := string(request.Data)
			if id != myID {
				n.metrics.IncrKicks(instID)
				err = platform.FatalError{errors.New("Kicked by new subscriber " + id)}
			}
		case strings.HasSuffix(request.Subject, methodPing):
			var p ping
			err = encoder.Decode(request.Subject, request.Data, &p)
			if err == nil {
				err = remote.Ping()
			}
			n.enc.Publish(request.Reply, PingResponse{makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodVersion):
			var vsn string
			vsn, err = remote.Version()
			n.enc.Publish(request.Reply, VersionResponse{vsn, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodAllServices):
			var (
				req fluxrpc.AllServicesRequestV4
				res []platform.Service
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				res, err = remote.AllServices(req.MaybeNamespace, req.Ignored)
			}
			n.enc.Publish(request.Reply, AllServicesResponse{res, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodSomeServices):
			var (
				req []flux.ServiceID
				res []platform.Service
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				res, err = remote.SomeServices(req)
			}
			n.enc.Publish(request.Reply, SomeServicesResponse{res, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodApply):
			var (
				req []platform.ServiceDefinition
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				err = remote.Apply(req)
			}
			response := ApplyResponse{}
			switch applyErr := err.(type) {
			case platform.ApplyError:
				result := fluxrpc.ApplyResult{}
				for s, e := range applyErr {
					result[s] = e.Error()
				}
				response.Result = result
			default:
				response.ErrorResponse = makeErrorResponse(err)
			}
			n.enc.Publish(request.Reply, response)
		case strings.HasSuffix(request.Subject, methodExport):
			var (
				req   export
				bytes []byte
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				bytes, err = remote.Export()
			}
			n.enc.Publish(request.Reply, ExportResponse{bytes, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodSync):
			var def platform.SyncDef
			err = encoder.Decode(request.Subject, request.Data, &def)
			if err == nil {
				err = remote.Sync(def)
			}
			response := SyncResponse{}
			switch syncErr := err.(type) {
			case platform.SyncError:
				result := fluxrpc.SyncResult{}
				for s, e := range syncErr {
					result[s] = e.Error()
				}
				response.Result = result
			default:
				response.ErrorResponse = makeErrorResponse(err)
			}
			n.enc.Publish(request.Reply, response)
		default:
			err = errors.New("unknown message: " + request.Subject)
		}
		if _, ok := err.(platform.FatalError); ok && err != nil {
			select {
			case errc <- err:
			default:
				// If the error channel is closed, it means that a
				// different RPC goroutine had a fatal error that
				// triggered the clean up and return of the parent
				// goroutine. It is likely that the error we have
				// encountered is due to the closure of the RPC
				// client whilst our request was still in progress
				// - don't panic.
			}
		}
	}

	go func() {
		forceReconnect := time.NewTimer(maxAge)
		defer forceReconnect.Stop()
		for {
			select {
			// If both an error and a request are available, the runtime may
			// chose (by uniform pseudo-random selection) to process the
			// request first. This may seem like a problem, but even if we were
			// guaranteed to prefer the error channel there would still be a
			// race between selecting a request here and a failing goroutine
			// putting an error into the channel - it's an unavoidable
			// consequence of asynchronous request handling. The error will get
			// selected and handled soon enough.
			case err := <-errc:
				sub.Unsubscribe()
				close(requests)
				done <- err
				return
			case request := <-requests:
				// Some of these operations (Apply in particular) can block for a long time;
				// dispatch in a goroutine and deliver any errors back to us so that we can
				// clean up on any hard failures.
				go processRequest(request)
			case <-forceReconnect.C:
				sub.Unsubscribe()
				close(requests)
				done <- nil
				return
			}
		}
	}()
}
<commit_msg>Bring the bus implementation of Ping up-to-date<commit_after>package nats

import (
	"errors"
	"strings"
	"time"

	"github.com/nats-io/nats"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/guid"
	"github.com/weaveworks/flux/platform"
	fluxrpc "github.com/weaveworks/flux/platform/rpc"
)

const (
	// We give subscriptions an age limit, because if we have very
	// long-lived connections we don't get fine-enough-grained usage
	// metrics
	maxAge         = 2 * time.Hour
	defaultTimeout = 5 * time.Second
	// Apply can take minutes, simply because some deployments take a
	// while to roll out for whatever reason
	defaultApplyTimeout = 20 * time.Minute
	presenceTick        = 50 * time.Millisecond
	encoderType         = nats.JSON_ENCODER

	methodKick         = ".Platform.Kick"
	methodPing         = ".Platform.Ping"
	methodVersion      = ".Platform.Version"
	methodAllServices  = ".Platform.AllServices"
	methodSomeServices = ".Platform.SomeServices"
	methodApply        = ".Platform.Apply"
	methodExport       = ".Platform.Export"
	methodSync         = ".Platform.Sync"
)

var applyTimeout = defaultApplyTimeout
var timeout = defaultTimeout

type NATS struct {
	url string
	// It's convenient to send (or request) on an encoding connection,
	// since that'll do encoding work for us. When receiving though,
	// we want to decode based on the method as given in the subject,
	// so we use a regular connection and do the decoding ourselves.
	enc     *nats.EncodedConn
	raw     *nats.Conn
	metrics platform.BusMetrics
}

var _ platform.MessageBus = &NATS{}

func NewMessageBus(url string, metrics platform.BusMetrics) (*NATS, error) {
	conn, err := nats.Connect(url, nats.MaxReconnects(-1))
	if err != nil {
		return nil, err
	}
	encConn, err := nats.NewEncodedConn(conn, encoderType)
	if err != nil {
		return nil, err
	}
	return &NATS{
		url:     url,
		raw:     conn,
		enc:     encConn,
		metrics: metrics,
	}, nil
}

// Wait up to `timeout` for a particular instance to connect. Mostly
// useful for synchronising during testing.
func (n *NATS) AwaitPresence(instID flux.InstanceID, timeout time.Duration) error {
	timer := time.After(timeout)
	attempts := time.NewTicker(presenceTick)
	defer attempts.Stop()

	for {
		select {
		case <-attempts.C:
			if err := n.Ping(instID); err == nil {
				return nil
			}
		case <-timer:
			return platform.UnavailableError(errors.New("presence timeout"))
		}
	}
}

func (n *NATS) Ping(instID flux.InstanceID) error {
	var response PingResponse
	if err := n.enc.Request(string(instID)+methodPing, ping{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	return extractError(response.ErrorResponse)
}

// ErrorResponse is for dropping into responses so they have
// appropriate fields. The field `Error` carries either an empty
// string (no error), or the error message to be reconstituted as an
// error). The field `Fatal` indicates that the error resulted in the
// connection to the daemon being torn down.
type ErrorResponse struct {
	Error string
	Fatal bool
}

type AllServicesResponse struct {
	Services []platform.Service
	ErrorResponse
}

type SomeServicesResponse struct {
	Services []platform.Service
	ErrorResponse
}

type ApplyResponse struct {
	Result fluxrpc.ApplyResult
	ErrorResponse
}

type ping struct{}

type PingResponse struct {
	ErrorResponse
}

type version struct{}

type VersionResponse struct {
	Version string
	ErrorResponse
}

type export struct{}

type ExportResponse struct {
	Config []byte
	ErrorResponse
}

type SyncResponse struct {
	Result fluxrpc.SyncResult
	ErrorResponse
}

func extractError(resp ErrorResponse) error {
	if resp.Error != "" {
		if resp.Fatal {
			return platform.FatalError{errors.New(resp.Error)}
		}
		return platform.ClusterError(errors.New(resp.Error))
	}
	return nil
}

func makeErrorResponse(err error) (resp ErrorResponse) {
	if err == nil {
		return resp
	}
	if _, ok := err.(platform.FatalError); ok {
		resp.Fatal = true
	}
	resp.Error = err.Error()
	return resp
}

// natsPlatform collects the things you need to make a request via NATS
// together, and implements platform.Platform using that mechanism.
type natsPlatform struct {
	conn     *nats.EncodedConn
	instance string
}

func (r *natsPlatform) AllServices(ns string, ig flux.ServiceIDSet) ([]platform.Service, error) {
	var response AllServicesResponse
	if err := r.conn.Request(r.instance+methodAllServices, fluxrpc.AllServicesRequestV4{ns, ig}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Services, extractError(response.ErrorResponse)
}

func (r *natsPlatform) SomeServices(incl []flux.ServiceID) ([]platform.Service, error) {
	var response SomeServicesResponse
	if err := r.conn.Request(r.instance+methodSomeServices, incl, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Services, extractError(response.ErrorResponse)
}

// Call Apply on the remote platform. Note that we use a much longer
// timeout, because for now at least, Applys can take an arbitrary
// amount of time, and we don't want to return an error if it's simply
// taking a while. The downside is that if the platform is actually
// not present, this won't return at all. This is somewhat mitigated
// because applys are done after other RPCs which have the normal
// timeout, but better would be to split Applys into RPCs which can
// each have a short timeout.
func (r *natsPlatform) Apply(specs []platform.ServiceDefinition) error {
	var response ApplyResponse
	if err := r.conn.Request(r.instance+methodApply, specs, &response, applyTimeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	if len(response.Result) > 0 {
		errs := platform.ApplyError{}
		for s, e := range response.Result {
			errs[s] = errors.New(e)
		}
		return errs
	}
	return extractError(response.ErrorResponse)
}

func (r *natsPlatform) Ping() error {
	var response PingResponse
	if err := r.conn.Request(r.instance+methodPing, ping{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	return extractError(response.ErrorResponse)
}

func (r *natsPlatform) Version() (string, error) {
	var response VersionResponse
	if err := r.conn.Request(r.instance+methodVersion, version{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return "", err
	}
	return response.Version, extractError(response.ErrorResponse)
}

func (r *natsPlatform) Export() ([]byte, error) {
	var response ExportResponse
	if err := r.conn.Request(r.instance+methodExport, export{}, &response, timeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return nil, err
	}
	return response.Config, extractError(response.ErrorResponse)
}

func (r *natsPlatform) Sync(spec platform.SyncDef) error {
	var response SyncResponse
	// I use the applyTimeout here to be conservative; just applying
	// things should take much less time (though it'll still be in the
	// seconds)
	if err := r.conn.Request(r.instance+methodSync, spec, &response, applyTimeout); err != nil {
		if err == nats.ErrTimeout {
			err = platform.UnavailableError(err)
		}
		return err
	}
	if len(response.Result) > 0 {
		errs := platform.SyncError{}
		for s, e := range response.Result {
			errs[s] = errors.New(e)
		}
		return errs
	}
	return extractError(response.ErrorResponse)
}

// --- end Platform implementation

// Connect returns a platform.Platform implementation that can be used
// to talk to a particular instance.
func (n *NATS) Connect(instID flux.InstanceID) (platform.Platform, error) {
	return &natsPlatform{
		conn:     n.enc,
		instance: string(instID),
	}, nil
}

// Subscribe registers a remote platform.Platform implementation as
// the daemon for an instance (identified by instID). Any
// platform.FatalError returned when processing requests will result
// in the platform being deregistered, with the error put on the
// channel `done`.
func (n *NATS) Subscribe(instID flux.InstanceID, remote platform.Platform, done chan<- error) {
	encoder := nats.EncoderForType(encoderType)

	requests := make(chan *nats.Msg)
	sub, err := n.raw.ChanSubscribe(string(instID)+".Platform.>", requests)
	if err != nil {
		done <- err
		return
	}

	// It's possible that more than one connection for a particular
	// instance will arrive at the service. To prevent confusion, when
	// a subscription arrives, it sends a "kick" message with a unique
	// ID (so it can recognise its own kick message). Any other
	// subscription for the instance _should_ then exit upon receipt
	// of the kick.
	myID := guid.New()
	n.raw.Publish(string(instID)+methodKick, []byte(myID))

	errc := make(chan error)

	processRequest := func(request *nats.Msg) {
		var err error
		switch {
		case strings.HasSuffix(request.Subject, methodKick):
			id := string(request.Data)
			if id != myID {
				n.metrics.IncrKicks(instID)
				err = platform.FatalError{errors.New("Kicked by new subscriber " + id)}
			}
		case strings.HasSuffix(request.Subject, methodPing):
			var p ping
			err = encoder.Decode(request.Subject, request.Data, &p)
			if err == nil {
				err = remote.Ping()
			}
			n.enc.Publish(request.Reply, PingResponse{makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodVersion):
			var vsn string
			vsn, err = remote.Version()
			n.enc.Publish(request.Reply, VersionResponse{vsn, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodAllServices):
			var (
				req fluxrpc.AllServicesRequestV4
				res []platform.Service
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				res, err = remote.AllServices(req.MaybeNamespace, req.Ignored)
			}
			n.enc.Publish(request.Reply, AllServicesResponse{res, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodSomeServices):
			var (
				req []flux.ServiceID
				res []platform.Service
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				res, err = remote.SomeServices(req)
			}
			n.enc.Publish(request.Reply, SomeServicesResponse{res, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodApply):
			var (
				req []platform.ServiceDefinition
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				err = remote.Apply(req)
			}
			response := ApplyResponse{}
			switch applyErr := err.(type) {
			case platform.ApplyError:
				result := fluxrpc.ApplyResult{}
				for s, e := range applyErr {
					result[s] = e.Error()
				}
				response.Result = result
			default:
				response.ErrorResponse = makeErrorResponse(err)
			}
			n.enc.Publish(request.Reply, response)
		case strings.HasSuffix(request.Subject, methodExport):
			var (
				req   export
				bytes []byte
			)
			err = encoder.Decode(request.Subject, request.Data, &req)
			if err == nil {
				bytes, err = remote.Export()
			}
			n.enc.Publish(request.Reply, ExportResponse{bytes, makeErrorResponse(err)})
		case strings.HasSuffix(request.Subject, methodSync):
			var def platform.SyncDef
			err = encoder.Decode(request.Subject, request.Data, &def)
			if err == nil {
				err = remote.Sync(def)
			}
			response := SyncResponse{}
			switch syncErr := err.(type) {
			case platform.SyncError:
				result := fluxrpc.SyncResult{}
				for s, e := range syncErr {
					result[s] = e.Error()
				}
				response.Result = result
			default:
				response.ErrorResponse = makeErrorResponse(err)
			}
			n.enc.Publish(request.Reply, response)
		default:
			err = errors.New("unknown message: " + request.Subject)
		}
		if _, ok := err.(platform.FatalError); ok && err != nil {
			select {
			case errc <- err:
			default:
				// If the error channel is closed, it means that a
				// different RPC goroutine had a fatal error that
				// triggered the clean up and return of the parent
				// goroutine. It is likely that the error we have
				// encountered is due to the closure of the RPC
				// client whilst our request was still in progress
				// - don't panic.
			}
		}
	}

	go func() {
		forceReconnect := time.NewTimer(maxAge)
		defer forceReconnect.Stop()
		for {
			select {
			// If both an error and a request are available, the runtime may
			// chose (by uniform pseudo-random selection) to process the
			// request first. This may seem like a problem, but even if we were
			// guaranteed to prefer the error channel there would still be a
			// race between selecting a request here and a failing goroutine
			// putting an error into the channel - it's an unavoidable
			// consequence of asynchronous request handling. The error will get
			// selected and handled soon enough.
			case err := <-errc:
				sub.Unsubscribe()
				close(requests)
				done <- err
				return
			case request := <-requests:
				// Some of these operations (Apply in particular) can block for a long time;
				// dispatch in a goroutine and deliver any errors back to us so that we can
				// clean up on any hard failures.
				go processRequest(request)
			case <-forceReconnect.C:
				sub.Unsubscribe()
				close(requests)
				done <- nil
				return
			}
		}
	}()
}
<|endoftext|>


<commit_before>package macvlan

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/ipallocator"
	"github.com/docker/libnetwork/types"
	"github.com/gorilla/mux"
	"github.com/samalba/dockerclient"
	"github.com/vishvananda/netlink"
)

const (
	MethodReceiver       = "NetworkDriver"
	defaultRoute         = "0.0.0.0/0"
	containerIfacePrefix = "eth"
)

type Driver interface {
	Listen(string) error
}

// Struct for binding bridge options CLI flags
type bridgeOpts struct {
	brName   string
	brSubnet net.IPNet
	brIP     net.IPNet
}

type driver struct {
	dockerer
	ipAllocator *ipallocator.IPAllocator
	version     string
	network     string
	cidr        *net.IPNet
	nameserver  string
}

func New(version string) (Driver, error) {
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return nil, fmt.Errorf("could not connect to docker: %s", err)
	}

	ipAllocator := ipallocator.New()
	d := &driver{
		dockerer: dockerer{
			client: docker,
		},
		ipAllocator: ipAllocator,
		version:     version,
	}
	return d, nil
}

func (driver *driver) Listen(socket string) error {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.Methods("GET").Path("/status").HandlerFunc(driver.status)
	router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(driver.handshake)

	handleMethod := func(method string, h http.HandlerFunc) {
		router.Methods("POST").Path(fmt.Sprintf("/%s.%s", MethodReceiver, method)).HandlerFunc(h)
	}
	handleMethod("CreateNetwork", driver.createNetwork)
	handleMethod("DeleteNetwork", driver.deleteNetwork)
	handleMethod("CreateEndpoint", driver.createEndpoint)
	handleMethod("DeleteEndpoint", driver.deleteEndpoint)
	handleMethod("EndpointOperInfo", driver.infoEndpoint)
	handleMethod("Join", driver.joinEndpoint)
	handleMethod("Leave", driver.leaveEndpoint)
	var (
		listener net.Listener
		err      error
	)

	listener, err = net.Listen("unix", socket)
	if err != nil {
		return err
	}

	return http.Serve(listener, router)
}

func notFound(w http.ResponseWriter, r *http.Request) {
	log.Warnf("[plugin] Not found: %+v", r)
	http.NotFound(w, r)
}

func sendError(w http.ResponseWriter, msg string, code int) {
	log.Errorf("%d %s", code, msg)
	http.Error(w, msg, code)
}

func errorResponsef(w http.ResponseWriter, fmtString string, item ...interface{}) {
	json.NewEncoder(w).Encode(map[string]string{
		"Err": fmt.Sprintf(fmtString, item...),
	})
}

func objectResponse(w http.ResponseWriter, obj interface{}) {
	if err := json.NewEncoder(w).Encode(obj); err != nil {
		sendError(w, "Could not JSON encode response", http.StatusInternalServerError)
		return
	}
}

func emptyResponse(w http.ResponseWriter) {
	json.NewEncoder(w).Encode(map[string]string{})
}

type handshakeResp struct {
	Implements []string
}

func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{"NetworkDriver"},
	})
	if err != nil {
		log.Fatalf("handshake encode: %s", err)
		sendError(w, "encode error", http.StatusInternalServerError)
		return
	}
	log.Debug("Handshake completed")
}

func (driver *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("macvlan plugin", driver.version))
}

type networkCreate struct {
	NetworkID string
	Options   map[string]interface{}
}

func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) {
	var create networkCreate
	err := json.NewDecoder(r.Body).Decode(&create)
	if err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}

	if driver.network != "" {
		errorResponsef(w, "You get just one network, and you already made %s", driver.network)
		return
	}
	driver.network = create.NetworkID

	// Parse the network address from the user supplied or default container network
	_, ipNet, err := net.ParseCIDR(defaultSubnet)
	if err != nil {
		log.Warnf("Error parsing cidr from the default subnet: %s", err)
	}
	//	cidr := ipNet
	driver.cidr = ipNet
	driver.ipAllocator.RequestIP(driver.cidr, nil)

	emptyResponse(w)
}

type networkDelete struct {
	NetworkID string
}

func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) {
	var delete networkDelete
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	log.Debugf("Delete network request: %+v", &delete)
	if delete.NetworkID != driver.network {
		log.Debugf("network not found: %+v", &delete)
		errorResponsef(w, "Network %s not found", delete.NetworkID)
		return
	}
	driver.network = ""
	emptyResponse(w)
	log.Infof("Destroy network %s", delete.NetworkID)
}

type endpointCreate struct {
	NetworkID  string
	EndpointID string
	Interfaces []*iface
	Options    map[string]interface{}
}

type iface struct {
	ID         int
	SrcName    string
	DstPrefix  string
	Address    string
	MacAddress string
}

type endpointResponse struct {
	Interfaces []*iface
}

func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) {
	var create endpointCreate
	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}

	netID := create.NetworkID
	endID := create.EndpointID

	if netID != driver.network {
		log.Warnf("Network not found, [ %s ]", netID)
		errorResponsef(w, "No such network %s", netID)
		return
	}
	// Request an IP address from libnetwork based on the cidr scope
	// TODO: Add a user defined static ip addr option
	allocatedIP, err := driver.ipAllocator.RequestIP(driver.cidr, nil)
	if err != nil || allocatedIP == nil {
		log.Errorf("Unable to obtain an IP address from libnetwork ipam: %s", err)
		errorResponsef(w, "%s", err)
		return
	}
	// generate a mac address for the pending container
	mac := makeMac(allocatedIP)
	// Have to convert container IP to a string ip/mask format
	_, containerMask := driver.cidr.Mask.Size()
	containerAddress := allocatedIP.String() + "/" + strconv.Itoa(containerMask)
	log.Infof("The allocated container IP is: [ %s ]", allocatedIP.String())

	respIface := &iface{
		Address:    containerAddress,
		MacAddress: mac,
	}
	resp := &endpointResponse{
		Interfaces: []*iface{respIface},
	}
	objectResponse(w, resp)
	log.Debugf("Create endpoint %s %+v", endID, resp)
}

type endpointDelete struct {
	NetworkID  string
	EndpointID string
}

func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
	var delete endpointDelete
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Delete endpoint request: %+v", &delete)
	emptyResponse(w)
	// null check cidr in case driver restarted and doesnt know the network to avoid panic
	if driver.cidr == nil {
		return
	}
	// ReleaseIP releases an ip back to a network
	if err := driver.ipAllocator.ReleaseIP(driver.cidr, driver.cidr.IP); err != nil {
		log.Warnf("error releasing IP: %s", err)
	}
	log.Debugf("Delete endpoint %s", delete.EndpointID)

	containerLink := delete.EndpointID[:5]

	log.Infof("Removing unused macvlan link [ %s ] from the removed container", containerLink)
	clink, err := netlink.LinkByName(containerLink)
	if err != nil {
		log.Warnf("Error looking up link [ %s ] object: [ %v ] error: [ %s ]", clink.Attrs().Name, clink, err)
	}
	if err := netlink.LinkDel(clink); err != nil {
		log.Errorf("unable to delete the Macvlan link [ %s ] on leave: %s", clink, err)
	}
}

type endpointInfoReq struct {
	NetworkID  string
	EndpointID string
}

type endpointInfo struct {
	Value map[string]interface{}
}

func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) {
	var info endpointInfoReq
	if err := json.NewDecoder(r.Body).Decode(&info); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Endpoint info request: %+v", &info)
	objectResponse(w, &endpointInfo{Value: map[string]interface{}{}})
	log.Debugf("Endpoint info %s", info.EndpointID)
}

type joinInfo struct {
	InterfaceNames []*iface
	Gateway        string
	GatewayIPv6    string
	HostsPath      string
	ResolvConfPath string
}

type join struct {
	NetworkID  string
	EndpointID string
	SandboxKey string
	Options    map[string]interface{}
}

type staticRoute struct {
	Destination string
	RouteType   int
	NextHop     string
	InterfaceID int
}

type joinResponse struct {
	HostsPath      string
	ResolvConfPath string
	Gateway        string
	InterfaceNames []*iface
	StaticRoutes   []*staticRoute
}

func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
	var j join
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Join request: %+v", &j)

	endID := j.EndpointID
	// unique name while still on the common netns
	preMoveName := endID[:5]
	mode, err := setVlanMode(macvlanMode)
	if err != nil {
		log.Errorf("%s", err)
		return
	}
	m, err := netlink.LinkByName(macvlanEthIface)
	if err != nil {
		log.Warnf("Error looking up the parent iface [ %s ] mode: [ %s ] error: [ %s ]", macvlanEthIface, mode, err)
	}
	macvlan := &netlink.Macvlan{
		LinkAttrs: netlink.LinkAttrs{
			Name:        preMoveName,
			ParentIndex: m.Attrs().Index,
		},
		Mode: mode,
	}
	if err := netlink.LinkAdd(macvlan); err != nil {
		log.Warnf("failed to create Macvlan: [ %v ] with the error: %s", macvlan, err)
	}
	log.Infof("Created Macvlan port: [ %s ] using the mode: [ %s ]", macvlan.Name, macvlanMode)

	// SrcName gets renamed to DstPrefix on the container iface
	ifname := &iface{
		SrcName:   macvlan.Name,
		DstPrefix: containerIfacePrefix,
		ID:        0,
	}
	res := &joinResponse{
		InterfaceNames: []*iface{ifname},
		Gateway:        gatewayIP,
	}
	// Add Connected Route
	routeToDNS := &staticRoute{
		Destination: defaultSubnet,
		RouteType:   types.CONNECTED,
		NextHop:     "",
		InterfaceID: 0,
	}
	res.StaticRoutes = []*staticRoute{routeToDNS}

	objectResponse(w, res)
	log.Debugf("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
}

type leave struct {
	NetworkID  string
	EndpointID string
	Options    map[string]interface{}
}

func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
	var l leave
	if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Leave request: %+v", &l)
	emptyResponse(w)
	log.Debugf("Leave %s:%s", l.NetworkID, l.EndpointID)
}
<commit_msg>fix ip allocator mask<commit_after>package macvlan

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/ipallocator"
	"github.com/gorilla/mux"
	"github.com/samalba/dockerclient"
	"github.com/vishvananda/netlink"
)

const (
	MethodReceiver       = "NetworkDriver"
	defaultRoute         = "0.0.0.0/0"
	containerIfacePrefix = "eth"
)

type Driver interface {
	Listen(string) error
}

// Struct for binding bridge options CLI flags
type bridgeOpts struct {
	brName   string
	brSubnet net.IPNet
	brIP     net.IPNet
}

type driver struct {
	dockerer
	ipAllocator *ipallocator.IPAllocator
	version     string
	network     string
	cidr        *net.IPNet
	nameserver  string
}

func New(version string) (Driver, error) {
	docker, err := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
	if err != nil {
		return nil, fmt.Errorf("could not connect to docker: %s", err)
	}

	ipAllocator := ipallocator.New()
	d := &driver{
		dockerer: dockerer{
			client: docker,
		},
		ipAllocator: ipAllocator,
		version:     version,
	}
	return d, nil
}

func (driver *driver) Listen(socket string) error {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFound)

	router.Methods("GET").Path("/status").HandlerFunc(driver.status)
	router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(driver.handshake)

	handleMethod := func(method string, h http.HandlerFunc) {
		router.Methods("POST").Path(fmt.Sprintf("/%s.%s", MethodReceiver, method)).HandlerFunc(h)
	}
	handleMethod("CreateNetwork", driver.createNetwork)
	handleMethod("DeleteNetwork", driver.deleteNetwork)
	handleMethod("CreateEndpoint", driver.createEndpoint)
	handleMethod("DeleteEndpoint", driver.deleteEndpoint)
	handleMethod("EndpointOperInfo", driver.infoEndpoint)
	handleMethod("Join", driver.joinEndpoint)
	handleMethod("Leave", driver.leaveEndpoint)
	var (
		listener net.Listener
		err      error
	)

	listener, err = net.Listen("unix", socket)
	if err != nil {
		return err
	}

	return http.Serve(listener, router)
}

func notFound(w http.ResponseWriter, r *http.Request) {
	log.Warnf("[plugin] Not found: %+v", r)
	http.NotFound(w, r)
}

func sendError(w http.ResponseWriter, msg string, code int) {
	log.Errorf("%d %s", code, msg)
	http.Error(w, msg, code)
}

func errorResponsef(w http.ResponseWriter, fmtString string, item ...interface{}) {
	json.NewEncoder(w).Encode(map[string]string{
		"Err": fmt.Sprintf(fmtString, item...),
	})
}

func objectResponse(w http.ResponseWriter, obj interface{}) {
	if err := json.NewEncoder(w).Encode(obj); err != nil {
		sendError(w, "Could not JSON encode response", http.StatusInternalServerError)
		return
	}
}

func emptyResponse(w http.ResponseWriter) {
	json.NewEncoder(w).Encode(map[string]string{})
}

type handshakeResp struct {
	Implements []string
}

func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{"NetworkDriver"},
	})
	if err != nil {
		log.Fatalf("handshake encode: %s", err)
		sendError(w, "encode error", http.StatusInternalServerError)
		return
	}
	log.Debug("Handshake completed")
}

func (driver *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("macvlan plugin", driver.version))
}

type networkCreate struct {
	NetworkID string
	Options   map[string]interface{}
}

func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) {
	var create networkCreate
	err := json.NewDecoder(r.Body).Decode(&create)
	if err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}

	if driver.network != "" {
		errorResponsef(w, "You get just one network, and you already made %s", driver.network)
		return
	}
	driver.network = create.NetworkID

	// Parse the network address from the user supplied or default container network
	_, ipNet, err := net.ParseCIDR(defaultSubnet)
	if err != nil {
		log.Warnf("Error parsing cidr from the default subnet: %s", err)
	}
	driver.cidr = ipNet
	driver.ipAllocator.RequestIP(ipNet, nil)

	emptyResponse(w)
}

type networkDelete struct {
	NetworkID string
}

func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) {
	var delete networkDelete
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}
	log.Debugf("Delete network request: %+v", &delete)
	if delete.NetworkID != driver.network {
		log.Debugf("network not found: %+v", &delete)
		errorResponsef(w, "Network %s not found", delete.NetworkID)
		return
	}
	driver.network = ""
	emptyResponse(w)
	log.Infof("Destroy network %s", delete.NetworkID)
}

type endpointCreate struct {
	NetworkID  string
	EndpointID string
	Interfaces []*iface
	Options    map[string]interface{}
}

type iface struct {
	ID         int
	SrcName    string
	DstPrefix  string
	Address    string
	MacAddress string
}

type endpointResponse struct {
	Interfaces []*iface
}

func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) {
	var create endpointCreate
	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
		return
	}

	netID := create.NetworkID
	endID := create.EndpointID

	if netID != driver.network {
		log.Warnf("Network not found, [ %s ]", netID)
		errorResponsef(w, "No such network %s", netID)
		return
	}
	_, ipNet, err := net.ParseCIDR(defaultSubnet)
	if err != nil {
		log.Warnf("Error parsing cidr from the default subnet: %s", err)
	}
	// Request an IP address from libnetwork based on the cidr scope
	// TODO: Add a user defined static ip addr option
	allocatedIP, err := driver.ipAllocator.RequestIP(ipNet, nil)
	if err != nil || allocatedIP == nil {
		log.Errorf("Unable to obtain an IP address from libnetwork ipam: %s", err)
		errorResponsef(w, "%s", err)
		return
	}
	// generate a mac address for the pending container
	mac := makeMac(allocatedIP)
	// Have to convert container IP to a string ip/mask format
	bridgeMask := strings.Split(ipNet.String(), "/")
	containerAddress := allocatedIP.String() + "/" + bridgeMask[1]

	log.Infof("Dynamically allocated container IP is: [ %s ]", containerAddress)

	respIface := &iface{
		Address:    containerAddress,
		MacAddress: mac,
	}
	resp := &endpointResponse{
		Interfaces: []*iface{respIface},
	}
	objectResponse(w, resp)
	log.Debugf("Create endpoint %s %+v", endID, resp)
}

type endpointDelete struct {
	NetworkID  string
	EndpointID string
}

func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
	var delete endpointDelete
	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Delete endpoint request: %+v", &delete)
	emptyResponse(w)
	// null check cidr in case driver restarted and doesnt know the network to avoid panic
	if driver.cidr == nil {
		return
	}
	// ReleaseIP releases an ip back to a network
	if err := driver.ipAllocator.ReleaseIP(driver.cidr, driver.cidr.IP); err != nil {
		log.Warnf("error releasing IP: %s", err)
	}
	log.Debugf("Delete endpoint %s", delete.EndpointID)

	containerLink := delete.EndpointID[:5]

	log.Infof("Removing unused macvlan link [ %s ] from the removed container", containerLink)
	clink, err := netlink.LinkByName(containerLink)
	if err != nil {
		log.Warnf("Error looking up link [ %s ] object: [ %v ] error: [ %s ]", clink.Attrs().Name, clink, err)
	}
	if err := netlink.LinkDel(clink); err != nil {
		log.Errorf("unable to delete the Macvlan link [ %s ] on leave: %s", clink, err)
	}
}

type endpointInfoReq struct {
	NetworkID  string
	EndpointID string
}

type endpointInfo struct {
	Value map[string]interface{}
}

func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) {
	var info endpointInfoReq
	if err := json.NewDecoder(r.Body).Decode(&info); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Endpoint info request: %+v", &info)
	objectResponse(w, &endpointInfo{Value: map[string]interface{}{}})
	log.Debugf("Endpoint info %s", info.EndpointID)
}

type joinInfo struct {
	InterfaceNames []*iface
	Gateway        string
	GatewayIPv6    string
	HostsPath      string
	ResolvConfPath string
}

type join struct {
	NetworkID  string
	EndpointID string
	SandboxKey string
	Options    map[string]interface{}
}

type staticRoute struct {
	Destination string
	RouteType   int
	NextHop     string
	InterfaceID int
}

type joinResponse struct {
	HostsPath      string
	ResolvConfPath string
	Gateway        string
	InterfaceNames []*iface
	StaticRoutes   []*staticRoute
}

func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
	var j join
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Join request: %+v", &j)

	endID := j.EndpointID
	// unique name while still on the common netns
	preMoveName := endID[:5]
	mode, err := setVlanMode(macvlanMode)
	if err != nil {
		log.Errorf("%s", err)
		return
	}
	m, err := netlink.LinkByName(macvlanEthIface)
	if err != nil {
		log.Warnf("Error looking up the parent iface [ %s ] mode: [ %s ] error: [ %s ]", macvlanEthIface, mode, err)
	}
	macvlan := &netlink.Macvlan{
		LinkAttrs: netlink.LinkAttrs{
			Name:        preMoveName,
			ParentIndex: m.Attrs().Index,
		},
		Mode: mode,
	}
	if err := netlink.LinkAdd(macvlan); err != nil {
		log.Warnf("failed to create Macvlan: [ %v ] with the error: %s", macvlan, err)
	}
	log.Infof("Created Macvlan port: [ %s ] using the mode: [ %s ]", macvlan.Name, macvlanMode)

	// SrcName gets renamed to DstPrefix on the container iface
	ifname := &iface{
		SrcName:   macvlan.Name,
		DstPrefix: containerIfacePrefix,
		ID:        0,
	}
	res := &joinResponse{
		InterfaceNames: []*iface{ifname},
		Gateway:        gatewayIP,
	}
	objectResponse(w, res)
	log.Debugf("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
}

type leave struct {
	NetworkID  string
	EndpointID string
	Options    map[string]interface{}
}

func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
	var l leave
	if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
		return
	}
	log.Debugf("Leave request: %+v", &l)
	emptyResponse(w)
	log.Debugf("Leave %s:%s", l.NetworkID, l.EndpointID)
}
<|endoftext|>


<commit_before>package device

import (
	"context"
	"io"
	"time"

	"github.com/LK4D4/joincontext"
	"github.com/golang/protobuf/ptypes"
	"github.com/hashicorp/nomad/plugins/base"
	"github.com/hashicorp/nomad/plugins/device/proto"
	netctx "golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// devicePluginClient implements the client side of a remote device plugin, using
// gRPC to communicate to the remote plugin.
type devicePluginClient struct {
	// basePluginClient is embedded to give access to the base plugin methods.
	*base.BasePluginClient

	client proto.DevicePluginClient

	// doneCtx is closed when the plugin exits
	doneCtx context.Context
}

// Fingerprint is used to retrieve the set of devices and their health from the
// device plugin. An error may be immediately returned if the fingerprint call
// could not be made or as part of the streaming response. If the context is
// cancelled, the error will be propogated.
func (d *devicePluginClient) Fingerprint(ctx context.Context) (<-chan *FingerprintResponse, error) {
	// Join the passed context and the shutdown context
	ctx, _ = joincontext.Join(ctx, d.doneCtx)

	var req proto.FingerprintRequest
	stream, err := d.client.Fingerprint(ctx, &req)
	if err != nil {
		return nil, err
	}

	out := make(chan *FingerprintResponse, 1)
	go d.handleFingerprint(ctx, stream, out)
	return out, nil
}

// handleFingerprint should be launched in a goroutine and handles converting
// the gRPC stream to a channel. Exits either when context is cancelled or the
// stream has an error.
func (d *devicePluginClient) handleFingerprint(
	ctx netctx.Context,
	stream proto.DevicePlugin_FingerprintClient,
	out chan *FingerprintResponse) {

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				out <- &FingerprintResponse{
					Error: d.handleStreamErr(err, ctx),
				}
			}

			// End the stream
			close(out)
			return
		}

		// Send the response
		out <- &FingerprintResponse{
			Devices: convertProtoDeviceGroups(resp.GetDeviceGroup()),
		}
	}
}

func (d *devicePluginClient) Reserve(deviceIDs []string) (*ContainerReservation, error) {
	// Build the request
	req := &proto.ReserveRequest{
		DeviceIds: deviceIDs,
	}

	// Make the request
	resp, err := d.client.Reserve(context.Background(), req)
	if err != nil {
		return nil, err
	}

	// Convert the response
	out := convertProtoContainerReservation(resp.GetContainerRes())
	return out, nil
}

// Stats is used to retrieve device statistics from the device plugin. An error
// may be immediately returned if the stats call could not be made or as part of
// the streaming response. If the context is cancelled, the error will be
// propogated.
func (d *devicePluginClient) Stats(ctx context.Context, interval time.Duration) (<-chan *StatsResponse, error) {
	// Join the passed context and the shutdown context
	ctx, _ = joincontext.Join(ctx, d.doneCtx)

	req := proto.StatsRequest{
		CollectionInterval: ptypes.DurationProto(interval),
	}
	stream, err := d.client.Stats(ctx, &req)
	if err != nil {
		return nil, err
	}

	out := make(chan *StatsResponse, 1)
	go d.handleStats(ctx, stream, out)
	return out, nil
}

// handleStats should be launched in a goroutine and handles converting
// the gRPC stream to a channel. Exits either when context is cancelled or the
// stream has an error.
func (d *devicePluginClient) handleStats(
	ctx netctx.Context,
	stream proto.DevicePlugin_StatsClient,
	out chan *StatsResponse) {

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				out <- &StatsResponse{
					Error: d.handleStreamErr(err, ctx),
				}
			}

			// End the stream
			close(out)
			return
		}

		// Send the response
		out <- &StatsResponse{
			Groups: convertProtoDeviceGroupsStats(resp.GetGroups()),
		}
	}
}

// handleStreamErr is used to handle a non io.EOF error in a stream. It handles
// detecting if the plugin has shutdown
func (d *devicePluginClient) handleStreamErr(err error, ctx context.Context) error {
	if err == nil {
		return nil
	}

	// Determine if the error is because the plugin shutdown
	if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.Unavailable {
		// Potentially wait a little before returning an error so we can detect
		// the exit
		select {
		case <-d.doneCtx.Done():
			err = base.ErrPluginShutdown
		case <-ctx.Done():
			err = ctx.Err()

			// There is no guarantee that the select will choose the
			// doneCtx first so we have to double check
			select {
			case <-d.doneCtx.Done():
				err = base.ErrPluginShutdown
			default:
			}
		case <-time.After(3 * time.Second):
			// Its okay to wait a while since the connection isn't available and
			// on local host it is likely shutting down. It is not expected for
			// this to ever reach even close to 3 seconds.
		}

		// It is an error we don't know how to handle, so return it
		return err
	}

	// Context was cancelled
	if errStatus := status.FromContextError(ctx.Err()); errStatus.Code() == codes.Canceled {
		return context.Canceled
	}

	return err
}
<commit_msg>reserve uses donectx<commit_after>package device

import (
	"context"
	"io"
	"time"

	"github.com/LK4D4/joincontext"
	"github.com/golang/protobuf/ptypes"
	"github.com/hashicorp/nomad/plugins/base"
	"github.com/hashicorp/nomad/plugins/device/proto"
	netctx "golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// devicePluginClient implements the client side of a remote device plugin, using
// gRPC to communicate to the remote plugin.
type devicePluginClient struct {
	// basePluginClient is embedded to give access to the base plugin methods.
	*base.BasePluginClient

	client proto.DevicePluginClient

	// doneCtx is closed when the plugin exits
	doneCtx context.Context
}

// Fingerprint is used to retrieve the set of devices and their health from the
// device plugin. An error may be immediately returned if the fingerprint call
// could not be made or as part of the streaming response. If the context is
// cancelled, the error will be propogated.
func (d *devicePluginClient) Fingerprint(ctx context.Context) (<-chan *FingerprintResponse, error) {
	// Join the passed context and the shutdown context
	ctx, _ = joincontext.Join(ctx, d.doneCtx)

	var req proto.FingerprintRequest
	stream, err := d.client.Fingerprint(ctx, &req)
	if err != nil {
		return nil, err
	}

	out := make(chan *FingerprintResponse, 1)
	go d.handleFingerprint(ctx, stream, out)
	return out, nil
}

// handleFingerprint should be launched in a goroutine and handles converting
// the gRPC stream to a channel. Exits either when context is cancelled or the
// stream has an error.
func (d *devicePluginClient) handleFingerprint(
	ctx netctx.Context,
	stream proto.DevicePlugin_FingerprintClient,
	out chan *FingerprintResponse) {

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				out <- &FingerprintResponse{
					Error: d.handleStreamErr(err, ctx),
				}
			}

			// End the stream
			close(out)
			return
		}

		// Send the response
		out <- &FingerprintResponse{
			Devices: convertProtoDeviceGroups(resp.GetDeviceGroup()),
		}
	}
}

func (d *devicePluginClient) Reserve(deviceIDs []string) (*ContainerReservation, error) {
	// Build the request
	req := &proto.ReserveRequest{
		DeviceIds: deviceIDs,
	}

	// Make the request
	resp, err := d.client.Reserve(d.doneCtx, req)
	if err != nil {
		return nil, err
	}

	// Convert the response
	out := convertProtoContainerReservation(resp.GetContainerRes())
	return out, nil
}

// Stats is used to retrieve device statistics from the device plugin. An error
// may be immediately returned if the stats call could not be made or as part of
// the streaming response. If the context is cancelled, the error will be
// propogated.
func (d *devicePluginClient) Stats(ctx context.Context, interval time.Duration) (<-chan *StatsResponse, error) {
	// Join the passed context and the shutdown context
	ctx, _ = joincontext.Join(ctx, d.doneCtx)

	req := proto.StatsRequest{
		CollectionInterval: ptypes.DurationProto(interval),
	}
	stream, err := d.client.Stats(ctx, &req)
	if err != nil {
		return nil, err
	}

	out := make(chan *StatsResponse, 1)
	go d.handleStats(ctx, stream, out)
	return out, nil
}

// handleStats should be launched in a goroutine and handles converting
// the gRPC stream to a channel. Exits either when context is cancelled or the
// stream has an error.
func (d *devicePluginClient) handleStats(
	ctx netctx.Context,
	stream proto.DevicePlugin_StatsClient,
	out chan *StatsResponse) {

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				out <- &StatsResponse{
					Error: d.handleStreamErr(err, ctx),
				}
			}

			// End the stream
			close(out)
			return
		}

		// Send the response
		out <- &StatsResponse{
			Groups: convertProtoDeviceGroupsStats(resp.GetGroups()),
		}
	}
}

// handleStreamErr is used to handle a non io.EOF error in a stream. It handles
// detecting if the plugin has shutdown
func (d *devicePluginClient) handleStreamErr(err error, ctx context.Context) error {
	if err == nil {
		return nil
	}

	// Determine if the error is because the plugin shutdown
	if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.Unavailable {
		// Potentially wait a little before returning an error so we can detect
		// the exit
		select {
		case <-d.doneCtx.Done():
			err = base.ErrPluginShutdown
		case <-ctx.Done():
			err = ctx.Err()

			// There is no guarantee that the select will choose the
			// doneCtx first so we have to double check
			select {
			case <-d.doneCtx.Done():
				err = base.ErrPluginShutdown
			default:
			}
		case <-time.After(3 * time.Second):
			// Its okay to wait a while since the connection isn't available and
			// on local host it is likely shutting down. It is not expected for
			// this to ever reach even close to 3 seconds.
		}

		// It is an error we don't know how to handle, so return it
		return err
	}

	// Context was cancelled
	if errStatus := status.FromContextError(ctx.Err()); errStatus.Code() == codes.Canceled {
		return context.Canceled
	}

	return err
}
<|endoftext|>


<commit_before>// Package mailgun provides methods for interacting with the Mailgun API.
// For further information please see the Mailgun documentation at
// http://documentation.mailgun.com/
//
// Author: Michael Banzon
package mailgun

import (
	"fmt"
	"time"
)

const (
	apiBase                 = "https://api.mailgun.net/v2"
	messagesEndpoint        = "messages"
	addressValidateEndpoint = "address/validate"
	addressParseEndpoint    = "address/parse"
	bouncesEndpoint         = "bounces"
	statsEndpoint           = "stats"
	domainsEndpoint         = "domains"
	deleteTagEndpoint       = "tags"
	campaignsEndpoint       = "campaigns"
	basicAuthUser           = "api"
)

type Mailgun interface {
	Domain() string
	ApiKey() string
	PublicApiKey() string
	Send(m *Message) (string, string, error)
	ValidateEmail(email string) (EmailVerification, error)
	ParseAddresses(addresses ...string) ([]string, []string, error)
	GetBounces(limit, skip int) (int, []Bounce, error)
	GetSingleBounce(address string) (Bounce, error)
	AddBounce(address, code, error string) error
	DeleteBounce(address string) error
	GetStats(limit int, skip int, startDate time.Time, event ...string) (int, []Stat, error)
	DeleteTag(tag string) error
	GetDomains(limit, skip int) (int, []Domain, error)
	GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error)
	CreateDomain(name string, smtpPassword string, spamAction bool, wildcard bool) error
	DeleteDomain(name string) error
	GetCampaigns() (int, []Campaign, error)
	CreateCampaign(name, id string) error
	UpdateCampaign(oldId, name, newId string) error
	DeleteCampaign(id string) error
	GetComplaints(limit, skip int) (int, []Complaint, error)
	GetSingleComplaint(address string) (Complaint, error)
}

type mailgunImpl struct {
	domain       string
	apiKey       string
	publicApiKey string
}

func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
	m := mailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey}
	return &m
}

func (m *mailgunImpl) Domain() string {
	return m.domain
}

// Returns the API key.
func (m *mailgunImpl) ApiKey() string {
	return m.apiKey
}

// Returns the public API key.
func (m *mailgunImpl) PublicApiKey() string {
	return m.publicApiKey
}

// Generates the URL for the API using the domain and endpoint.
func generateApiUrl(m Mailgun, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint)
}

func generatePublicApiUrl(endpoint string) string {
	return fmt.Sprintf("%s/%s", apiBase, endpoint)
}

func parseMailgunTime(ts string) (t time.Time, err error) {
	t, err = time.Parse("Mon, 2 Jan 2006 15:04:05 MST", ts)
	return
}
<commit_msg>Comment.<commit_after>// Package mailgun provides methods for interacting with the Mailgun API.
// For further information please see the Mailgun documentation at
// http://documentation.mailgun.com/
//
// Author: Michael Banzon
package mailgun

import (
	"fmt"
	"time"
)

const (
	apiBase                 = "https://api.mailgun.net/v2"
	messagesEndpoint        = "messages"
	addressValidateEndpoint = "address/validate"
	addressParseEndpoint    = "address/parse"
	bouncesEndpoint         = "bounces"
	statsEndpoint           = "stats"
	domainsEndpoint         = "domains"
	deleteTagEndpoint       = "tags"
	campaignsEndpoint       = "campaigns"
	basicAuthUser           = "api"
)

type Mailgun interface {
	Domain() string
	ApiKey() string
	PublicApiKey() string
	Send(m *Message) (string, string, error)
	ValidateEmail(email string) (EmailVerification, error)
	ParseAddresses(addresses ...string) ([]string, []string, error)
	GetBounces(limit, skip int) (int, []Bounce, error)
	GetSingleBounce(address string) (Bounce, error)
	AddBounce(address, code, error string) error
	DeleteBounce(address string) error
	GetStats(limit int, skip int, startDate time.Time, event ...string) (int, []Stat, error)
	DeleteTag(tag string) error
	GetDomains(limit, skip int) (int, []Domain, error)
	GetSingleDomain(domain string) (Domain, []DNSRecord, []DNSRecord, error)
	CreateDomain(name string, smtpPassword string, spamAction bool, wildcard bool) error
	DeleteDomain(name string) error
	GetCampaigns() (int, []Campaign, error)
	CreateCampaign(name, id string) error
	UpdateCampaign(oldId, name, newId string) error
	DeleteCampaign(id string) error
	GetComplaints(limit, skip int) (int, []Complaint, error)
	GetSingleComplaint(address string) (Complaint, error)
}

type mailgunImpl struct {
	domain       string
	apiKey       string
	publicApiKey string
}

// Creates a new Mailgun instance.
func NewMailgun(domain, apiKey, publicApiKey string) Mailgun {
	m := mailgunImpl{domain: domain, apiKey: apiKey, publicApiKey: publicApiKey}
	return &m
}

func (m *mailgunImpl) Domain() string {
	return m.domain
}

// Returns the API key.
func (m *mailgunImpl) ApiKey() string {
	return m.apiKey
}

// Returns the public API key.
func (m *mailgunImpl) PublicApiKey() string {
	return m.publicApiKey
}

// Generates the URL for the API using the domain and endpoint.
func generateApiUrl(m Mailgun, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint)
}

func generatePublicApiUrl(endpoint string) string {
	return fmt.Sprintf("%s/%s", apiBase, endpoint)
}

func parseMailgunTime(ts string) (t time.Time, err error) {
	t, err = time.Parse("Mon, 2 Jan 2006 15:04:05 MST", ts)
	return
}
<|endoftext|>


<commit_before>package test161

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

// This file defines test161's test manager.  The manager is responsible for
// keeping track of the number of running tests and limiting that number to
// a configurable capacity.
//
// There is a global test manager (testManager) that listens for new job
// requests on its SubmitChan.  In the current implementation, this can
// only be accessed from within the package by one of the TestRunners.

// A test161Job consists of the test to run, the directory to find the
// binaries, and a channel to communicate the results on.
type test161Job struct {
	Test     *Test
	Env      *TestEnvironment
	DoneChan chan *Test161JobResult
}

// A Test161JobResult consists of the completed test and any error that
// occurred while running the test.
type Test161JobResult struct {
	Test *Test
	Err  error
}

type manager struct {
	SubmitChan chan *test161Job
	Capacity   uint

	// protected by L
	statsCond *sync.Cond
	isRunning bool

	stats ManagerStats
}

type ManagerStats struct {
	// protected by manager.L
	Running     uint  `json:"running"`
	HighRunning uint  `json:"high_running"`
	Queued      uint  `json:"queued"`
	HighQueued  uint  `json:"high_queued"`
	Finished    uint  `json:"finished"`
	MaxWait     int64 `json:"max_wait_ms"`
	AvgWait     int64 `json:"avg_wait_ms"`
	total       int64 // denominator for avg
}

// Combined submission and tests statistics since the service started
type Test161Stats struct {
	Status          string       `json:"status"`
	SubmissionStats ManagerStats `json:"submission_stats"`
	TestStats       ManagerStats `json:"test_stats"`
}

const DEFAULT_MGR_CAPACITY uint = 0

func newManager() *manager {
	m := &manager{
		SubmitChan: nil,
		Capacity:   DEFAULT_MGR_CAPACITY,
		statsCond:  sync.NewCond(&sync.Mutex{}),
		isRunning:  false,
	}
	return m
}

// The global test manager.  NewEnvironment has a reference to
// this manager, which should be used by clients of this library.
// Unit tests use their own manager/environment for runner/assertion
// isolation.
var testManager *manager = newManager()

// Clear state and start listening for job requests
func (m *manager) start() {
	m.statsCond.L.Lock()
	defer m.statsCond.L.Unlock()

	if m.isRunning {
		return
	}

	m.stats = ManagerStats{}
	m.SubmitChan = make(chan *test161Job)
	m.isRunning = true

	// Listener goroutine
	go func() {
		// We simply spawn a worker that blocks until it can run.
		for job := range m.SubmitChan {
			go m.runOrQueueJob(job)
		}
	}()
}

// Queue the job if we're at capacity, and run it once we're under.
func (m *manager) runOrQueueJob(job *test161Job) {

	m.statsCond.L.Lock()
	queued := false
	start := time.Now()

	for m.Capacity > 0 && m.stats.Running >= m.Capacity {
		if !queued {
			queued = true

			// Update queued stats
			m.stats.Queued += 1
			if m.stats.Queued > m.stats.HighQueued {
				m.stats.HighQueued = m.stats.Queued
			}
		}

		// Wait for a finished test to signal us
		m.statsCond.Wait()
	}

	// We've got the green light... (and the stats lock)
	if queued {
		m.stats.Queued -= 1
		queued = false

		// Max and average waits
		curWait := int64(time.Now().Sub(start).Nanoseconds() / 1e6)
		if m.stats.MaxWait < curWait {
			m.stats.MaxWait = curWait
		}
		m.stats.AvgWait = (m.stats.total*m.stats.AvgWait + curWait) / (m.stats.total + 1)
		m.stats.total += 1

		m.statsCond.Broadcast()
	}

	m.stats.Running += 1
	if m.stats.Running > m.stats.HighRunning {
		m.stats.HighRunning = m.stats.Running
	}

	m.statsCond.L.Unlock()

	// Go!
	err := job.Test.Run(job.Env)

	// And... we're done.

	// Update stats
	m.statsCond.L.Lock()
	m.stats.Running -= 1
	m.stats.Finished += 1

	// Broadcast because different entities are blocking for different reasons
	m.statsCond.Broadcast()
	m.statsCond.L.Unlock()

	// Pass the completed test back to the caller
	// (Blocking call, we need to make sure the caller gets the result.)
	job.DoneChan <- &Test161JobResult{job.Test, err}
}

// Shut it down
func (m *manager) stop() {
	m.statsCond.L.Lock()
	defer m.statsCond.L.Unlock()

	if !m.isRunning {
		return
	}

	m.isRunning = false
	close(m.SubmitChan)
}

// Exported shared test manger functions

// Start the shared test manager
func StartManager() {
	testManager.start()
}

// Stop the shared test manager
func StopManager() {
	testManager.stop()
}

func SetManagerCapacity(capacity uint) {
	testManager.Capacity = capacity
}

func ManagerCapacity() uint {
	return testManager.Capacity
}

func (m *manager) Stats() *ManagerStats {
	// Lock so we at least get a consistent view of the stats
	testManager.statsCond.L.Lock()
	defer testManager.statsCond.L.Unlock()

	// copy
	var res ManagerStats = testManager.stats
	return &res
}

// Return a copy of the current shared test manager stats
func GetManagerStats() *ManagerStats {
	return testManager.Stats()
}

////////  Submission Manager
//
// SubmissionManager handles running multiple submissions, which is useful for the server.
// The (Test)Manager already handles rate limiting tests, but we also need to be careful
// about rate limiting submissions.  In particular, we don't need to build all new
// if we have queued tests.  This just wastes cycles and I/O that the tests could use.
// Plus, the student will see the status go from building to running, but the boot test
// will just get queued.

const (
	SM_ACCEPTING = iota
	SM_NOT_ACCEPTING
)

type SubmissionManager struct {
	env     *TestEnvironment
	runlock *sync.Mutex // Block everything from running
	l       *sync.Mutex // Synchronize other state
	status  int
	stats   ManagerStats
}

func NewSubmissionManager(env *TestEnvironment) *SubmissionManager {
	mgr := &SubmissionManager{
		env:     env,
		runlock: &sync.Mutex{},
		l:       &sync.Mutex{},
		status:  SM_ACCEPTING,
	}
	return mgr
}

func (sm *SubmissionManager) CombinedStats() *Test161Stats {
	stats := &Test161Stats{
		SubmissionStats: *sm.Stats(),
		TestStats:       *sm.env.manager.Stats(),
	}
	if sm.Status() == SM_ACCEPTING {
		stats.Status = "accepting submissions"
	} else {
		stats.Status = "not accepting submissions"
	}
	return stats
}

func (sm *SubmissionManager) Stats() *ManagerStats {
	sm.l.Lock()
	defer sm.l.Unlock()
	copy := sm.stats
	return &copy
}

func (sm *SubmissionManager) Run(s *Submission) error {

	// The test manager we're associated with
	mgr := sm.env.manager

	sm.l.Lock()

	// Check to see if we've been paused or stopped. The server checks too, but there's delay.
	if sm.status != SM_ACCEPTING {
		sm.l.Unlock()
		s.Status = SUBMISSION_ABORTED
		err := errors.New("The submission server is not accepting new submissions at this time")
		s.Errors = append(s.Errors, fmt.Sprintf("%v", err))
		sm.env.notifyAndLogErr("Submissions Closed", s, MSG_PERSIST_COMPLETE, 0)
		return err
	}

	// Update Queued
	sm.stats.Queued += 1
	start := time.Now()
	if sm.stats.HighQueued < sm.stats.Queued {
		sm.stats.HighQueued = sm.stats.Queued
	}
	sm.l.Unlock()

	///////////
	// Queued here
	sm.runlock.Lock()

	// Still queued, but on deck. Wait on the manager's condition variable so we
	// get notifications when stats change.
	mgr.statsCond.L.Lock()
	for mgr.stats.Queued > 0 {
		mgr.statsCond.Wait()
	}
	mgr.statsCond.L.Unlock()
	// It's OK if we get a rush of builds here. Eventually we'll get a queue again, and
	// the test manager handles this.
	///////////

	// Update run stats
	sm.l.Lock()
	sm.stats.Queued -= 1
	sm.stats.Running += 1
	if sm.stats.HighRunning < sm.stats.Running {
		sm.stats.HighRunning = sm.stats.Running
	}

	// Max and average waits
	curWait := int64(time.Now().Sub(start).Nanoseconds() / 1e6)
	if sm.stats.MaxWait < curWait {
		sm.stats.MaxWait = curWait
	}
	sm.stats.AvgWait = (sm.stats.total*sm.stats.AvgWait + curWait) / (sm.stats.total + 1)
	sm.stats.total += 1

	sm.l.Unlock()

	// Run the submission
	sm.runlock.Unlock()
	err := s.Run()

	// Update stats
	sm.l.Lock()
	sm.stats.Running -= 1
	sm.stats.Finished += 1
	sm.l.Unlock()

	return err
}

func (sm *SubmissionManager) Pause() {
	sm.l.Lock()
	defer sm.l.Unlock()
	sm.status = SM_NOT_ACCEPTING
}

func (sm *SubmissionManager) Resume() {
	sm.l.Lock()
	defer sm.l.Unlock()
	sm.status = SM_ACCEPTING
}

func (sm *SubmissionManager) Status() int {
	sm.l.Lock()
	defer sm.l.Unlock()
	return sm.status
}
<commit_msg>Added start time to manager stats<commit_after>package test161

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

// This file defines test161's test manager.  The manager is responsible for
// keeping track of the number of running tests and limiting that number to
// a configurable capacity.
//
// There is a global test manager (testManager) that listens for new job
// requests on its SubmitChan.  In the current implementation, this can
// only be accessed from within the package by one of the TestRunners.

// A test161Job consists of the test to run, the directory to find the
// binaries, and a channel to communicate the results on.
type test161Job struct {
	Test     *Test
	Env      *TestEnvironment
	DoneChan chan *Test161JobResult
}

// A Test161JobResult consists of the completed test and any error that
// occurred while running the test.
type Test161JobResult struct {
	Test *Test
	Err  error
}

type manager struct {
	SubmitChan chan *test161Job
	Capacity   uint

	// protected by L
	statsCond *sync.Cond
	isRunning bool

	stats ManagerStats
}

type ManagerStats struct {
	// protected by manager.L
	Running     uint  `json:"running"`
	HighRunning uint  `json:"high_running"`
	Queued      uint  `json:"queued"`
	HighQueued  uint  `json:"high_queued"`
	Finished    uint  `json:"finished"`
	MaxWait     int64 `json:"max_wait_ms"`
	AvgWait     int64 `json:"avg_wait_ms"`
	StartTime   time.Time
	total       int64 // denominator for avg
}

// Combined submission and tests statistics since the service started
type Test161Stats struct {
	Status          string       `json:"status"`
	SubmissionStats ManagerStats `json:"submission_stats"`
	TestStats       ManagerStats `json:"test_stats"`
}

const DEFAULT_MGR_CAPACITY uint = 0

func newManager() *manager {
	m := &manager{
		SubmitChan: nil,
		Capacity:   DEFAULT_MGR_CAPACITY,
		statsCond:  sync.NewCond(&sync.Mutex{}),
		isRunning:  false,
	}
	return m
}

// The global test manager.  NewEnvironment has a reference to
// this manager, which should be used by clients of this library.
// Unit tests use their own manager/environment for runner/assertion
// isolation.
var testManager *manager = newManager()

// Clear state and start listening for job requests
func (m *manager) start() {
	m.statsCond.L.Lock()
	defer m.statsCond.L.Unlock()

	if m.isRunning {
		return
	}

	m.stats = ManagerStats{
		StartTime: time.Now(),
	}
	m.SubmitChan = make(chan *test161Job)
	m.isRunning = true

	// Listener goroutine
	go func() {
		// We simply spawn a worker that blocks until it can run.
		for job := range m.SubmitChan {
			go m.runOrQueueJob(job)
		}
	}()
}

// Queue the job if we're at capacity, and run it once we're under.
func (m *manager) runOrQueueJob(job *test161Job) {

	m.statsCond.L.Lock()
	queued := false
	start := time.Now()

	for m.Capacity > 0 && m.stats.Running >= m.Capacity {
		if !queued {
			queued = true

			// Update queued stats
			m.stats.Queued += 1
			if m.stats.Queued > m.stats.HighQueued {
				m.stats.HighQueued = m.stats.Queued
			}
		}

		// Wait for a finished test to signal us
		m.statsCond.Wait()
	}

	// We've got the green light... (and the stats lock)
	if queued {
		m.stats.Queued -= 1
		queued = false

		// Max and average waits
		curWait := int64(time.Now().Sub(start).Nanoseconds() / 1e6)
		if m.stats.MaxWait < curWait {
			m.stats.MaxWait = curWait
		}
		m.stats.AvgWait = (m.stats.total*m.stats.AvgWait + curWait) / (m.stats.total + 1)
		m.stats.total += 1

		m.statsCond.Broadcast()
	}

	m.stats.Running += 1
	if m.stats.Running > m.stats.HighRunning {
		m.stats.HighRunning = m.stats.Running
	}

	m.statsCond.L.Unlock()

	// Go!
	err := job.Test.Run(job.Env)

	// And... we're done.

	// Update stats
	m.statsCond.L.Lock()
	m.stats.Running -= 1
	m.stats.Finished += 1

	// Broadcast because different entities are blocking for different reasons
	m.statsCond.Broadcast()
	m.statsCond.L.Unlock()

	// Pass the completed test back to the caller
	// (Blocking call, we need to make sure the caller gets the result.)
	job.DoneChan <- &Test161JobResult{job.Test, err}
}

// Shut it down
func (m *manager) stop() {
	m.statsCond.L.Lock()
	defer m.statsCond.L.Unlock()

	if !m.isRunning {
		return
	}

	m.isRunning = false
	close(m.SubmitChan)
}

// Exported shared test manger functions

// Start the shared test manager
func StartManager() {
	testManager.start()
}

// Stop the shared test manager
func StopManager() {
	testManager.stop()
}

func SetManagerCapacity(capacity uint) {
	testManager.Capacity = capacity
}

func ManagerCapacity() uint {
	return testManager.Capacity
}

func (m *manager) Stats() *ManagerStats {
	// Lock so we at least get a consistent view of the stats
	testManager.statsCond.L.Lock()
	defer testManager.statsCond.L.Unlock()

	// copy
	var res ManagerStats = testManager.stats
	return &res
}

// Return a copy of the current shared test manager stats
func GetManagerStats() *ManagerStats {
	return testManager.Stats()
}

////////  Submission Manager
//
// SubmissionManager handles running multiple submissions, which is useful for the server.
// The (Test)Manager already handles rate limiting tests, but we also need to be careful
// about rate limiting submissions.  In particular, we don't need to build all new
// if we have queued tests.  This just wastes cycles and I/O that the tests could use.
// Plus, the student will see the status go from building to running, but the boot test
// will just get queued.

const (
	SM_ACCEPTING = iota
	SM_NOT_ACCEPTING
)

type SubmissionManager struct {
	env     *TestEnvironment
	runlock *sync.Mutex // Block everything from running
	l       *sync.Mutex // Synchronize other state
	status  int
	stats   ManagerStats
}

func NewSubmissionManager(env *TestEnvironment) *SubmissionManager {
	mgr := &SubmissionManager{
		env:     env,
		runlock: &sync.Mutex{},
		l:       &sync.Mutex{},
		status:  SM_ACCEPTING,
		stats: ManagerStats{
			StartTime: time.Now(),
		},
	}
	return mgr
}

func (sm *SubmissionManager) CombinedStats() *Test161Stats {
	stats := &Test161Stats{
		SubmissionStats: *sm.Stats(),
		TestStats:       *sm.env.manager.Stats(),
	}
	if sm.Status() == SM_ACCEPTING {
		stats.Status = "accepting submissions"
	} else {
		stats.Status = "not accepting submissions"
	}
	return stats
}

func (sm *SubmissionManager) Stats() *ManagerStats {
	sm.l.Lock()
	defer sm.l.Unlock()
	copy := sm.stats
	return &copy
}

func (sm *SubmissionManager) Run(s *Submission) error {

	// The test manager we're associated with
	mgr := sm.env.manager

	sm.l.Lock()

	// Check to see if we've been paused or stopped. The server checks too, but there's delay.
	if sm.status != SM_ACCEPTING {
		sm.l.Unlock()
		s.Status = SUBMISSION_ABORTED
		err := errors.New("The submission server is not accepting new submissions at this time")
		s.Errors = append(s.Errors, fmt.Sprintf("%v", err))
		sm.env.notifyAndLogErr("Submissions Closed", s, MSG_PERSIST_COMPLETE, 0)
		return err
	}

	// Update Queued
	sm.stats.Queued += 1
	start := time.Now()
	if sm.stats.HighQueued < sm.stats.Queued {
		sm.stats.HighQueued = sm.stats.Queued
	}
	sm.l.Unlock()

	///////////
	// Queued here
	sm.runlock.Lock()

	// Still queued, but on deck. Wait on the manager's condition variable so we
	// get notifications when stats change.
	mgr.statsCond.L.Lock()
	for mgr.stats.Queued > 0 {
		mgr.statsCond.Wait()
	}
	mgr.statsCond.L.Unlock()
	// It's OK if we get a rush of builds here. Eventually we'll get a queue again, and
	// the test manager handles this.
	///////////

	// Update run stats
	sm.l.Lock()
	sm.stats.Queued -= 1
	sm.stats.Running += 1
	if sm.stats.HighRunning < sm.stats.Running {
		sm.stats.HighRunning = sm.stats.Running
	}

	// Max and average waits
	curWait := int64(time.Now().Sub(start).Nanoseconds() / 1e6)
	if sm.stats.MaxWait < curWait {
		sm.stats.MaxWait = curWait
	}
	sm.stats.AvgWait = (sm.stats.total*sm.stats.AvgWait + curWait) / (sm.stats.total + 1)
	sm.stats.total += 1

	sm.l.Unlock()

	// Run the submission
	sm.runlock.Unlock()
	err := s.Run()

	// Update stats
	sm.l.Lock()
	sm.stats.Running -= 1
	sm.stats.Finished += 1
	sm.l.Unlock()

	return err
}

func (sm *SubmissionManager) Pause() {
	sm.l.Lock()
	defer sm.l.Unlock()
	sm.status = SM_NOT_ACCEPTING
}

func (sm *SubmissionManager) Resume() {
	sm.l.Lock()
	defer sm.l.Unlock()
	sm.status = SM_ACCEPTING
}

func (sm *SubmissionManager) Status() int {
	sm.l.Lock()
	defer sm.l.Unlock()
	return sm.status
}
<|endoftext|>