<commit_before>/*
Tarantula is a mild framework wrapping Go's net/http with some simple utilities for different kinds of HTTP output.

	package main
	import "net/http"
	import "github.com/swdunlop/tarantula"
	func main() {
		svc := tarantula.NewService(cfg.Bind)
		svc.Bind("/", presentContent)
		svc.Bind("/.wait", waitForRefresh)
		err := svc.Run()
		if err != nil {
			println("!!", err.Error())
		}
	}

	func presentContent(q *http.Request) (interface{}, error) {
		return "Hello, JSON!", nil
	}

Tarantula is intended primarily for JSON web services; as such, bound functions are expected to return values that
can be converted to JSON, and when they do, they will be provided to the browser.  Errors will override returned data
and will be provded instead if they are present.

Tarantula is also somewhat clever about watching for SIGUSR1; it regards this as an indication that the service should
enter a controlled shutdown, finishing any pending requests before permitting the Run method to return.  The Stop method
produces similar behavior, closing the HTTP listener then permitting existing connections to wind down.

Tarantula provides a simple interface, tarantula.ResponderToHttp, that indicates a value that knows how to write
itself to a http.ResponseWriter.  A number of convenient wrappers can be found in Tarantula that implement this interface,
including HttpError, ForwardToURL and WithTemplate.

Refer to https://github.com/swdunlop/livefire-go for a more involved example for Tarantula.
*/
package tarantula

import (
	"encoding/json"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"sync"
)

// NewService creates a new tarantula.Service that will (eventually) listen to the supplied TCP address.
func NewService(addr string) *Service {
	svc := new(Service)
	svc.addr = addr
	svc.mux = http.NewServeMux()
	svc.server.Handler = svc
	return svc
}

// Service collects trivia about a Tarantula HTTP service and maintains state.
type Service struct {
	addr     string
	pending  sync.WaitGroup
	mux      *http.ServeMux
	started  bool
	server   http.Server
	listener net.Listener
}

// ServeHTTP is an implementation of the http.ServeHTTP interface.
func (svc *Service) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	svc.pending.Add(1)
	defer svc.pending.Done()
	//TODO: recoverError here.
	svc.mux.ServeHTTP(rw, req)
}

func (svc *Service) waitPending() {
	if svc.started {
		svc.pending.Wait()
	}
}

// Initiates an eventual stop of the service by closing its listener.
func (svc *Service) Stop() {
	svc.listener.Close()
}

// Performs all configuration and preparation for the service, but does not
// accept requests, see Run() for that.
func (svc *Service) Start() error {
	var err error
	if svc.started {
		return nil
	}
	svc.listener, err = net.Listen("tcp", svc.addr)
	if err != nil {
		return err
	}
	svc.started = true
	go svc.handleSignals()
	return nil
}

// Serves requests in a loop until the service is Stopped.  Note that the service will continue to service existing
// connections.
func (svc *Service) Run() error {
	svc.Start()
	err := svc.server.Serve(svc.listener)
	svc.waitPending()
	return err
}

// recoverError is used by Run() and invokeService to contain panics and errors.
func recoverError(perr *error) {
	r := recover()
	if r == nil {
		return
	}
	if err, ok := r.(error); ok {
		*perr = err
		return
	}
	panic(r)
}

// Func's are invoked when a http.Request is received and produce either a response or an error.
type Func func(req *http.Request) (interface{}, error)

// Binds a function that responds with either JSON bricks or ResponderToHttp's
func (svc *Service) Bind(pattern string, fn Func) {
	svc.mux.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
		val, err := invokeService(fn, req)
		err = RespondToHttp(w, val, err)
		if err != nil {
			log.Println(req.RemoteAddr, "response error", err.Error())
		}
	})
}

// RespondToHttp permits ResponderToHttp implementations to reuse how Tarantula responds to a HTTP request.
func RespondToHttp(w http.ResponseWriter, val interface{}, err error) error {
	if err != nil {
		return writeHttpError(w, err)
	}
	return writeHttpValue(w, val)
}

// Induces a HTTP level redirect to dest.
func (svc *Service) BindRedirect(pattern string, dest string) {
	svc.mux.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Location", dest)
		w.WriteHeader(http.StatusMovedPermanently)
	})
}

// Used by BindService to contain and encapsulate panics and errors.
func invokeService(fn Func, req *http.Request) (v interface{}, err error) {
	defer recoverError(&err)
	v, err = fn(req)
	return
}

// Used by BindService to inform the browser about an error.
func writeHttpError(w http.ResponseWriter, err error) error {
	msg := err.Error()

	switch e := err.(type) {
	case ResponderToHttp:
		return e.RespondToHttp(w)
	}

	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(500)
	var e struct {
		Msg string `json:"msg"`
	}
	e.Msg = msg
	return json.NewEncoder(w).Encode(&e)
}

// Used by BindService to provide value to the browser.
func writeHttpValue(w http.ResponseWriter, v interface{}) error {
	switch data := v.(type) {
	case ResponderToHttp:
		return data.RespondToHttp(w)
	}

	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(200)
	return json.NewEncoder(w).Encode(v)
}

// Implementations of ResponderToHttp may be used by bound services to specify non-JSON responses.
type ResponderToHttp interface {
	RespondToHttp(w http.ResponseWriter) error
}

// CopyToHttp, when used as a ResponderToHttp, will copy an io.ReaderCloser with specified MIME Content Type to the client.
type CopyToHttp struct {
	Mime string
	File File
}

// RespondToHttp is an implementation of ResponderToHttp.
func (cth CopyToHttp) RespondToHttp(w http.ResponseWriter) error {
	defer cth.File.Close()
	w.Header().Set("Content-type", cth.Mime)
	w.WriteHeader(200)
	_, err := io.Copy(w, cth.File)
	return err
}

// The minimum interface required to express a data stream that can be supplied via CopyToHttp.
type File interface {
	io.Reader
	io.Closer
}

// A ForwardToURL response instructs the client to follow a 302 redirect to URL
type ForwardToURL struct {
	URL string
}

// RespondToHttp is an implementation of ResponderToHttp.
func (ftu ForwardToURL) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set("Location", ftu.URL)
	w.WriteHeader(302)
	_, err := w.Write([]byte{})
	return err
}

// Error is an implementation of error for better logging.
func (ftu ForwardToURL) Error() string {
	return "forward to " + ftu.URL
}

// A HttpError response indicates an error that has a specific HTTP error code associated with it.
type HttpError struct {
	Code int
	Msg  string
}

// RespondToHttp is an implementation of ResponderToHttp.
func (hem HttpError) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(hem.Code)
	// we cannot safely write to the body, in case this is a HEAD request TODO
	//return json.NewEncoder(w).Encode(hem.Msg)
	return nil
}

// Error is an implementation of error that limits itself to the actual message.
func (hem HttpError) Error() string {
	return hem.Msg
}

// WithHeader is a ResponderToHttp that wraps another ResponderWithHttp, augmenting it with a HTTP header.
type WithHeader struct {
	Key, Val string
	Next     interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wit WithHeader) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set(wit.Key, wit.Val)
	return RespondToHttp(w, wit.Next, nil)
}

// WithTemplate is a ResponderToHttp that uses Tmpl to convert Data to a text/html response.
type WithTemplate struct {
	Tmpl *template.Template
	Data interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wt WithTemplate) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set("Content-type", "text/html")
	return wt.Tmpl.Execute(w, wt.Data)
}

// WithCookie is a ResponderToHttp that sets a cookie before forwarding to Next.
type WithCookie struct {
	Cookie *http.Cookie
	Next   interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wc WithCookie) RespondToHttp(w http.ResponseWriter) error {
	http.SetCookie(w, wc.Cookie)
	return RespondToHttp(w, wc.Next, nil)
}
<commit_msg>added content-length and connection keep-alive where lengths are known<commit_after>/*
Tarantula is a mild framework wrapping Go's net/http with some simple utilities for different kinds of HTTP output.

	package main
	import "net/http"
	import "github.com/swdunlop/tarantula"
	func main() {
		svc := tarantula.NewService(cfg.Bind)
		svc.Bind("/", presentContent)
		svc.Bind("/.wait", waitForRefresh)
		err := svc.Run()
		if err != nil {
			println("!!", err.Error())
		}
	}

	func presentContent(q *http.Request) (interface{}, error) {
		return "Hello, JSON!", nil
	}

Tarantula is intended primarily for JSON web services; as such, bound functions are expected to return values that
can be converted to JSON, and when they do, they will be provided to the browser.  Errors will override returned data
and will be provded instead if they are present.

Tarantula is also somewhat clever about watching for SIGUSR1; it regards this as an indication that the service should
enter a controlled shutdown, finishing any pending requests before permitting the Run method to return.  The Stop method
produces similar behavior, closing the HTTP listener then permitting existing connections to wind down.

Tarantula provides a simple interface, tarantula.ResponderToHttp, that indicates a value that knows how to write
itself to a http.ResponseWriter.  A number of convenient wrappers can be found in Tarantula that implement this interface,
including HttpError, ForwardToURL and WithTemplate.

Refer to https://github.com/swdunlop/livefire-go for a more involved example for Tarantula.
*/
package tarantula

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"sync"
)

// NewService creates a new tarantula.Service that will (eventually) listen to the supplied TCP address.
func NewService(addr string) *Service {
	svc := new(Service)
	svc.addr = addr
	svc.mux = http.NewServeMux()
	svc.server.Handler = svc
	return svc
}

// Service collects trivia about a Tarantula HTTP service and maintains state.
type Service struct {
	addr     string
	pending  sync.WaitGroup
	mux      *http.ServeMux
	started  bool
	server   http.Server
	listener net.Listener
}

// ServeHTTP is an implementation of the http.ServeHTTP interface.
func (svc *Service) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	svc.pending.Add(1)
	defer svc.pending.Done()
	//TODO: recoverError here.
	svc.mux.ServeHTTP(rw, req)
}

func (svc *Service) waitPending() {
	if svc.started {
		svc.pending.Wait()
	}
}

// Initiates an eventual stop of the service by closing its listener.
func (svc *Service) Stop() {
	svc.listener.Close()
}

// Performs all configuration and preparation for the service, but does not
// accept requests, see Run() for that.
func (svc *Service) Start() error {
	var err error
	if svc.started {
		return nil
	}
	svc.listener, err = net.Listen("tcp", svc.addr)
	if err != nil {
		return err
	}
	svc.started = true
	go svc.handleSignals()
	return nil
}

// Serves requests in a loop until the service is Stopped.  Note that the service will continue to service existing
// connections.
func (svc *Service) Run() error {
	svc.Start()
	err := svc.server.Serve(svc.listener)
	svc.waitPending()
	return err
}

// recoverError is used by Run() and invokeService to contain panics and errors.
func recoverError(perr *error) {
	r := recover()
	if r == nil {
		return
	}
	if err, ok := r.(error); ok {
		*perr = err
		return
	}
	panic(r)
}

// Func's are invoked when a http.Request is received and produce either a response or an error.
type Func func(req *http.Request) (interface{}, error)

// Binds a function that responds with either JSON bricks or ResponderToHttp's
func (svc *Service) Bind(pattern string, fn Func) {
	svc.mux.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
		val, err := invokeService(fn, req)
		err = RespondToHttp(w, val, err)
		if err != nil {
			log.Println(req.RemoteAddr, "response error", err.Error())
		}
	})
}

// RespondToHttp permits ResponderToHttp implementations to reuse how Tarantula responds to a HTTP request.
func RespondToHttp(w http.ResponseWriter, val interface{}, err error) error {
	if err != nil {
		return writeHttpError(w, err)
	}
	return writeHttpValue(w, val)
}

// Induces a HTTP level redirect to dest.
func (svc *Service) BindRedirect(pattern string, dest string) {
	svc.mux.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Location", dest)
		w.WriteHeader(http.StatusMovedPermanently)
	})
}

// Used by BindService to contain and encapsulate panics and errors.
func invokeService(fn Func, req *http.Request) (v interface{}, err error) {
	defer recoverError(&err)
	v, err = fn(req)
	return
}

// Used by BindService to inform the browser about an error.
func writeHttpError(w http.ResponseWriter, err error) error {
	msg := err.Error()

	switch e := err.(type) {
	case ResponderToHttp:
		return e.RespondToHttp(w)
	}

	var e struct {
		Msg string `json:"msg"`
	}
	e.Msg = msg
	return writeHttpJson(w, 500, &e)
}

// Used by BindService to provide value to the browser.
func writeHttpValue(w http.ResponseWriter, v interface{}) error {
	switch data := v.(type) {
	case ResponderToHttp:
		return data.RespondToHttp(w)
	}
	return writeHttpJson(w, 200, v)
}

func writeHttpJson(w http.ResponseWriter, c int, v interface{}) error {
	js, err := json.Marshal(v)
	if err != nil {
		return err
	}
	h := w.Header()
	h.Set("Content-type", "application/json")
	h.Set("Content-length", fmt.Sprint(len(js)))
	h.Set("Connection", "keep-alive")
	w.WriteHeader(c)
	_, err = w.Write(js)
	return err
}

// Implementations of ResponderToHttp may be used by bound services to specify non-JSON responses.
type ResponderToHttp interface {
	RespondToHttp(w http.ResponseWriter) error
}

// CopyToHttp, when used as a ResponderToHttp, will copy an io.ReaderCloser with specified MIME Content Type to the client; if Length is omitted this will NOT keep-alive the connection, because the length will not be known.  Length will NOT limit how many bytes are copied.
type CopyToHttp struct {
	Mime   string
	File   File
	Length int64
}

// RespondToHttp is an implementation of ResponderToHttp.
func (cth CopyToHttp) RespondToHttp(w http.ResponseWriter) error {
	defer cth.File.Close()
	if cth.Length != 0 {
		return cth.copyN(w)
	}

	w.Header().Set("Content-type", cth.Mime)
	w.WriteHeader(200)
	_, err := io.Copy(w, cth.File)
	return err
}

func (cth *CopyToHttp) copyN(w http.ResponseWriter) error {
	h := w.Header()
	h.Set("Content-type", cth.Mime)
	h.Set("Content-length", fmt.Sprint(cth.Length))
	h.Set("Connection", "keep-alive")
	w.WriteHeader(200)
	_, err := io.CopyN(w, cth.File, cth.Length)
	return err
}

// The minimum interface required to express a data stream that can be supplied via CopyToHttp.
type File interface {
	io.Reader
	io.Closer
}

// A ForwardToURL response instructs the client to follow a 302 redirect to URL
type ForwardToURL struct {
	URL string
}

// RespondToHttp is an implementation of ResponderToHttp.
func (ftu ForwardToURL) RespondToHttp(w http.ResponseWriter) error {
	h := w.Header()
	h.Set("Location", ftu.URL)
	h.Set("Content-length", "0")
	h.Set("Connection", "keep-alive")
	w.WriteHeader(302)
	_, err := w.Write([]byte{})
	return err
}

// Error is an implementation of error for better logging.
func (ftu ForwardToURL) Error() string {
	return "forward to " + ftu.URL
}

// A HttpError response indicates an error that has a specific HTTP error code associated with it.
type HttpError struct {
	Code int
	Msg  string
}

// RespondToHttp is an implementation of ResponderToHttp.
func (hem HttpError) RespondToHttp(w http.ResponseWriter) error {
	h := w.Header()
	h.Set("Content-length", "0")
	h.Set("Connection", "keep-alive")
	w.WriteHeader(hem.Code)
	// we cannot safely write to the body, in case this is a HEAD request TODO
	//return json.NewEncoder(w).Encode(hem.Msg)
	_, err := w.Write([]byte{})
	return err
}

// Error is an implementation of error that limits itself to the actual message.
func (hem HttpError) Error() string {
	return hem.Msg
}

// WithHeader is a ResponderToHttp that wraps another ResponderWithHttp, augmenting it with a HTTP header.
type WithHeader struct {
	Key, Val string
	Next     interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wit WithHeader) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set(wit.Key, wit.Val)
	return RespondToHttp(w, wit.Next, nil)
}

// WithTemplate is a ResponderToHttp that uses Tmpl to convert Data to a text/html response.
type WithTemplate struct {
	Tmpl *template.Template
	Data interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wt WithTemplate) RespondToHttp(w http.ResponseWriter) error {
	w.Header().Set("Content-type", "text/html")
	return wt.Tmpl.Execute(w, wt.Data)
}

// WithCookie is a ResponderToHttp that sets a cookie before forwarding to Next.
type WithCookie struct {
	Cookie *http.Cookie
	Next   interface{}
}

// RespondToHttp is an implementation of ResponderToHttp.
func (wc WithCookie) RespondToHttp(w http.ResponseWriter) error {
	http.SetCookie(w, wc.Cookie)
	return RespondToHttp(w, wc.Next, nil)
}
<|endoftext|>


<commit_before>package mcmap

import (
	"errors"
	"github.com/kch42/gonbt/nbt"
	"time"
)

func calcBlockOffset(x, y, z int) int {
	if (x < 0) || (y < 0) || (z < 0) || (x >= 16) || (y >= 256) || (z >= 16) {
		return -1
	}

	return x | (z << 4) | (y << 8)
}

func offsetToPos(off int) (x, y, z int) {
	x = off & 0xf
	z = (off >> 4) & 0xf
	y = (off >> 8) & 0xff
	return
}

// BlockToChunk calculates the chunk (cx, cz) and the block position in this chunk(rbx, rbz) of a block position given global coordinates.
func BlockToChunk(bx, bz int) (cx, cz, rbx, rbz int) {
	cx = bx << 4
	cz = bz << 4
	rbx = ((cx % 16) + 16) % 16
	rbz = ((cz % 16) + 16) % 16
	return
}

// ChunkToBlock calculates the global position of a block, given the chunk position (cx, cz) and the plock position in that chunk (rbx, rbz).
func ChunkToBlock(cx, cz, rbx, rbz int) (bx, bz int) {
	bx = cx*16 + rbx
	bz = cz*16 + rbz
	return
}

// Chunk represents a 16*16*256 Chunk of the region.
type Chunk struct {
	Entities []nbt.TagCompound

	x, z int32

	lastUpdate    int64
	populated     bool
	inhabitedTime int64
	ts            time.Time

	heightMap []int32 // Ordered ZX

	modified bool
	blocks   []Block // Ordered YZX
	biomes   []Biome // Ordered XZ
}

// MarkModified needs to be called, if some data of the chunk was modified.
func (c *Chunk) MarkModified() { c.modified = true }

// Coords returns the Chunk's coordinates.
func (c *Chunk) Coords() (X, Z int32) { return c.x, c.z }

// Block gives you a reference to the Block located at x, y, z. If you modify the block data, you need to call the MarkModified() function of the chunk.
//
// x and z must be in [0, 15], y in [0, 255]. Otherwise a nil pointer is returned.
func (c *Chunk) Block(x, y, z int) *Block {
	off := calcBlockOffset(x, y, z)
	if off < 0 {
		return nil
	}

	return &(c.blocks[off])
}

// Height returns the height at x, z.
//
// x and z must be in [0, 15]. Height will panic, if this is violated!
func (c *Chunk) Height(x, z int) int {
	if (x < 0) || (x > 15) || (z < 0) || (z > 15) {
		panic(errors.New("x or z parameter was out of range"))
	}

	return int(c.heightMap[z*16+x])
}

// TODO: func (c *Chunk) RecalcHeightMap()
<commit_msg>Added functions to get/set biome data.<commit_after>package mcmap

import (
	"errors"
	"github.com/kch42/gonbt/nbt"
	"time"
)

func calcBlockOffset(x, y, z int) int {
	if (x < 0) || (y < 0) || (z < 0) || (x >= 16) || (y >= 256) || (z >= 16) {
		return -1
	}

	return x | (z << 4) | (y << 8)
}

func offsetToPos(off int) (x, y, z int) {
	x = off & 0xf
	z = (off >> 4) & 0xf
	y = (off >> 8) & 0xff
	return
}

// BlockToChunk calculates the chunk (cx, cz) and the block position in this chunk(rbx, rbz) of a block position given global coordinates.
func BlockToChunk(bx, bz int) (cx, cz, rbx, rbz int) {
	cx = bx << 4
	cz = bz << 4
	rbx = ((cx % 16) + 16) % 16
	rbz = ((cz % 16) + 16) % 16
	return
}

// ChunkToBlock calculates the global position of a block, given the chunk position (cx, cz) and the plock position in that chunk (rbx, rbz).
func ChunkToBlock(cx, cz, rbx, rbz int) (bx, bz int) {
	bx = cx*16 + rbx
	bz = cz*16 + rbz
	return
}

// Chunk represents a 16*16*256 Chunk of the region.
type Chunk struct {
	Entities []nbt.TagCompound

	x, z int32

	lastUpdate    int64
	populated     bool
	inhabitedTime int64
	ts            time.Time

	heightMap []int32 // Ordered ZX

	modified bool
	blocks   []Block // Ordered YZX
	biomes   []Biome // Ordered XZ
}

// MarkModified needs to be called, if some data of the chunk was modified.
func (c *Chunk) MarkModified() { c.modified = true }

// Coords returns the Chunk's coordinates.
func (c *Chunk) Coords() (X, Z int32) { return c.x, c.z }

// Block gives you a reference to the Block located at x, y, z. If you modify the block data, you need to call the MarkModified() function of the chunk.
//
// x and z must be in [0, 15], y in [0, 255]. Otherwise a nil pointer is returned.
func (c *Chunk) Block(x, y, z int) *Block {
	off := calcBlockOffset(x, y, z)
	if off < 0 {
		return nil
	}

	return &(c.blocks[off])
}

// Height returns the height at x, z.
//
// x and z must be in [0, 15]. Height will panic, if this is violated!
func (c *Chunk) Height(x, z int) int {
	if (x < 0) || (x > 15) || (z < 0) || (z > 15) {
		panic(errors.New("x or z parameter was out of range"))
	}

	return int(c.heightMap[z*16+x])
}

// Biome gets the Biome at x,z.
func (c *Chunk) Biome(x, z int) Biome { return c.biomes[x*16+z] }

// SetBiome sets the biome at x,z.
func (c *Chunk) SetBiome(x, z int, bio Biome) { c.biomes[x*16+z] = bio }

// TODO: func (c *Chunk) RecalcHeightMap()
<|endoftext|>


<commit_before>package supervisor

import (
	"fmt"
	"sync"
	"testing"
	"time"
)

func TestString(t *testing.T) {
	t.Parallel()

	const expected = "test"
	var supervisor Supervisor
	supervisor.Name = expected

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestStringDefaultName(t *testing.T) {
	t.Parallel()

	const expected = "supervisor"
	var supervisor Supervisor
	supervisor.prepare()

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestCascaded(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestCascaded root"
	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	var childSupervisor Supervisor
	childSupervisor.Name = "TestCascaded child"
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := waitservice{id: 4}
	childSupervisor.Add(&svc4)
	supervisor.Add(&childSupervisor)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)

	if count := getServiceCount(&supervisor); count != 3 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc2.count != 1, svc3.count != 1, svc4.count != 1:
		t.Errorf("services should have been executed only once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	}
}

func TestLog(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestLog",
		Backoff: 500 * time.Millisecond,
	}
	svc1 := panicservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)
}

func TestCascadedWithProblems(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestCascadedWithProblems root",
		Backoff: 1 * time.Second,
		Log: func(msg interface{}) {
			t.Log("supervisor log (cascaded with problems):", msg)
		},
	}
	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := panicservice{id: 2}
	supervisor.Add(&svc2)
	svcHolding := &holdingservice{id: 98}
	svcHolding.Add(1)
	supervisor.Add(svcHolding)

	childSupervisor := Supervisor{
		Name:    "TestCascadedWithProblems child",
		Backoff: 250 * time.Millisecond,
		Log: func(msg interface{}) {
			t.Log("supervisor log (cascaded with problems - child):", msg)
		},
	}
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := failingservice{id: 4}
	childSupervisor.Add(&svc4)
	svcHolding2 := &holdingservice{id: 99}
	svcHolding2.Add(1)
	childSupervisor.Add(svcHolding2)

	supervisor.Add(&childSupervisor)

	ctx, _ := contextWithTimeout(5 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svcHolding.Wait()
	svcHolding2.Wait()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 4 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc3.count != 1:
		t.Errorf("services should have been executed only once. svc1.count:%d svc3.count: %d",
			svc1.count, svc3.count)
	case svc2.count <= 1, svc4.count <= 1:
		t.Errorf("services should have been executed at least once. svc2.count:%d svc4.count: %d",
			svc2.count, svc4.count)
	}
}

func TestPanic(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestPanic supervisor",
		Backoff: 500 * time.Millisecond,
		Log: func(msg interface{}) {
			t.Log("supervisor log (panic):", msg)
		},
	}
	svc1 := panicservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestPanicBug(t *testing.T) {
	t.Parallel()

	supervisor := Group{
		Supervisor: &Supervisor{
			Name:    "TestPanicBug supervisor",
			Backoff: 2 * time.Second,
			Log: func(msg interface{}) {
				t.Log("supervisor log (panic bug):", msg)
			},
		},
	}
	svc1 := quickpanicservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	ctx, _ := contextWithTimeout(6 * time.Second)
	supervisor.Serve(ctx)

	if supervisor.backoff[svc1.String()].lastfail.IsZero() {
		t.Errorf("%s service should have backoff activated at least once", &svc1)
	}
	if !supervisor.backoff[svc2.String()].lastfail.IsZero() {
		t.Errorf("%s service should no thave backoff activated", &svc2)
	}
}

func TestFailing(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestFailing supervisor",
		Backoff: 1 * time.Second,
		Log: func(msg interface{}) {
			t.Log("supervisor log (failing):", msg)
		},
	}
	svc1 := failingservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(3 * time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestAddServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestAddServiceAfterServe supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)

	ctx, cancel := contextWithTimeout(2 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svc1.Wait()

	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)
	svc2.Wait()

	cancel()
	<-ctx.Done()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 2 {
		t.Errorf("unexpected service count: %v", count)
	}
}

func TestRemoveServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestRemoveServiceAfterServe supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	lbefore := getServiceCount(&supervisor)
	supervisor.Remove("unknown service")
	lafter := getServiceCount(&supervisor)

	if lbefore != lafter {
		t.Error("the removal of an unknown service shouldn't happen")
	}

	svc1.Wait()
	svc2.Wait()

	supervisor.Remove(svc1.String())
	lremoved := getServiceCount(&supervisor)
	if lbefore == lremoved {
		t.Error("the removal of a service should have affected the supervisor:", lbefore, lremoved)
	}

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestRemoveServiceAfterServeBug(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestRemoveServiceAfterServeBug supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)

	ctx, _ := contextWithTimeout(2 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svc1.Wait()
	supervisor.Remove(svc1.String())
	wg.Wait()

	if svc1.count > 1 {
		t.Errorf("the removal of a service should have terminated it. It was started %v times", svc1.count)
	}
}

func TestServices(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestServices supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()
	svc1.Wait()
	svc2.Wait()

	svcs := supervisor.Services()
	for _, svcname := range []string{svc1.String(), svc2.String()} {
		if _, ok := svcs[svcname]; !ok {
			t.Errorf("expected service not found: %s", svcname)
		}
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestManualCancelation(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name: "TestManualCancelation supervisor",
		Log: func(msg interface{}) {
			t.Log("supervisor log (restartable):", msg)
		},
	}
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := restartableservice{2, make(chan struct{})}
	supervisor.Add(&svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	svc1.Wait()

	// Testing restart
	<-svc2.restarted
	svcs := supervisor.Cancelations()
	svcancel := svcs[svc2.String()]
	svcancel()
	<-svc2.restarted

	cancel()
	<-ctx.Done()
	wg.Done()

	// should arrive here with no panic
}

func TestServiceList(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestServiceList supervisor"
	svc1 := holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(&svc1)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()
	svc1.Wait()

	svcs := supervisor.Services()
	if svc, ok := svcs[svc1.String()]; !ok || &svc1 != svc.(*holdingservice) {
		t.Errorf("could not find service when listing them. %s missing", svc1.String())
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestGroup(t *testing.T) {
	t.Parallel()

	supervisor := Group{
		Supervisor: &Supervisor{
			Name: "TestGroup supervisor",
			Log: func(msg interface{}) {
				t.Log("group log:", msg)
			},
		},
	}
	svc1 := holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(&svc1)

	svc2 := holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(&svc2)

	ctx, cancel := contextWithTimeout(3 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		// should arrive here with no panic
		if !(svc1.count == svc2.count && svc1.count == 2) {
			t.Errorf("affected service of a group should affect all. svc1.count: %d svc2.count: %d (both should be 2)", svc1.count, svc2.count)
		}
		wg.Done()
	}()
	svc1.Wait()
	svc2.Wait()

	svc1.Add(1)
	svc2.Add(1)

	cs := supervisor.Cancelations()
	cs[svc1.String()]()

	svc1.Wait()
	svc2.Wait()

	cancel()
	wg.Wait()
}

func TestInvalidGroup(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("defer called, but not because of panic")
		}
	}()
	var group Group
	ctx, _ := contextWithTimeout(10 * time.Second)
	group.Serve(ctx)
	t.Error("this group is invalid and should have had panic()'d")
}

func getServiceCount(s *Supervisor) int {
	s.mu.Lock()
	l := len(s.services)
	s.mu.Unlock()
	return l
}
<commit_msg>Recover test coverage to 100% - TestRemovePanicService<commit_after>package supervisor

import (
	"fmt"
	"sync"
	"testing"
	"time"
)

func TestString(t *testing.T) {
	t.Parallel()

	const expected = "test"
	var supervisor Supervisor
	supervisor.Name = expected

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestStringDefaultName(t *testing.T) {
	t.Parallel()

	const expected = "supervisor"
	var supervisor Supervisor
	supervisor.prepare()

	if got := fmt.Sprintf("%s", &supervisor); got != expected {
		t.Errorf("error getting supervisor name: %s", got)
	}
}

func TestCascaded(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestCascaded root"
	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	var childSupervisor Supervisor
	childSupervisor.Name = "TestCascaded child"
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := waitservice{id: 4}
	childSupervisor.Add(&svc4)
	supervisor.Add(&childSupervisor)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)

	if count := getServiceCount(&supervisor); count != 3 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc2.count != 1, svc3.count != 1, svc4.count != 1:
		t.Errorf("services should have been executed only once. %d %d %d %d",
			svc1.count, svc2.count, svc3.count, svc4.count)
	}
}

func TestLog(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestLog",
		Backoff: 500 * time.Millisecond,
	}
	svc1 := panicservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)
}

func TestCascadedWithProblems(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestCascadedWithProblems root",
		Backoff: 1 * time.Second,
		Log: func(msg interface{}) {
			t.Log("supervisor log (cascaded with problems):", msg)
		},
	}
	svc1 := waitservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := panicservice{id: 2}
	supervisor.Add(&svc2)
	svcHolding := &holdingservice{id: 98}
	svcHolding.Add(1)
	supervisor.Add(svcHolding)

	childSupervisor := Supervisor{
		Name:    "TestCascadedWithProblems child",
		Backoff: 250 * time.Millisecond,
		Log: func(msg interface{}) {
			t.Log("supervisor log (cascaded with problems - child):", msg)
		},
	}
	svc3 := waitservice{id: 3}
	childSupervisor.Add(&svc3)
	svc4 := failingservice{id: 4}
	childSupervisor.Add(&svc4)
	svcHolding2 := &holdingservice{id: 99}
	svcHolding2.Add(1)
	childSupervisor.Add(svcHolding2)

	supervisor.Add(&childSupervisor)

	ctx, _ := contextWithTimeout(5 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svcHolding.Wait()
	svcHolding2.Wait()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 4 {
		t.Errorf("unexpected service count: %v", count)
	}

	switch {
	case svc1.count != 1, svc3.count != 1:
		t.Errorf("services should have been executed only once. svc1.count:%d svc3.count: %d",
			svc1.count, svc3.count)
	case svc2.count <= 1, svc4.count <= 1:
		t.Errorf("services should have been executed at least once. svc2.count:%d svc4.count: %d",
			svc2.count, svc4.count)
	}
}

func TestPanic(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestPanic supervisor",
		Backoff: 500 * time.Millisecond,
		Log: func(msg interface{}) {
			t.Log("supervisor log (panic):", msg)
		},
	}
	svc1 := panicservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(1 * time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestPanicBug(t *testing.T) {
	t.Parallel()

	supervisor := Group{
		Supervisor: &Supervisor{
			Name:    "TestPanicBug supervisor",
			Backoff: 2 * time.Second,
			Log: func(msg interface{}) {
				t.Log("supervisor log (panic bug):", msg)
			},
		},
	}
	svc1 := quickpanicservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)

	ctx, _ := contextWithTimeout(6 * time.Second)
	supervisor.Serve(ctx)

	if supervisor.backoff[svc1.String()].lastfail.IsZero() {
		t.Errorf("%s service should have backoff activated at least once", &svc1)
	}
	if !supervisor.backoff[svc2.String()].lastfail.IsZero() {
		t.Errorf("%s service should no thave backoff activated", &svc2)
	}
}

func TestRemovePanicService(t *testing.T) {
	t.Parallel()

	supervisor := Group{
		Supervisor: &Supervisor{
			Name:    "TestRemovePanicService supervisor",
			Backoff: 2 * time.Second,
			Log: func(msg interface{}) {
				t.Log("supervisor log (panic bug):", msg)
			},
		},
	}
	svc1 := quickpanicservice{id: 1}
	supervisor.Add(&svc1)
	svc2 := waitservice{id: 2}
	supervisor.Add(&svc2)
	svc3 := holdingservice{id: 3}
	svc3.Add(1)
	supervisor.Add(&svc3)

	ctx, _ := contextWithTimeout(6 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svc3.Wait()
	supervisor.Remove(svc1.String())

	wg.Wait()
}

func TestFailing(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name:    "TestFailing supervisor",
		Backoff: 1 * time.Second,
		Log: func(msg interface{}) {
			t.Log("supervisor log (failing):", msg)
		},
	}
	svc1 := failingservice{id: 1}
	supervisor.Add(&svc1)

	ctx, _ := contextWithTimeout(3 * time.Second)
	supervisor.Serve(ctx)

	// should arrive here with no panic
	if svc1.count == 1 {
		t.Error("the failed service should have been started at least once.")
	}
}

func TestAddServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestAddServiceAfterServe supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)

	ctx, cancel := contextWithTimeout(2 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svc1.Wait()

	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)
	svc2.Wait()

	cancel()
	<-ctx.Done()
	wg.Wait()

	if count := getServiceCount(&supervisor); count != 2 {
		t.Errorf("unexpected service count: %v", count)
	}
}

func TestRemoveServiceAfterServe(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestRemoveServiceAfterServe supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	lbefore := getServiceCount(&supervisor)
	supervisor.Remove("unknown service")
	lafter := getServiceCount(&supervisor)

	if lbefore != lafter {
		t.Error("the removal of an unknown service shouldn't happen")
	}

	svc1.Wait()
	svc2.Wait()

	supervisor.Remove(svc1.String())
	lremoved := getServiceCount(&supervisor)
	if lbefore == lremoved {
		t.Error("the removal of a service should have affected the supervisor:", lbefore, lremoved)
	}

	cancel()
	<-ctx.Done()
	wg.Wait()
}

func TestRemoveServiceAfterServeBug(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestRemoveServiceAfterServeBug supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)

	ctx, _ := contextWithTimeout(2 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		wg.Done()
	}()

	svc1.Wait()
	supervisor.Remove(svc1.String())
	wg.Wait()

	if svc1.count > 1 {
		t.Errorf("the removal of a service should have terminated it. It was started %v times", svc1.count)
	}
}

func TestServices(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestServices supervisor"
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := &holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()
	svc1.Wait()
	svc2.Wait()

	svcs := supervisor.Services()
	for _, svcname := range []string{svc1.String(), svc2.String()} {
		if _, ok := svcs[svcname]; !ok {
			t.Errorf("expected service not found: %s", svcname)
		}
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestManualCancelation(t *testing.T) {
	t.Parallel()

	supervisor := Supervisor{
		Name: "TestManualCancelation supervisor",
		Log: func(msg interface{}) {
			t.Log("supervisor log (restartable):", msg)
		},
	}
	svc1 := &holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(svc1)
	svc2 := restartableservice{2, make(chan struct{})}
	supervisor.Add(&svc2)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()

	svc1.Wait()

	// Testing restart
	<-svc2.restarted
	svcs := supervisor.Cancelations()
	svcancel := svcs[svc2.String()]
	svcancel()
	<-svc2.restarted

	cancel()
	<-ctx.Done()
	wg.Done()

	// should arrive here with no panic
}

func TestServiceList(t *testing.T) {
	t.Parallel()

	var supervisor Supervisor
	supervisor.Name = "TestServiceList supervisor"
	svc1 := holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(&svc1)

	ctx, cancel := contextWithTimeout(10 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
	}()
	svc1.Wait()

	svcs := supervisor.Services()
	if svc, ok := svcs[svc1.String()]; !ok || &svc1 != svc.(*holdingservice) {
		t.Errorf("could not find service when listing them. %s missing", svc1.String())
	}

	cancel()
	<-ctx.Done()
	wg.Done()
}

func TestGroup(t *testing.T) {
	t.Parallel()

	supervisor := Group{
		Supervisor: &Supervisor{
			Name: "TestGroup supervisor",
			Log: func(msg interface{}) {
				t.Log("group log:", msg)
			},
		},
	}
	svc1 := holdingservice{id: 1}
	svc1.Add(1)
	supervisor.Add(&svc1)

	svc2 := holdingservice{id: 2}
	svc2.Add(1)
	supervisor.Add(&svc2)

	ctx, cancel := contextWithTimeout(3 * time.Second)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		supervisor.Serve(ctx)
		// should arrive here with no panic
		if !(svc1.count == svc2.count && svc1.count == 2) {
			t.Errorf("affected service of a group should affect all. svc1.count: %d svc2.count: %d (both should be 2)", svc1.count, svc2.count)
		}
		wg.Done()
	}()
	svc1.Wait()
	svc2.Wait()

	svc1.Add(1)
	svc2.Add(1)

	cs := supervisor.Cancelations()
	cs[svc1.String()]()

	svc1.Wait()
	svc2.Wait()

	cancel()
	wg.Wait()
}

func TestInvalidGroup(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("defer called, but not because of panic")
		}
	}()
	var group Group
	ctx, _ := contextWithTimeout(10 * time.Second)
	group.Serve(ctx)
	t.Error("this group is invalid and should have had panic()'d")
}

func getServiceCount(s *Supervisor) int {
	s.mu.Lock()
	l := len(s.services)
	s.mu.Unlock()
	return l
}
<|endoftext|>


<commit_before>package mesos

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/CiscoCloud/mantl-api/utils/http"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

type Mesos struct {
	Principal  string
	Secret     string
	SecretPath string
	httpClient *http.HttpClient
}

type Framework struct {
	Name             string  `json:"name"`
	ID               string  `json:"id"`
	PID              string  `json:"pid"`
	Active           bool    `json:"active"`
	Hostname         string  `json:"hostname"`
	User             string  `json:"user"`
	RegisteredTime   float64 `json:"registered_time"`
	ReregisteredTime float64 `json:"reregistered_time"`
	Tasks            []*Task `json:"tasks"`
}

type State struct {
	CompletedFrameworks    []*Framework `json:"completed_frameworks"`
	Frameworks             []*Framework `json:"frameworks"`
	UnregisteredFrameworks []string     `json:"unregistered_frameworks"`
	Flags                  Flags        `json:"flags"`
}

type Flags struct {
	Authenticate       string `json:"authenticate"`
	AuthenticateSlaves string `json:"authenticate_slaves"`
}

type Task struct {
	FrameworkID string `json:"framework_id"`
	ID          string `json:"id"`
	Name        string `json:"name"`
	SlaveID     string `json:"slave_id"`
	State       string `json:"state"`
}

func NewMesos(url string, principal string, secretPath string, noVerifySsl bool) (*Mesos, error) {
	secret := readSecret(secretPath)
	httpClient, err := http.NewHttpClient(url, principal, secret, noVerifySsl)

	if err != nil {
		return nil, err
	}

	return &Mesos{
		Principal:  principal,
		Secret:     secret,
		SecretPath: secretPath,
		httpClient: httpClient,
	}, nil
}

func (m Mesos) Frameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.Frameworks, nil
}

func (m Mesos) CompletedFrameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.CompletedFrameworks, nil
}

func (m Mesos) Shutdown(frameworkId string) error {
	log.Debugf("Shutting down framework: %s", frameworkId)
	data := fmt.Sprintf("frameworkId=%s", frameworkId)
	httpReq, err := m.httpClient.Post("/master/teardown", []byte(data))
	if err != nil {
		return err
	}
	if httpReq.Response.StatusCode == 200 {
		log.Debugf(httpReq.ResponseText)
		return nil
	} else {
		responseText := httpReq.ResponseText
		return errors.New(fmt.Sprintf("Could not shutdown framework %s: %d %s", frameworkId, httpReq.Response.StatusCode, responseText))
	}
}

func (m Mesos) ShutdownFrameworkByName(name string) error {
	log.Debugf("Looking for %s framework", name)

	// find mesos framework
	fw, err := m.FindFramework(name)
	if err != nil {
		return err
	}

	if fw == nil {
		log.Debugf("Framework %s not active", name)
		return nil
	}

	// shutdown mesos framework
	return m.Shutdown(fw.ID)
}

func (m Mesos) FindFrameworks(name string) ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	matching := make(map[string]*Framework)
	for _, fw := range state.Frameworks {
		if fw.Name == name && fw.Active {
			matching[fw.ID] = fw
		}
	}

	var uniqueFws []*Framework
	for _, fw := range matching {
		uniqueFws = append(uniqueFws, fw)
	}

	return uniqueFws, nil
}

func (m Mesos) FindFramework(name string) (*Framework, error) {
	fws, err := m.FindFrameworks(name)
	if err != nil {
		return nil, err
	}

	fwCount := len(fws)
	if fwCount == 0 {
		return nil, nil
	} else if fwCount > 1 {
		return nil, errors.New(fmt.Sprintf("There are %d %s frameworks.", fwCount, name))
	}

	return fws[0], nil
}

func (m Mesos) RequiresAuthentication() (bool, error) {
	state, err := m.state()
	if err != nil {
		return false, err
	}

	b, err := strconv.ParseBool(state.Flags.Authenticate)
	if err != nil {
		return false, err
	}

	return b, nil
}

func (m Mesos) state() (*State, error) {
	httpReq, err := m.httpClient.Get("/master/state.json")
	if err != nil {
		return nil, err
	}

	body := httpReq.ResponseBody
	if err != nil {
		return nil, err
	}

	state := &State{}
	err = json.Unmarshal(body, state)
	return state, err
}

func readSecret(path string) string {
	secret := ""
	if len(path) > 0 {
		if _, err := os.Stat(path); err == nil {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatalf("Could not read secret from %s: %v", path, err)
			} else {
				secret = strings.TrimSpace(string(data))
			}
		} else {
			log.Fatalf("Secret file %s does not exist", path)
		}
	}
	return secret
}
<commit_msg>don't fail when credential file is missing<commit_after>package mesos

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/CiscoCloud/mantl-api/utils/http"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

type Mesos struct {
	Principal  string
	Secret     string
	SecretPath string
	httpClient *http.HttpClient
}

type Framework struct {
	Name             string  `json:"name"`
	ID               string  `json:"id"`
	PID              string  `json:"pid"`
	Active           bool    `json:"active"`
	Hostname         string  `json:"hostname"`
	User             string  `json:"user"`
	RegisteredTime   float64 `json:"registered_time"`
	ReregisteredTime float64 `json:"reregistered_time"`
	Tasks            []*Task `json:"tasks"`
}

type State struct {
	CompletedFrameworks    []*Framework `json:"completed_frameworks"`
	Frameworks             []*Framework `json:"frameworks"`
	UnregisteredFrameworks []string     `json:"unregistered_frameworks"`
	Flags                  Flags        `json:"flags"`
}

type Flags struct {
	Authenticate       string `json:"authenticate"`
	AuthenticateSlaves string `json:"authenticate_slaves"`
}

type Task struct {
	FrameworkID string `json:"framework_id"`
	ID          string `json:"id"`
	Name        string `json:"name"`
	SlaveID     string `json:"slave_id"`
	State       string `json:"state"`
}

func NewMesos(url string, principal string, secretPath string, noVerifySsl bool) (*Mesos, error) {
	secret := readSecret(secretPath)
	httpClient, err := http.NewHttpClient(url, principal, secret, noVerifySsl)

	if err != nil {
		return nil, err
	}

	return &Mesos{
		Principal:  principal,
		Secret:     secret,
		SecretPath: secretPath,
		httpClient: httpClient,
	}, nil
}

func (m Mesos) Frameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.Frameworks, nil
}

func (m Mesos) CompletedFrameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.CompletedFrameworks, nil
}

func (m Mesos) Shutdown(frameworkId string) error {
	log.Debugf("Shutting down framework: %s", frameworkId)
	data := fmt.Sprintf("frameworkId=%s", frameworkId)
	httpReq, err := m.httpClient.Post("/master/teardown", []byte(data))
	if err != nil {
		return err
	}
	if httpReq.Response.StatusCode == 200 {
		log.Debugf(httpReq.ResponseText)
		return nil
	} else {
		responseText := httpReq.ResponseText
		return errors.New(fmt.Sprintf("Could not shutdown framework %s: %d %s", frameworkId, httpReq.Response.StatusCode, responseText))
	}
}

func (m Mesos) ShutdownFrameworkByName(name string) error {
	log.Debugf("Looking for %s framework", name)

	// find mesos framework
	fw, err := m.FindFramework(name)
	if err != nil {
		return err
	}

	if fw == nil {
		log.Debugf("Framework %s not active", name)
		return nil
	}

	// shutdown mesos framework
	return m.Shutdown(fw.ID)
}

func (m Mesos) FindFrameworks(name string) ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	matching := make(map[string]*Framework)
	for _, fw := range state.Frameworks {
		if fw.Name == name && fw.Active {
			matching[fw.ID] = fw
		}
	}

	var uniqueFws []*Framework
	for _, fw := range matching {
		uniqueFws = append(uniqueFws, fw)
	}

	return uniqueFws, nil
}

func (m Mesos) FindFramework(name string) (*Framework, error) {
	fws, err := m.FindFrameworks(name)
	if err != nil {
		return nil, err
	}

	fwCount := len(fws)
	if fwCount == 0 {
		return nil, nil
	} else if fwCount > 1 {
		return nil, errors.New(fmt.Sprintf("There are %d %s frameworks.", fwCount, name))
	}

	return fws[0], nil
}

func (m Mesos) RequiresAuthentication() (bool, error) {
	state, err := m.state()
	if err != nil {
		return false, err
	}

	b, err := strconv.ParseBool(state.Flags.Authenticate)
	if err != nil {
		return false, err
	}

	return b, nil
}

func (m Mesos) state() (*State, error) {
	httpReq, err := m.httpClient.Get("/master/state.json")
	if err != nil {
		return nil, err
	}

	body := httpReq.ResponseBody
	if err != nil {
		return nil, err
	}

	state := &State{}
	err = json.Unmarshal(body, state)
	return state, err
}

func readSecret(path string) string {
	secret := ""
	if len(path) > 0 {
		if _, err := os.Stat(path); err == nil {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatalf("Could not read secret from %s: %v", path, err)
			} else {
				secret = strings.TrimSpace(string(data))
			}
		} else {
			log.Warnf("Secret file %s does not exist", path)
		}
	}
	return secret
}
<|endoftext|>


<commit_before>package miner

import (
	"errors"
	"github.com/lisgie/bazo_miner/protocol"
	"github.com/lisgie/bazo_miner/storage"
	"golang.org/x/crypto/sha3"
	"fmt"
)

func isRootKey(hash [32]byte) bool {
	_, exists := storage.RootKeys[hash]
	return exists
}

func accStateChange(txSlice []*protocol.AccTx) error {

	for _, tx := range txSlice {
		switch tx.Header {
		case 1:
			//First bit set, given account will be a new root account
			newAcc := protocol.Account{Address: tx.PubKey}
			storage.RootKeys[sha3.Sum256(tx.PubKey[:])] = &newAcc
			continue
		case 2:
			//Second bit set, delete account from root account
			delete(storage.RootKeys, sha3.Sum256(tx.PubKey[:]))
			continue
		}

		//Create a regular account
		addressHash := sha3.Sum256(tx.PubKey[:])
		acc := storage.GetAccountFromHash(addressHash)
		if acc != nil {
			//Shouldn't happen, because this should have been prevented when adding an accTx to the block
			return errors.New("CRITICAL: Address already exists in the state")
		}
		newAcc := protocol.Account{Address: tx.PubKey}
		storage.State[addressHash] = &newAcc
	}
	return nil
}

func fundsStateChange(txSlice []*protocol.FundsTx) error {

	for index, tx := range txSlice {

		var err error
		//Check if we have to issue new coins (in case a root account signed the tx)
		for hash, rootAcc := range storage.RootKeys {
			if hash == tx.From {
				if rootAcc.Balance+tx.Amount+tx.Fee > MAX_MONEY {
					err = errors.New("Sender does not exist in the State.")
				}
				rootAcc.Balance += tx.Amount
				rootAcc.Balance += tx.Fee
			}
		}

		accSender, accReceiver := storage.GetAccountFromHash(tx.From), storage.GetAccountFromHash(tx.To)
		if accSender == nil {
			logger.Printf("CRITICAL: Sender does not exist in the State: %x\n", tx.From[0:8])
			err = errors.New("Sender does not exist in the State.")
		}

		if accReceiver == nil {
			logger.Printf("CRITICAL: Receiver does not exist in the State: %x\n", tx.To[0:8])
			err = errors.New("Receiver does not exist in the State.")
		}

		//Check transaction counter
		if tx.TxCnt != accSender.TxCnt {
			logger.Printf("Sender txCnt does not match: %v (tx.txCnt) vs. %v (state txCnt)\n", tx.TxCnt, accSender.TxCnt)
			err = errors.New("TxCnt mismatch!")
		}

		//Check sender balance
		if (tx.Amount + tx.Fee) > accSender.Balance {
			logger.Printf("Sender does not have enough balance: %x\n", accSender.Balance)
			err = errors.New("Sender does not have enough funds for the transaction.")
		}

		//Overflow protection
		if tx.Amount+accReceiver.Balance > MAX_MONEY {
			logger.Printf("Transaction amount (%v) would lead to balance overflow at the receiver account (%v)\n", tx.Amount, accReceiver.Balance)
			err = errors.New("Transaction amount would lead to balance overflow at the receiver account\n")
		}

		if err != nil {
			//If it was the first tx in the block, no rollback needed
			if index == 0 {
				return err
			}
			fundsStateChangeRollback(txSlice[0 : index-1])
			return err
		}

		//We're manipulating pointer, no need to write back
		accSender.TxCnt += 1
		accSender.Balance -= tx.Amount
		accReceiver.Balance += tx.Amount
	}

	return nil
}

//We accept config slices with unknown id, but don't act on the payload. This is in case we have not updated to a new
//software with corresponding code to act on the configTx id/payload
func configStateChange(configTxSlice []*protocol.ConfigTx, blockHash [32]byte) {

	var newParameters parameters
	//Initialize it to state right now (before validating config txs)
	newParameters = *activeParameters

	if len(configTxSlice) == 0 {
		return
	}
	var change bool
	for _, tx := range configTxSlice {
		switch tx.Id {
		case protocol.FEE_MINIMUM_ID:
			if parameterBoundsChecking(protocol.FEE_MINIMUM_ID, tx.Payload) {
				newParameters.fee_minimum = tx.Payload
				change = true
			}
		case protocol.BLOCK_SIZE_ID:
			if parameterBoundsChecking(protocol.BLOCK_SIZE_ID, tx.Payload) {
				newParameters.block_size = tx.Payload
				change = true
			}
		case protocol.BLOCK_REWARD_ID:
			if parameterBoundsChecking(protocol.BLOCK_REWARD_ID, tx.Payload) {
				newParameters.block_reward = tx.Payload
				change = true
			}
		case protocol.DIFF_INTERVAL_ID:
			if parameterBoundsChecking(protocol.DIFF_INTERVAL_ID, tx.Payload) {
				newParameters.diff_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_INTERVAL_ID:
			if parameterBoundsChecking(protocol.BLOCK_INTERVAL_ID, tx.Payload) {
				newParameters.block_interval = tx.Payload
				change = true
			}
		}
	}

	//Only add a new parameter struct if a relevant system parameter changed
	if change {
		newParameters.blockHash = blockHash
		parameterSlice = append(parameterSlice, newParameters)
		activeParameters = &parameterSlice[len(parameterSlice)-1]
		logger.Printf("Config parameters changed. New configuration: %v", *activeParameters)
	}
}

func collectTxFees(accTxSlice []*protocol.AccTx, fundsTxSlice []*protocol.FundsTx, configTxSlice []*protocol.ConfigTx, minerHash [32]byte) error {

	var tmpAccTx []*protocol.AccTx
	var tmpFundsTx []*protocol.FundsTx
	var tmpConfigTx []*protocol.ConfigTx

	minerAcc := storage.GetAccountFromHash(minerHash)

	for _, tx := range accTxSlice {
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//Money gets created from thin air, no need to subtract money from root key
		minerAcc.Balance += tx.Fee
		tmpAccTx = append(tmpAccTx, tx)
	}

	//subtract fees from sender (check if that is allowed has already been done in the block validation)
	for _, tx := range fundsTxSlice {
		//Prevent protocol account from overflowing
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee

		senderAcc := storage.GetAccountFromHash(tx.From)
		senderAcc.Balance -= tx.Fee

		tmpFundsTx = append(tmpFundsTx, tx)
	}

	for _, tx := range configTxSlice {
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//No need to subtract money because signed by root account
		minerAcc.Balance += tx.Fee
		tmpConfigTx = append(tmpConfigTx, tx)
	}

	return nil
}

func collectBlockReward(reward uint64, minerHash [32]byte) error {
	miner := storage.GetAccountFromHash(minerHash)

	if miner == nil {
		return errors.New("Miner doesn't exist in the state!")
	}

	if miner.Balance+reward > MAX_MONEY {
		logger.Printf("Miner balance (%v) overflows with block reward (%v).\n", miner.Balance, reward)
		return errors.New("Miner balance overflows with transaction fee.\n")
	}
	miner.Balance += reward
	return nil
}

//For logging purposes
func getState() (state string) {
	for key, acc := range storage.State {
		state += fmt.Sprintf("%x: %v\n", key[0:10], acc)
	}
	return state
}
<commit_msg>Potential future to-do.<commit_after>package miner

import (
	"errors"
	"github.com/lisgie/bazo_miner/protocol"
	"github.com/lisgie/bazo_miner/storage"
	"golang.org/x/crypto/sha3"
	"fmt"
)

func isRootKey(hash [32]byte) bool {
	_, exists := storage.RootKeys[hash]
	return exists
}

func accStateChange(txSlice []*protocol.AccTx) error {

	for _, tx := range txSlice {
		switch tx.Header {
		case 1:
			//First bit set, given account will be a new root account
			//It might be cleaner to move this to the storage package (e.g., storage.Delete(...))
			//leave it here for now (not fully convinced yet)
			newAcc := protocol.Account{Address: tx.PubKey}
			storage.RootKeys[sha3.Sum256(tx.PubKey[:])] = &newAcc
			continue
		case 2:
			//Second bit set, delete account from root account
			delete(storage.RootKeys, sha3.Sum256(tx.PubKey[:]))
			continue
		}

		//Create a regular account
		addressHash := sha3.Sum256(tx.PubKey[:])
		acc := storage.GetAccountFromHash(addressHash)
		if acc != nil {
			//Shouldn't happen, because this should have been prevented when adding an accTx to the block
			return errors.New("CRITICAL: Address already exists in the state")
		}
		newAcc := protocol.Account{Address: tx.PubKey}
		storage.State[addressHash] = &newAcc
	}
	return nil
}

func fundsStateChange(txSlice []*protocol.FundsTx) error {

	for index, tx := range txSlice {

		var err error
		//Check if we have to issue new coins (in case a root account signed the tx)
		for hash, rootAcc := range storage.RootKeys {
			if hash == tx.From {
				if rootAcc.Balance+tx.Amount+tx.Fee > MAX_MONEY {
					err = errors.New("Sender does not exist in the State.")
				}
				rootAcc.Balance += tx.Amount
				rootAcc.Balance += tx.Fee
			}
		}

		accSender, accReceiver := storage.GetAccountFromHash(tx.From), storage.GetAccountFromHash(tx.To)
		if accSender == nil {
			logger.Printf("CRITICAL: Sender does not exist in the State: %x\n", tx.From[0:8])
			err = errors.New("Sender does not exist in the State.")
		}

		if accReceiver == nil {
			logger.Printf("CRITICAL: Receiver does not exist in the State: %x\n", tx.To[0:8])
			err = errors.New("Receiver does not exist in the State.")
		}

		//Check transaction counter
		if tx.TxCnt != accSender.TxCnt {
			logger.Printf("Sender txCnt does not match: %v (tx.txCnt) vs. %v (state txCnt)\n", tx.TxCnt, accSender.TxCnt)
			err = errors.New("TxCnt mismatch!")
		}

		//Check sender balance
		if (tx.Amount + tx.Fee) > accSender.Balance {
			logger.Printf("Sender does not have enough balance: %x\n", accSender.Balance)
			err = errors.New("Sender does not have enough funds for the transaction.")
		}

		//Overflow protection
		if tx.Amount+accReceiver.Balance > MAX_MONEY {
			logger.Printf("Transaction amount (%v) would lead to balance overflow at the receiver account (%v)\n", tx.Amount, accReceiver.Balance)
			err = errors.New("Transaction amount would lead to balance overflow at the receiver account\n")
		}

		if err != nil {
			//If it was the first tx in the block, no rollback needed
			if index == 0 {
				return err
			}
			fundsStateChangeRollback(txSlice[0 : index-1])
			return err
		}

		//We're manipulating pointer, no need to write back
		accSender.TxCnt += 1
		accSender.Balance -= tx.Amount
		accReceiver.Balance += tx.Amount
	}

	return nil
}

//We accept config slices with unknown id, but don't act on the payload. This is in case we have not updated to a new
//software with corresponding code to act on the configTx id/payload
func configStateChange(configTxSlice []*protocol.ConfigTx, blockHash [32]byte) {

	var newParameters parameters
	//Initialize it to state right now (before validating config txs)
	newParameters = *activeParameters

	if len(configTxSlice) == 0 {
		return
	}
	var change bool
	for _, tx := range configTxSlice {
		switch tx.Id {
		case protocol.FEE_MINIMUM_ID:
			if parameterBoundsChecking(protocol.FEE_MINIMUM_ID, tx.Payload) {
				newParameters.fee_minimum = tx.Payload
				change = true
			}
		case protocol.BLOCK_SIZE_ID:
			if parameterBoundsChecking(protocol.BLOCK_SIZE_ID, tx.Payload) {
				newParameters.block_size = tx.Payload
				change = true
			}
		case protocol.BLOCK_REWARD_ID:
			if parameterBoundsChecking(protocol.BLOCK_REWARD_ID, tx.Payload) {
				newParameters.block_reward = tx.Payload
				change = true
			}
		case protocol.DIFF_INTERVAL_ID:
			if parameterBoundsChecking(protocol.DIFF_INTERVAL_ID, tx.Payload) {
				newParameters.diff_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_INTERVAL_ID:
			if parameterBoundsChecking(protocol.BLOCK_INTERVAL_ID, tx.Payload) {
				newParameters.block_interval = tx.Payload
				change = true
			}
		}
	}

	//Only add a new parameter struct if a relevant system parameter changed
	if change {
		newParameters.blockHash = blockHash
		parameterSlice = append(parameterSlice, newParameters)
		activeParameters = &parameterSlice[len(parameterSlice)-1]
		logger.Printf("Config parameters changed. New configuration: %v", *activeParameters)
	}
}

func collectTxFees(accTxSlice []*protocol.AccTx, fundsTxSlice []*protocol.FundsTx, configTxSlice []*protocol.ConfigTx, minerHash [32]byte) error {

	var tmpAccTx []*protocol.AccTx
	var tmpFundsTx []*protocol.FundsTx
	var tmpConfigTx []*protocol.ConfigTx

	minerAcc := storage.GetAccountFromHash(minerHash)

	for _, tx := range accTxSlice {
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//Money gets created from thin air, no need to subtract money from root key
		minerAcc.Balance += tx.Fee
		tmpAccTx = append(tmpAccTx, tx)
	}

	//subtract fees from sender (check if that is allowed has already been done in the block validation)
	for _, tx := range fundsTxSlice {
		//Prevent protocol account from overflowing
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee

		senderAcc := storage.GetAccountFromHash(tx.From)
		senderAcc.Balance -= tx.Fee

		tmpFundsTx = append(tmpFundsTx, tx)
	}

	for _, tx := range configTxSlice {
		if minerAcc.Balance+tx.Fee > MAX_MONEY {
			//Rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//No need to subtract money because signed by root account
		minerAcc.Balance += tx.Fee
		tmpConfigTx = append(tmpConfigTx, tx)
	}

	return nil
}

func collectBlockReward(reward uint64, minerHash [32]byte) error {
	miner := storage.GetAccountFromHash(minerHash)

	if miner == nil {
		return errors.New("Miner doesn't exist in the state!")
	}

	if miner.Balance+reward > MAX_MONEY {
		logger.Printf("Miner balance (%v) overflows with block reward (%v).\n", miner.Balance, reward)
		return errors.New("Miner balance overflows with transaction fee.\n")
	}
	miner.Balance += reward
	return nil
}

//For logging purposes
func getState() (state string) {
	for key, acc := range storage.State {
		state += fmt.Sprintf("%x: %v\n", key[0:10], acc)
	}
	return state
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/fsouza/go-dockerclient"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("main")
var ETCD = "quay.io/coreos/etcd:v2.1.3"
var DOCKER_SOCK_PATH = "unix:///var/run/docker.sock"

func runContainer(client *docker.Client, name, image string,
	binds []string, args []string) error {
	err := client.PullImage(docker.PullImageOptions{
		Repository:   image,
		OutputStream: os.Stdout},
		docker.AuthConfiguration{})
	if err != nil {
		return err
	}

	container, err := client.CreateContainer(docker.CreateContainerOptions{
		Name: name,
		Config: &docker.Config{
			Image: image,
			Cmd:   args,
		}})
	if err != nil {
		return err
	}

	err = client.StartContainer(container.ID,
		&docker.HostConfig{NetworkMode: "host", Binds: binds})
	if err != nil {
		return err
	}

	return nil
}

func BootWorker(etcdToken string) error {
	client, err := docker.NewClient(DOCKER_SOCK_PATH)
	if err != nil {
		return err
	}

	log.Info("Attempting to boot etcd-client")
	err = runContainer(client, "etcd-client", ETCD,
		[]string{"/usr/share/ca-certificates:/etc/ssl/certs"},
		[]string{"--discovery=" + etcdToken, "--proxy=on"})
	if err != nil {
		return err
	}

	log.Info("Successfully booted etcd-client")
	return nil
}

func BootMaster(etcdToken string, ip string) error {
	client, err := docker.NewClient(DOCKER_SOCK_PATH)
	if err != nil {
		return err
	}

	advertiseClient := fmt.Sprintf("http://%s:2379", ip)
	listenClient := fmt.Sprintf("http://0.0.0.0:2379")

	initialAdvertisePeer := fmt.Sprintf("http://%s:2380", ip)
	listenPeer := fmt.Sprintf("http://%s:2380", ip)

	args := []string{"--discovery=" + etcdToken,
		"--advertise-client-urls=" + advertiseClient,
		"--initial-advertise-peer-urls=" + initialAdvertisePeer,
		"--listen-client-urls=" + listenClient,
		"--listen-peer-urls=" + listenPeer}

	binds := []string{"/usr/share/ca-certificates:/etc/ssl/certs"}

	log.Info("Attempting to boot etcd-master")
	err = runContainer(client, "etcd-master", ETCD, binds, args)
	if err != nil {
		return err
	}

	log.Info("Successfully booted etcd-master")
	return err
}
<commit_msg>minion: Use const for constant variables<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/fsouza/go-dockerclient"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("main")

const ETCD = "quay.io/coreos/etcd:v2.1.3"
const DOCKER_SOCK_PATH = "unix:///var/run/docker.sock"

func runContainer(client *docker.Client, name, image string,
	binds []string, args []string) error {
	err := client.PullImage(docker.PullImageOptions{
		Repository:   image,
		OutputStream: os.Stdout},
		docker.AuthConfiguration{})
	if err != nil {
		return err
	}

	container, err := client.CreateContainer(docker.CreateContainerOptions{
		Name: name,
		Config: &docker.Config{
			Image: image,
			Cmd:   args,
		}})
	if err != nil {
		return err
	}

	err = client.StartContainer(container.ID,
		&docker.HostConfig{NetworkMode: "host", Binds: binds})
	if err != nil {
		return err
	}

	return nil
}

func BootWorker(etcdToken string) error {
	client, err := docker.NewClient(DOCKER_SOCK_PATH)
	if err != nil {
		return err
	}

	log.Info("Attempting to boot etcd-client")
	err = runContainer(client, "etcd-client", ETCD,
		[]string{"/usr/share/ca-certificates:/etc/ssl/certs"},
		[]string{"--discovery=" + etcdToken, "--proxy=on"})
	if err != nil {
		return err
	}

	log.Info("Successfully booted etcd-client")
	return nil
}

func BootMaster(etcdToken string, ip string) error {
	client, err := docker.NewClient(DOCKER_SOCK_PATH)
	if err != nil {
		return err
	}

	advertiseClient := fmt.Sprintf("http://%s:2379", ip)
	listenClient := fmt.Sprintf("http://0.0.0.0:2379")

	initialAdvertisePeer := fmt.Sprintf("http://%s:2380", ip)
	listenPeer := fmt.Sprintf("http://%s:2380", ip)

	args := []string{"--discovery=" + etcdToken,
		"--advertise-client-urls=" + advertiseClient,
		"--initial-advertise-peer-urls=" + initialAdvertisePeer,
		"--listen-client-urls=" + listenClient,
		"--listen-peer-urls=" + listenPeer}

	binds := []string{"/usr/share/ca-certificates:/etc/ssl/certs"}

	log.Info("Attempting to boot etcd-master")
	err = runContainer(client, "etcd-master", ETCD, binds, args)
	if err != nil {
		return err
	}

	log.Info("Successfully booted etcd-master")
	return err
}
<|endoftext|>


<commit_before>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package render

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"

	"github.com/gin-gonic/gin/internal/bytesconv"
	"github.com/gin-gonic/gin/internal/json"
)

// JSON contains the given interface object.
type JSON struct {
	Data interface{}
}

// IndentedJSON contains the given interface object.
type IndentedJSON struct {
	Data interface{}
}

// SecureJSON contains the given interface object and its prefix.
type SecureJSON struct {
	Prefix string
	Data   interface{}
}

// JsonpJSON contains the given interface object its callback.
type JsonpJSON struct {
	Callback string
	Data     interface{}
}

// AsciiJSON contains the given interface object.
type AsciiJSON struct {
	Data interface{}
}

// PureJSON contains the given interface object.
type PureJSON struct {
	Data interface{}
}

var (
	jsonContentType      = []string{"application/json; charset=utf-8"}
	jsonpContentType     = []string{"application/javascript; charset=utf-8"}
	jsonASCIIContentType = []string{"application/json"}
)

// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
	if err = WriteJSON(w, r.Data); err != nil {
		panic(err)
	}
	return
}

// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
	writeContentType(w, jsonContentType)
	jsonBytes, err := json.Marshal(obj)
	if err != nil {
		return err
	}
	_, err = w.Write(jsonBytes)
	return err
}

// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	jsonBytes, err := json.MarshalIndent(r.Data, "", "    ")
	if err != nil {
		return err
	}
	_, err = w.Write(jsonBytes)
	return err
}

// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	jsonBytes, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}
	// if the jsonBytes is array values
	if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes,
		bytesconv.StringToBytes("]")) {
		_, err = w.Write(bytesconv.StringToBytes(r.Prefix))
		if err != nil {
			return err
		}
	}
	_, err = w.Write(jsonBytes)
	return err
}

// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
	r.WriteContentType(w)
	ret, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}

	if r.Callback == "" {
		_, err = w.Write(ret)
		return err
	}

	callback := template.JSEscapeString(r.Callback)
	_, err = w.Write(bytesconv.StringToBytes(callback))
	if err != nil {
		return err
	}
	_, err = w.Write(bytesconv.StringToBytes("("))
	if err != nil {
		return err
	}
	_, err = w.Write(ret)
	if err != nil {
		return err
	}
	_, err = w.Write(bytesconv.StringToBytes(");"))
	if err != nil {
		return err
	}

	return nil
}

// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonpContentType)
}

// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
	r.WriteContentType(w)
	ret, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}

	var buffer bytes.Buffer
	for _, r := range bytesconv.BytesToString(ret) {
		cvt := string(r)
		if r >= 128 {
			cvt = fmt.Sprintf("\\u%04x", int64(r))
		}
		buffer.WriteString(cvt)
	}

	_, err = w.Write(buffer.Bytes())
	return err
}

// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonASCIIContentType)
}

// Render (PureJSON) writes custom ContentType and encodes the given interface object.
func (r PureJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	encoder := json.NewEncoder(w)
	encoder.SetEscapeHTML(false)
	return encoder.Encode(r.Data)
}

// WriteContentType (PureJSON) writes custom ContentType.
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}
<commit_msg>minor tweaks,optimize code (#2788)<commit_after>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package render

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"

	"github.com/gin-gonic/gin/internal/bytesconv"
	"github.com/gin-gonic/gin/internal/json"
)

// JSON contains the given interface object.
type JSON struct {
	Data interface{}
}

// IndentedJSON contains the given interface object.
type IndentedJSON struct {
	Data interface{}
}

// SecureJSON contains the given interface object and its prefix.
type SecureJSON struct {
	Prefix string
	Data   interface{}
}

// JsonpJSON contains the given interface object its callback.
type JsonpJSON struct {
	Callback string
	Data     interface{}
}

// AsciiJSON contains the given interface object.
type AsciiJSON struct {
	Data interface{}
}

// PureJSON contains the given interface object.
type PureJSON struct {
	Data interface{}
}

var (
	jsonContentType      = []string{"application/json; charset=utf-8"}
	jsonpContentType     = []string{"application/javascript; charset=utf-8"}
	jsonASCIIContentType = []string{"application/json"}
)

// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
	if err = WriteJSON(w, r.Data); err != nil {
		panic(err)
	}
	return
}

// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
	writeContentType(w, jsonContentType)
	jsonBytes, err := json.Marshal(obj)
	if err != nil {
		return err
	}
	_, err = w.Write(jsonBytes)
	return err
}

// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	jsonBytes, err := json.MarshalIndent(r.Data, "", "    ")
	if err != nil {
		return err
	}
	_, err = w.Write(jsonBytes)
	return err
}

// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	jsonBytes, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}
	// if the jsonBytes is array values
	if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes,
		bytesconv.StringToBytes("]")) {
		if _, err = w.Write(bytesconv.StringToBytes(r.Prefix)); err != nil {
			return err
		}
	}
	_, err = w.Write(jsonBytes)
	return err
}

// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}

// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
	r.WriteContentType(w)
	ret, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}

	if r.Callback == "" {
		_, err = w.Write(ret)
		return err
	}

	callback := template.JSEscapeString(r.Callback)
	if _, err = w.Write(bytesconv.StringToBytes(callback)); err != nil {
		return err
	}

	if _, err = w.Write(bytesconv.StringToBytes("(")); err != nil {
		return err
	}

	if _, err = w.Write(ret); err != nil {
		return err
	}

	if _, err = w.Write(bytesconv.StringToBytes(");")); err != nil {
		return err
	}

	return nil
}

// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonpContentType)
}

// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
	r.WriteContentType(w)
	ret, err := json.Marshal(r.Data)
	if err != nil {
		return err
	}

	var buffer bytes.Buffer
	for _, r := range bytesconv.BytesToString(ret) {
		cvt := string(r)
		if r >= 128 {
			cvt = fmt.Sprintf("\\u%04x", int64(r))
		}
		buffer.WriteString(cvt)
	}

	_, err = w.Write(buffer.Bytes())
	return err
}

// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonASCIIContentType)
}

// Render (PureJSON) writes custom ContentType and encodes the given interface object.
func (r PureJSON) Render(w http.ResponseWriter) error {
	r.WriteContentType(w)
	encoder := json.NewEncoder(w)
	encoder.SetEscapeHTML(false)
	return encoder.Encode(r.Data)
}

// WriteContentType (PureJSON) writes custom ContentType.
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
	writeContentType(w, jsonContentType)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.CreateRequest:
		to := &CreateFileOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SymlinkRequest:
		to := &CreateSymlinkOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.NewName,
			Target: typed.Target,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RenameRequest:
		to := &RenameOp{
			bfReq:     typed,
			OldParent: InodeID(typed.Header.Node),
			OldName:   typed.OldName,
			NewParent: InodeID(typed.NewDir),
			NewName:   typed.NewName,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<commit_msg>CreateFileOp<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpCreate:
		size := fusekernel.CreateInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.CreateIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &CreateFileOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SymlinkRequest:
		to := &CreateSymlinkOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.NewName,
			Target: typed.Target,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RenameRequest:
		to := &RenameOp{
			bfReq:     typed,
			OldParent: InodeID(typed.Header.Node),
			OldName:   typed.OldName,
			NewParent: InodeID(typed.NewDir),
			NewName:   typed.NewName,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<|endoftext|>