<commit_before>package main

import (
	"bytes"
	"errors"
	"sort"
	"strings"
	"time"

	"github.com/Cloud-Foundations/golib/pkg/communications/configuredemail"
)

const emailAdminTemplateData = `
{{define "Bootstrap OTP Admin Email"}}
From: {{.InitiatorAddr}}
To: {{.AdminAddrs}}
Subject: Keymaster Bootstrap OTP generated for {{.Username}}

A Bootstrap OTP was generated by {{.InitiatorUser}} for user: {{.Username}}

The OTP fingerprint is: {{printf "%x" .Fingerprint}}

The OTP will expire in {{.Duration}}.

The user profile may be viewed at: {{.HostIdentity}}/profile/{{.Username}}
{{end}}
`

const emailUserTemplateData = `
{{define "Bootstrap OTP User Email"}}
From: {{.InitiatorAddr}}
To: {{.UserAddr}}
Subject: Welcome to Keymaster

Hi, {{.Username}}. Welcome to Keymaster. Please log in to:
{{.HostIdentity}}

with your username and password. After this step you will be asked to enter
your one-time passcode (Bootstrap OTP) which is:
{{.OTP}}

In case of debugging, your OTP fingerprint is: {{printf "%x" .Fingerprint}}

Please register your U2F security key after login.

You have {{.Duration}} to complete this operation before this passcode expires.
{{end}}
`

const emailTimeout = time.Second * 15

type bootstrapOtpEmailData struct {
	AdminAddrs    string
	Duration      time.Duration
	Fingerprint   [4]byte
	HostIdentity  string
	InitiatorAddr string
	InitiatorUser string
	OTP           string
	UserAddr      string
	Username      string
}

func (state *RuntimeState) initEmailDefaults() {
	state.Config.Email.AwsSecretLifetime = time.Minute * 5
}

func (state *RuntimeState) setupEmail() error {
	if state.Config.Email.Domain == "" {
		return nil
	}
	var err error
	state.emailManager, err = configuredemail.New(
		state.Config.Email.EmailConfig, logger)
	if err != nil {
		return err
	}
	return nil
}

func (state *RuntimeState) sendBootstrapOtpEmail(hash []byte, OTP string,
	duration time.Duration, initiatorUser, targetUser string) error {
	emailData := bootstrapOtpEmailData{
		Duration:      duration,
		HostIdentity:  state.Config.Base.HostIdentity,
		OTP:           OTP,
		InitiatorAddr: initiatorUser + "@" + state.Config.Email.Domain,
		InitiatorUser: initiatorUser,
		UserAddr:      targetUser + "@" + state.Config.Email.Domain,
		Username:      targetUser,
	}
	copy(emailData.Fingerprint[:], hash[:4])
	adminUsers := make(map[string]struct{})
	adminUsers[initiatorUser] = struct{}{}
	for _, user := range state.Config.Base.AdminUsers {
		adminUsers[user] = struct{}{}
	}
	// TODO(rgooch): refactor to use a UserInfo interface which is set up in
	//               config.go. This will require changes in several files.
	if state.gitDB != nil {
		for _, adminGroup := range state.Config.Base.AdminGroups {
			users, err := state.gitDB.GetUsersInGroup(adminGroup)
			if err != nil {
				return err
			}
			for _, user := range users {
				adminUsers[user] = struct{}{}
			}
		}
	}
	adminAddrs := make([]string, 0, len(adminUsers))
	for user := range adminUsers {
		adminAddrs = append(adminAddrs, user+"@"+state.Config.Email.Domain)
	}
	sort.Strings(adminAddrs)
	emailData.AdminAddrs = strings.Join(adminAddrs, ",")
	buffer := &bytes.Buffer{}
	err := state.textTemplates.ExecuteTemplate(buffer,
		"Bootstrap OTP Admin Email", emailData)
	if err != nil {
		return err
	}
	err = state.sendMail(emailData.InitiatorAddr, adminAddrs, buffer.Bytes(),
		emailTimeout)
	if err != nil {
		return err
	}
	buffer = &bytes.Buffer{}
	err = state.textTemplates.ExecuteTemplate(buffer,
		"Bootstrap OTP User Email", emailData)
	if err != nil {
		return err
	}
	err = state.sendMail(emailData.InitiatorAddr, []string{emailData.UserAddr},
		buffer.Bytes(), emailTimeout)
	if err != nil {
		return err
	}
	return nil
}

func (state *RuntimeState) sendMail(from string, to []string, msg []byte,
	timeout time.Duration) error {
	for msg[0] == '\n' { // Leading blank lines would prevent email being sent.
		msg = msg[1:]
	}
	timer := time.NewTimer(timeout)
	errorChan := make(chan error)
	go func() {
		err := state.emailManager.SendMail(from, to, msg)
		select {
		case errorChan <- err: // Status was consumed.
		default: // Status consumer has gone. Log it.
			if err != nil {
				state.logger.Printf("late failure sending email: %s\n", err)
			} else {
				state.logger.Println("late success sending email")
			}
		}
	}()
	select {
	case err := <-errorChan:
		return err
	case <-timer.C:
		return errors.New("timed out sending email")
	}
}
<commit_msg>Don't send admin notification to self-service Bootstrap OTP user.<commit_after>package main

import (
	"bytes"
	"errors"
	"sort"
	"strings"
	"time"

	"github.com/Cloud-Foundations/golib/pkg/communications/configuredemail"
)

const emailAdminTemplateData = `
{{define "Bootstrap OTP Admin Email"}}
From: {{.InitiatorAddr}}
To: {{.AdminAddrs}}
Subject: Keymaster Bootstrap OTP generated for {{.Username}}

A Bootstrap OTP was generated by {{.InitiatorUser}} for user: {{.Username}}

The OTP fingerprint is: {{printf "%x" .Fingerprint}}

The OTP will expire in {{.Duration}}.

The user profile may be viewed at: {{.HostIdentity}}/profile/{{.Username}}
{{end}}
`

const emailUserTemplateData = `
{{define "Bootstrap OTP User Email"}}
From: {{.InitiatorAddr}}
To: {{.UserAddr}}
Subject: Welcome to Keymaster

Hi, {{.Username}}. Welcome to Keymaster. Please log in to:
{{.HostIdentity}}

with your username and password. After this step you will be asked to enter
your one-time passcode (Bootstrap OTP) which is:
{{.OTP}}

In case of debugging, your OTP fingerprint is: {{printf "%x" .Fingerprint}}

Please register your U2F security key after login.

You have {{.Duration}} to complete this operation before this passcode expires.
{{end}}
`

const emailTimeout = time.Second * 15

type bootstrapOtpEmailData struct {
	AdminAddrs    string
	Duration      time.Duration
	Fingerprint   [4]byte
	HostIdentity  string
	InitiatorAddr string
	InitiatorUser string
	OTP           string
	UserAddr      string
	Username      string
}

func (state *RuntimeState) initEmailDefaults() {
	state.Config.Email.AwsSecretLifetime = time.Minute * 5
}

func (state *RuntimeState) setupEmail() error {
	if state.Config.Email.Domain == "" {
		return nil
	}
	var err error
	state.emailManager, err = configuredemail.New(
		state.Config.Email.EmailConfig, logger)
	if err != nil {
		return err
	}
	return nil
}

func (state *RuntimeState) sendBootstrapOtpEmail(hash []byte, OTP string,
	duration time.Duration, initiatorUser, targetUser string) error {
	emailData := bootstrapOtpEmailData{
		Duration:      duration,
		HostIdentity:  state.Config.Base.HostIdentity,
		OTP:           OTP,
		InitiatorAddr: initiatorUser + "@" + state.Config.Email.Domain,
		InitiatorUser: initiatorUser,
		UserAddr:      targetUser + "@" + state.Config.Email.Domain,
		Username:      targetUser,
	}
	copy(emailData.Fingerprint[:], hash[:4])
	adminUsers := make(map[string]struct{})
	if initiatorUser != targetUser {
		adminUsers[initiatorUser] = struct{}{}
	}
	for _, user := range state.Config.Base.AdminUsers {
		adminUsers[user] = struct{}{}
	}
	// TODO(rgooch): refactor to use a UserInfo interface which is set up in
	//               config.go. This will require changes in several files.
	if state.gitDB != nil {
		for _, adminGroup := range state.Config.Base.AdminGroups {
			users, err := state.gitDB.GetUsersInGroup(adminGroup)
			if err != nil {
				return err
			}
			for _, user := range users {
				adminUsers[user] = struct{}{}
			}
		}
	}
	adminAddrs := make([]string, 0, len(adminUsers))
	for user := range adminUsers {
		adminAddrs = append(adminAddrs, user+"@"+state.Config.Email.Domain)
	}
	sort.Strings(adminAddrs)
	emailData.AdminAddrs = strings.Join(adminAddrs, ",")
	buffer := &bytes.Buffer{}
	err := state.textTemplates.ExecuteTemplate(buffer,
		"Bootstrap OTP Admin Email", emailData)
	if err != nil {
		return err
	}
	err = state.sendMail(emailData.InitiatorAddr, adminAddrs, buffer.Bytes(),
		emailTimeout)
	if err != nil {
		return err
	}
	buffer = &bytes.Buffer{}
	err = state.textTemplates.ExecuteTemplate(buffer,
		"Bootstrap OTP User Email", emailData)
	if err != nil {
		return err
	}
	err = state.sendMail(emailData.InitiatorAddr, []string{emailData.UserAddr},
		buffer.Bytes(), emailTimeout)
	if err != nil {
		return err
	}
	return nil
}

func (state *RuntimeState) sendMail(from string, to []string, msg []byte,
	timeout time.Duration) error {
	for msg[0] == '\n' { // Leading blank lines would prevent email being sent.
		msg = msg[1:]
	}
	timer := time.NewTimer(timeout)
	errorChan := make(chan error)
	go func() {
		err := state.emailManager.SendMail(from, to, msg)
		select {
		case errorChan <- err: // Status was consumed.
		default: // Status consumer has gone. Log it.
			if err != nil {
				state.logger.Printf("late failure sending email: %s\n", err)
			} else {
				state.logger.Println("late success sending email")
			}
		}
	}()
	select {
	case err := <-errorChan:
		return err
	case <-timer.C:
		return errors.New("timed out sending email")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/pkg/logrusx"
	"github.com/spf13/pflag"
)

func main() {
	logrus.SetFormatter(&logrusx.JSONFormatter{})

	config := newConfig(nil, nil)
	pflag.Parse()

	dieOnError(config.loadConfig())
	dieOnError(config.setupLogging())

	sp, err := newStatsPusher(config)
	dieOnError(err)

	dieOnError(sp.run())
	sp.stopOnSignal()
}

func dieOnError(err error) {
	if err != nil {
		logrus.Fatal("encountered an error during startup")
	}
}
<commit_msg>Temporary fix for statspusher logging - log the error at the top<commit_after>package main

import (
	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/pkg/logrusx"
	"github.com/spf13/pflag"
)

func main() {
	logrus.SetFormatter(&logrusx.JSONFormatter{})

	config := newConfig(nil, nil)
	pflag.Parse()

	dieOnError(config.loadConfig())
	dieOnError(config.setupLogging())

	sp, err := newStatsPusher(config)
	dieOnError(err)

	dieOnError(sp.run())
	sp.stopOnSignal()
}

func dieOnError(err error) {
	if err != nil {
		logrus.WithField("error", err).Fatal("encountered an error during startup")
	}
}
<|endoftext|>


<commit_before>package main

import "net"
import "fmt"

// Mainloop tfor tcp connection
func runServer(addr string, port int) err error {
	lstn, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
	if err != nil {
		return err
	}
	defer lstn.Close()

	for {
		conn, err :=  lstn.Accept()
		if err != nil {
			return err
		}
		defer conn.Close()

		err = comm(conn, addr)
	}
}

// communicate with the client, main logic
func comm(conn Conn) {

}

<commit_msg>[devel]<commit_after>package main

import "net"
import "fmt"

// Define the message format
type message {
    src string,
    dest string,
    err error
}

// Mainloop tfor tcp connection
func runServer(addr string, port int) err error {
	lstn, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
	if err != nil {
		return err
	}
	defer lstn.Close()

    m := make(chan message)

	for {
		conn, err :=  lstn.Accept()
		if err != nil {
			return err
		}
        go comm(conn, m)
		defer conn.Close()
	}
}

// communicate with the client, main logic
func comm(conn Conn, m chan message) {
    // First send connection message
}

<|endoftext|>


<commit_before>// wc -- print byte, line, or word counts for files
// Part of goutils (https://github.com/trevorparker/goutils)
//
// Copyright (c) 2014 Trevor Parker <trevor@trevorparker.com>
// All rights reserved
//
// Distributed under the terms of the Modified BSD License (see LICENSE)

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

type arg struct {
	count_bytes bool
	count_lines bool
	count_words bool
	file        []string
}

const (
	usage_message string = "usage: wc [OPTION ...] [FILE ...]"
	help_message  string = `Count bytes, lines, or words for FILE or STDIN to STDOUT.

  -c, --bytes     count bytes
  -l, --lines     count newlines
  -w, --words     count words
  -h, --help      print this help message and exit
`
)

func usage(error string) {
	fmt.Fprintf(os.Stderr, "wc: %s\n%s\n", error, usage_message)
	os.Exit(1)
}

func help() {
	fmt.Printf("%s\n%s", usage_message, help_message)
	os.Exit(0)
}

func wc(file io.Reader, args arg, size int64) int64 {
	c := size

	if file == nil {
		file = os.Stdin
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanBytes)
		for s.Scan() {
			c++
		}
	}

	if args.count_lines && !args.count_bytes {
		c = 0
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanLines)
		for s.Scan() {
			c++
		}
	} else if args.count_words && !args.count_bytes {
		c = 0
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanWords)
		for s.Scan() {
			c++
		}
	}

	return c
}

func main() {
	args := arg{false, false, false, []string{}}
	reached_files := false
	for i := 1; i < len(os.Args); i++ {
		if reached_files == false {
			if os.Args[i] == "-h" || os.Args[i] == "--help" {
				help()
			}
			if os.Args[i] == "-c" || os.Args[i] == "--bytes" {
				args.count_bytes = true
				continue
			}
			if os.Args[i] == "-l" || os.Args[i] == "--lines" {
				args.count_lines = true
				continue
			}
			if os.Args[i] == "-w" || os.Args[i] == "--words" {
				args.count_words = true
				continue
			}
		}
		arg_v := os.Args[i]
		reached_files = true
		args.file = append(args.file, arg_v)
	}
	if len(args.file) == 0 {
		count := wc(nil, args, 0)
		fmt.Fprintf(os.Stdout, "%d\n", count)
	} else {
		for i := range args.file {
			file, err := os.Open(args.file[i])
			stat, err := file.Stat()
			size := stat.Size()
			if err != nil {
				panic(err)
			}
			count := wc(file, args, size)
			fmt.Fprintf(os.Stdout, "%d %s\n", count, args.file[i])
		}
	}
}
<commit_msg>Fix bug where STDIN bytes weren't counted<commit_after>// wc -- print byte, line, or word counts for files
// Part of goutils (https://github.com/trevorparker/goutils)
//
// Copyright (c) 2014 Trevor Parker <trevor@trevorparker.com>
// All rights reserved
//
// Distributed under the terms of the Modified BSD License (see LICENSE)

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

type arg struct {
	count_bytes bool
	count_lines bool
	count_words bool
	file        []string
}

const (
	usage_message string = "usage: wc [OPTION ...] [FILE ...]"
	help_message  string = `Count bytes, lines, or words for FILE or STDIN to STDOUT.

  -c, --bytes     count bytes
  -l, --lines     count newlines
  -w, --words     count words
  -h, --help      print this help message and exit
`
)

func usage(error string) {
	fmt.Fprintf(os.Stderr, "wc: %s\n%s\n", error, usage_message)
	os.Exit(1)
}

func help() {
	fmt.Printf("%s\n%s", usage_message, help_message)
	os.Exit(0)
}

func wc(file io.Reader, args arg, size int64) int64 {
	c := size

	if file == nil {
		file = os.Stdin
	}

	if c == 0 && args.count_bytes {
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanBytes)
		for s.Scan() {
			c++
		}
	}

	if args.count_lines && !args.count_bytes {
		c = 0
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanLines)
		for s.Scan() {
			c++
		}
	} else if args.count_words && !args.count_bytes {
		c = 0
		s := bufio.NewScanner(file)
		s.Split(bufio.ScanWords)
		for s.Scan() {
			c++
		}
	}

	return c
}

func main() {
	args := arg{false, false, false, []string{}}
	reached_files := false
	for i := 1; i < len(os.Args); i++ {
		if reached_files == false {
			if os.Args[i] == "-h" || os.Args[i] == "--help" {
				help()
			}
			if os.Args[i] == "-c" || os.Args[i] == "--bytes" {
				args.count_bytes = true
				continue
			}
			if os.Args[i] == "-l" || os.Args[i] == "--lines" {
				args.count_lines = true
				continue
			}
			if os.Args[i] == "-w" || os.Args[i] == "--words" {
				args.count_words = true
				continue
			}
		}
		arg_v := os.Args[i]
		reached_files = true
		args.file = append(args.file, arg_v)
	}
	if len(args.file) == 0 {
		count := wc(nil, args, 0)
		fmt.Fprintf(os.Stdout, "%d\n", count)
	} else {
		for i := range args.file {
			file, err := os.Open(args.file[i])
			stat, err := file.Stat()
			size := stat.Size()
			if err != nil {
				panic(err)
			}
			count := wc(file, args, size)
			fmt.Fprintf(os.Stdout, "%d %s\n", count, args.file[i])
		}
	}
}
<|endoftext|>


<commit_before>// Package wedge is a lightweight web framework which intends to
// cut-down on oft written boilerplate code
package wedge

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

const (
	HTML = iota
	JSON
	STATIC
	ICON
)

const (
	FileChunks = 1024
)

var (
	routes  []*url
	TIMEOUT = time.Second
)

type handlertype int

// Handler functions should match this signature
type view func(*http.Request) (string, int)

// appServer is our server instance which holds the ServeHTTP method
// so that it satisfies the http.Server interface.
type appServer struct {
	port       string
	routes     []*url
	timeout    time.Duration
	cache_map  *safeMap
	handler404 view
	stat_map   *safeMap
}

// appServer constructor
func NewAppServer(port string, timeout time.Duration) *appServer {
	return &appServer{
		port:      port,
		routes:    make([]*url, 0),
		timeout:   timeout,
		cache_map: NewSafeMap(),
	}
}

// Attaches more *urls to the Routes slice on the appServer value
func (App *appServer) AddURLs(patterns ...*url) {
	for _, url := range patterns {
		App.routes = append(App.routes, url)
	}
}

// EnableStatTracking does exactly what it says on the tin
//
// EnableStatTracking creates a NewSafeMap under the stat_map field which will
// then be used to increment and aggregate hits to URLs.
//
// This function will append a new *url onto the associated appServer. The url
// which this is under is ^/statistics/?$.
func (App *appServer) EnableStatTracking() {
	App.stat_map = NewSafeMap()

	staturl := makeurl("^/statistics/?$", "Statistics", func(req *http.Request) (string, int) {

		rawdata, ok := App.stat_map.Do(func(m map[interface{}]interface{}) interface{} {
			// we could return m here but that would mean we've broken the
			// reason why we made the map safe in the first place.

			outstr := "
"
			for key, value := range m {
				outstr += fmt.Sprintf("
%s	", key.(string))
				outstr += fmt.Sprintf("%d
", value.(int))
			}
			outstr += "
"
			return outstr
		})

		if !ok {
			return "Failure getting data", 500
		}
		return rawdata.(string), 200

	}, HTML, 0)
	App.routes = append(App.routes, staturl)
}

// incrementStats is a non-blocking method to increment a page counter
// for individual routes.
func (App *appServer) incrementStats(k string) {
	if App.stat_map == nil {
		panic("Cannot increment statistics when it has not been enabled!")
	}

	// create a goroutine which sends a function literal to the async
	// map which tries to increment the value under the k string.
	go App.stat_map.Do(func(m map[interface{}]interface{}) interface{} {
		val, ok := m[k]
		if ok {
			val, ok := val.(int)
			if ok {
				val++
				m[k] = val
			}
		} else {
			m[k] = 1
		}
		return true
	})
}

// Sets the 404 Handler for the appServer to fn.
func (App *appServer) Handler404(fn view) {
	App.handler404 = fn
}

// This is the main 'event loop' for the web server. All requests are
// sent to this handler, which checks the incoming request against
// all the routes we have setup if it finds a match it will invoke
// the handler which is attached to that match.
func (App *appServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	request := req.URL.Path

	for _, route := range App.routes {
		matches := route.match.FindAllStringSubmatch(request, 1)
		if len(matches) > 0 {
			log.Println("Request:", route.name, request)
			if App.stat_map != nil {
				App.incrementStats(route.name)
			}
			resp, status := App.getResponse(route, req)

			if status == 404 {
				App.handle404req(w, req)
				return
			}

			switch route.viewtype {
			case HTML:
				io.WriteString(w, resp)
				return
			case JSON:
				w.Header().Set("Content-type", "application/json")
				json.NewEncoder(w).Encode(map[string]string{
					"message": resp,
				})
				return
			case STATIC:
				if w.Header().Get("Content-Type") == "" {
					reqstr := req.URL.Path[len(route.rawre):]
					ctype := mime.TypeByExtension(filepath.Ext(reqstr))
					w.Header().Set("Content-Type", ctype)
				}
				io.WriteString(w, resp)
				return
			case ICON:
				w.Header().Set("Content-Type", "image/x-icon")
				io.WriteString(w, resp)
				return
			default:
				panic("Unknown handler type!")
			}
		}
	}
	App.handle404req(w, req)
	return
}

// handle404req checks if the 404 handler is a custom one and uses that, if not,
// it uses the built-in NotFound function.
func (App *appServer) handle404req(w http.ResponseWriter, req *http.Request) {
	log.Println("404", req.URL.Path)
	App.incrementStats("404")
	w.WriteHeader(404)

	if App.handler404 != nil {
		resp, _ := App.handler404(req)
		io.WriteString(w, resp)
		return
	} else {
		http.NotFound(w, req)
		return
	}
}

// getResponse checks the *url's cache_duration, if the cache duration
// is zero. Then we never cache the response. Otherwise, we check to
// see if the cache_duration has passed by reading the timeout channel
// if so, we run the URL handler associated with the route and store it's
// new response value. We then store the response in the cache_map and
// return it to the client.
//
// Accessing the cache_map from multiple threads is safe. There are two
// implementations of a safe map included with this library. One is sync'd
// with channels (safeMap) and the other is sync'd with a mutex lock
// (lockMap). We currently use the safeMap.
func (App *appServer) getResponse(route *url, req *http.Request) (string, int) {

	if route.cache_duration == 0 {
		return route.handler(req)
	}

	select {
	case <-route.timeout:
		go func(d time.Duration, ch chan bool) {
			log.Println("Timed out")
			f := time.After(d * TIMEOUT)
			<-f
			go func() {
				ch <- true
			}()
		}(route.cache_duration, route.timeout)
		resp, err := route.handler(req)
		if !App.cache_map.Insert(req.URL.Path, resp) {
			panic("Inserting into cache_map failure!")
		}
		return resp, err
	default:
		resp, ok := App.cache_map.Find(req.URL.Path).(string)
		if !ok {
			resp, _ = route.handler(req)

		}
		if !App.cache_map.Insert(req.URL.Path, resp) {
			panic("Inserting into cache_map failure!")
		}
		return resp, http.StatusOK
	}
	panic("unreachable")
}

// Basic URL struct which holds a match, a name and a handler function
//
// match:
//     Match is a *regexp.Regexp which we will use to check incoming
//     request URLs against and return the handler on any that match
// name:
//     Name is a simple string of what the url should be referred to as
// handler:
//     Handler is a wedge.view function which we will use against any
//     requests that match `match`.
type url struct {
	match          *regexp.Regexp
	name           string
	handler        view
	viewtype       handlertype
	rawre          string
	cache_duration time.Duration
	timeout        chan bool
}

func (u *url) String() string {
	return fmt.Sprintf(
		"{\n  URL: %s\n  Handler: %s\n}", u.match, u.name,
	)
}

// Unexported method which forms as the base method to return *url values
//
// We chose to do it like this because we can have specialized methods
// which have a simply API but fill in certain blanks for this. And the
// makeurl method can have a relatively clunky API since the work will
// be done under the hood.
func makeurl(re, name string, v view, t handlertype, duration time.Duration) *url {
	match := regexp.MustCompile(re)
	timeoutchan := make(chan bool)

	// Initialize the channel and seed with a value
	// so the first request will put the response
	// into memory
	if duration < 0 {
		duration = 30 * 12 * 30 * time.Hour
	}
	if duration > 0 {
		go func() {
			timeoutchan <- true
		}()
	}

	return &url{
		match:          match,
		name:           name,
		handler:        v,
		viewtype:       t,
		rawre:          re,
		cache_duration: duration,
		timeout:        timeoutchan,
	}
}

// URL is a function which returns a *url value.
// re:
//     re is a string which will be compiled to a *regexp.Regexp
//     and will panic if the regular expression cannot be compiled
// name:
//     Name is a simple string of what the url should be referred to as
// handler:
//     Handler is a wedge.view function which we will use against any
//     requests that match `match`.
func URL(re, name string, v view, t handlertype) *url {
	return makeurl(re, name, v, t, 0)
}

// StaticFiles is a not so light wrapper around the URL function
//
// We start off receiving an 'as' string which marks the URL to which
// we match against. We then take a []string which is filepaths to all
// the locations in which an incoming file request should be checked
// against. The file is read in chunks as per the module level constant4
// FileChunk.
//
// This function will return a file in a string format ready to be sent
// across the wire.
func StaticFiles(as string, paths ...string) *url {
	return makeurl(as, "Static File", func(req *http.Request) (string, int) {
		filename := req.URL.Path[len(as):]
		for _, path := range paths {
			// Prevent Directory Traversal Attacks
			if len(strings.Split(path, "..")) > 1 {
				return "", http.StatusNotFound
			}
			out_data, err := readFile(filepath.Join(path, filename))
			if err != nil {
				continue
			}
			return out_data, http.StatusOK
		}
		return "", http.StatusNotFound
	}, STATIC, -1)
}

// CacheURL returns a URL which has caching enabled for time.Duration d.
func CacheURL(re, name string, v view, t handlertype, d time.Duration) *url {
	return makeurl(re, name, v, t, d)
}

// Favicon takes a path to some file which you want to be returned when
// a request comes through for ^/favicon.ico$. By default this will cache
// for TIMEOUT * 10.
func Favicon(path string) *url {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	file.Close()

	return makeurl("^/favicon.ico$", "Favicon",
		func(req *http.Request) (string, int) {
			out_data, err := readFile(path)
			if err != nil {
				return "", http.StatusNotFound
			}
			return out_data, http.StatusOK
		}, ICON, -1)
}

// Helper method which reads a file into memory or returns an error
//
// Used in both Favicon and StaticFiles
func readFile(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	// there is only one return but doing it this way means that
	// further additions won't forget to close the fh
	defer file.Close()

	// if we're here, the file exists and we just need to send
	// it to the client.
	b := []string{}
	for {
		reader := make([]byte, FileChunks)
		count, err := file.Read(reader)
		if err != nil {
			return strings.Join(b, ""), nil
		}

		b = append(b, string(reader[:count]))
	}
	panic("Unreachable!")
}

// BasicReplace takes a string and a map[string]string which it uses
// to replace any instances of a key by the value under it.
func BasicReplace(template string, replacement_map map[string]string) string {
	var replacements []string
	for key, val := range replacement_map {
		replacements = append(replacements, key, val)
	}
	replacer := strings.NewReplacer(replacements...)
	return replacer.Replace(template)
}

// Starts the server running on PORT `port` with the timeout duration
func (App *appServer) Run() {
	server := http.Server{
		Addr:        ":" + App.port,
		Handler:     App,
		ReadTimeout: App.timeout * time.Second,
	}
	fmt.Printf("Serving on PORT: %s\n", App.port)
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>GOFMT<commit_after>// Package wedge is a lightweight web framework which intends to
// cut-down on oft written boilerplate code
package wedge

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

const (
	HTML = iota
	JSON
	STATIC
	ICON
)

const (
	FileChunks = 1024
)

var (
	routes  []*url
	TIMEOUT = time.Second
)

type handlertype int

// Handler functions should match this signature
type view func(*http.Request) (string, int)

// appServer is our server instance which holds the ServeHTTP method
// so that it satisfies the http.Server interface.
type appServer struct {
	port       string
	routes     []*url
	timeout    time.Duration
	cache_map  *safeMap
	handler404 view
	stat_map   *safeMap
}

// appServer constructor
func NewAppServer(port string, timeout time.Duration) *appServer {
	return &appServer{
		port:      port,
		routes:    make([]*url, 0),
		timeout:   timeout,
		cache_map: NewSafeMap(),
	}
}

// Attaches more *urls to the Routes slice on the appServer value
func (App *appServer) AddURLs(patterns ...*url) {
	for _, url := range patterns {
		App.routes = append(App.routes, url)
	}
}

// EnableStatTracking does exactly what it says on the tin
//
// EnableStatTracking creates a NewSafeMap under the stat_map field which will
// then be used to increment and aggregate hits to URLs.
//
// This function will append a new *url onto the associated appServer. The url
// which this is under is ^/statistics/?$.
func (App *appServer) EnableStatTracking() {
	App.stat_map = NewSafeMap()

	staturl := makeurl("^/statistics/?$", "Statistics", func(req *http.Request) (string, int) {

		rawdata, ok := App.stat_map.Do(func(m map[interface{}]interface{}) interface{} {
			// we could return m here but that would mean we've broken the
			// reason why we made the map safe in the first place.

			outstr := "
"
			for key, value := range m {
				outstr += fmt.Sprintf("
%s	", key.(string))
				outstr += fmt.Sprintf("%d
", value.(int))
			}
			outstr += "
"
			return outstr
		})

		if !ok {
			return "Failure getting data", 500
		}
		return rawdata.(string), 200

	}, HTML, 0)
	App.routes = append(App.routes, staturl)
}

// incrementStats is a non-blocking method to increment a page counter
// for individual routes.
func (App *appServer) incrementStats(k string) {
	if App.stat_map == nil {
		panic("Cannot increment statistics when it has not been enabled!")
	}

	// create a goroutine which sends a function literal to the async
	// map which tries to increment the value under the k string.
	go App.stat_map.Do(func(m map[interface{}]interface{}) interface{} {
		val, ok := m[k]
		if ok {
			val, ok := val.(int)
			if ok {
				val++
				m[k] = val
			}
		} else {
			m[k] = 1
		}
		return true
	})
}

// Sets the 404 Handler for the appServer to fn.
func (App *appServer) Handler404(fn view) {
	App.handler404 = fn
}

// This is the main 'event loop' for the web server. All requests are
// sent to this handler, which checks the incoming request against
// all the routes we have setup if it finds a match it will invoke
// the handler which is attached to that match.
func (App *appServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	request := req.URL.Path

	for _, route := range App.routes {
		matches := route.match.FindAllStringSubmatch(request, 1)
		if len(matches) > 0 {
			log.Println("Request:", route.name, request)
			if App.stat_map != nil {
				App.incrementStats(route.name)
			}
			resp, status := App.getResponse(route, req)

			if status == 404 {
				App.handle404req(w, req)
				return
			}

			switch route.viewtype {
			case HTML:
				io.WriteString(w, resp)
				return
			case JSON:
				w.Header().Set("Content-type", "application/json")
				json.NewEncoder(w).Encode(map[string]string{
					"message": resp,
				})
				return
			case STATIC:
				if w.Header().Get("Content-Type") == "" {
					reqstr := req.URL.Path[len(route.rawre):]
					ctype := mime.TypeByExtension(filepath.Ext(reqstr))
					w.Header().Set("Content-Type", ctype)
				}
				io.WriteString(w, resp)
				return
			case ICON:
				w.Header().Set("Content-Type", "image/x-icon")
				io.WriteString(w, resp)
				return
			default:
				panic("Unknown handler type!")
			}
		}
	}
	App.handle404req(w, req)
	return
}

// handle404req checks if the 404 handler is a custom one and uses that, if not,
// it uses the built-in NotFound function.
func (App *appServer) handle404req(w http.ResponseWriter, req *http.Request) {
	log.Println("404", req.URL.Path)
	App.incrementStats("404")
	w.WriteHeader(404)

	if App.handler404 != nil {
		resp, _ := App.handler404(req)
		io.WriteString(w, resp)
		return
	} else {
		http.NotFound(w, req)
		return
	}
}

// getResponse checks the *url's cache_duration, if the cache duration
// is zero. Then we never cache the response. Otherwise, we check to
// see if the cache_duration has passed by reading the timeout channel
// if so, we run the URL handler associated with the route and store it's
// new response value. We then store the response in the cache_map and
// return it to the client.
//
// Accessing the cache_map from multiple threads is safe. There are two
// implementations of a safe map included with this library. One is sync'd
// with channels (safeMap) and the other is sync'd with a mutex lock
// (lockMap). We currently use the safeMap.
func (App *appServer) getResponse(route *url, req *http.Request) (string, int) {

	if route.cache_duration == 0 {
		return route.handler(req)
	}

	select {
	case <-route.timeout:
		go func(d time.Duration, ch chan bool) {
			log.Println("Timed out")
			f := time.After(d * TIMEOUT)
			<-f
			go func() {
				ch <- true
			}()
		}(route.cache_duration, route.timeout)
		resp, err := route.handler(req)
		if !App.cache_map.Insert(req.URL.Path, resp) {
			panic("Inserting into cache_map failure!")
		}
		return resp, err
	default:
		resp, ok := App.cache_map.Find(req.URL.Path).(string)
		if !ok {
			resp, _ = route.handler(req)

		}
		if !App.cache_map.Insert(req.URL.Path, resp) {
			panic("Inserting into cache_map failure!")
		}
		return resp, http.StatusOK
	}
	panic("unreachable")
}

// Basic URL struct which holds a match, a name and a handler function
//
// match:
//     Match is a *regexp.Regexp which we will use to check incoming
//     request URLs against and return the handler on any that match
// name:
//     Name is a simple string of what the url should be referred to as
// handler:
//     Handler is a wedge.view function which we will use against any
//     requests that match `match`.
type url struct {
	match          *regexp.Regexp
	name           string
	handler        view
	viewtype       handlertype
	rawre          string
	cache_duration time.Duration
	timeout        chan bool
}

func (u *url) String() string {
	return fmt.Sprintf(
		"{\n  URL: %s\n  Handler: %s\n}", u.match, u.name,
	)
}

// Unexported method which forms as the base method to return *url values
//
// We chose to do it like this because we can have specialized methods
// which have a simply API but fill in certain blanks for this. And the
// makeurl method can have a relatively clunky API since the work will
// be done under the hood.
func makeurl(re, name string, v view, t handlertype, duration time.Duration) *url {
	match := regexp.MustCompile(re)
	timeoutchan := make(chan bool)

	// Initialize the channel and seed with a value
	// so the first request will put the response
	// into memory
	if duration < 0 {
		duration = 30 * 12 * 30 * time.Hour
	}
	if duration > 0 {
		go func() {
			timeoutchan <- true
		}()
	}

	return &url{
		match:          match,
		name:           name,
		handler:        v,
		viewtype:       t,
		rawre:          re,
		cache_duration: duration,
		timeout:        timeoutchan,
	}
}

// URL is a function which returns a *url value.
// re:
//     re is a string which will be compiled to a *regexp.Regexp
//     and will panic if the regular expression cannot be compiled
// name:
//     Name is a simple string of what the url should be referred to as
// handler:
//     Handler is a wedge.view function which we will use against any
//     requests that match `match`.
func URL(re, name string, v view, t handlertype) *url {
	return makeurl(re, name, v, t, 0)
}

// StaticFiles is a not so light wrapper around the URL function
//
// We start off receiving an 'as' string which marks the URL to which
// we match against. We then take a []string which is filepaths to all
// the locations in which an incoming file request should be checked
// against. The file is read in chunks as per the module level constant4
// FileChunk.
//
// This function will return a file in a string format ready to be sent
// across the wire.
func StaticFiles(as string, paths ...string) *url {
	return makeurl(as, "Static File", func(req *http.Request) (string, int) {
		filename := req.URL.Path[len(as):]
		for _, path := range paths {
			// Prevent Directory Traversal Attacks
			if len(strings.Split(path, "..")) > 1 {
				return "", http.StatusNotFound
			}
			out_data, err := readFile(filepath.Join(path, filename))
			if err != nil {
				continue
			}
			return out_data, http.StatusOK
		}
		return "", http.StatusNotFound
	}, STATIC, -1)
}

// CacheURL returns a URL which has caching enabled for time.Duration d.
func CacheURL(re, name string, v view, t handlertype, d time.Duration) *url {
	return makeurl(re, name, v, t, d)
}

// Favicon takes a path to some file which you want to be returned when
// a request comes through for ^/favicon.ico$. By default this will cache
// for TIMEOUT * 10.
func Favicon(path string) *url {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	file.Close()

	return makeurl("^/favicon.ico$", "Favicon",
		func(req *http.Request) (string, int) {
			out_data, err := readFile(path)
			if err != nil {
				return "", http.StatusNotFound
			}
			return out_data, http.StatusOK
		}, ICON, -1)
}

// Helper method which reads a file into memory or returns an error
//
// Used in both Favicon and StaticFiles
func readFile(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	// there is only one return but doing it this way means that
	// further additions won't forget to close the fh
	defer file.Close()

	// if we're here, the file exists and we just need to send
	// it to the client.
	b := []string{}
	for {
		reader := make([]byte, FileChunks)
		count, err := file.Read(reader)
		if err != nil {
			return strings.Join(b, ""), nil
		}

		b = append(b, string(reader[:count]))
	}
	panic("Unreachable!")
}

// BasicReplace takes a string and a map[string]string which it uses
// to replace any instances of a key by the value under it.
func BasicReplace(template string, replacement_map map[string]string) string {
	var replacements []string
	for key, val := range replacement_map {
		replacements = append(replacements, key, val)
	}
	replacer := strings.NewReplacer(replacements...)
	return replacer.Replace(template)
}

// Starts the server running on PORT `port` with the timeout duration
func (App *appServer) Run() {
	server := http.Server{
		Addr:        ":" + App.port,
		Handler:     App,
		ReadTimeout: App.timeout * time.Second,
	}
	fmt.Printf("Serving on PORT: %s\n", App.port)
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package ecs

import (
	"reflect"
	"sort"
)

// World contains a bunch of Entities, and a bunch of Systems. It is the
// recommended way to run ecs.
type World struct {
	systems      systems
	sysIn, sysEx map[reflect.Type][]reflect.Type
}

// AddSystem adds the given System to the World, sorted by priority.
func (w *World) AddSystem(system System) {
	if initializer, ok := system.(Initializer); ok {
		initializer.New(w)
	}

	w.systems = append(w.systems, system)
	sort.Sort(w.systems)
}

// AddSystemInterface adds a system to the world, but also adds a filter that allows
// automatic adding of entities that match the provided in interface, and excludes any
// that match the provided ex interface, even if they also match in. in and ex must be
// pointers to the interface or else this panics.
func (w *World) AddSystemInterface(sys SystemAddByInterfacer, in interface{}, ex interface{}) {
	w.AddSystem(sys)

	if w.sysIn == nil {
		w.sysIn = make(map[reflect.Type][]reflect.Type)
	}

	if reflect.TypeOf(in).AssignableTo(reflect.TypeOf([]interface{}{})) {
		in = []interface{}{in}
	}
	for _, v := range in.([]interface{}) {
		w.sysIn[reflect.TypeOf(sys)] = append(w.sysIn[reflect.TypeOf(sys)], reflect.TypeOf(v).Elem())
	}

	if ex == nil {
		return
	}

	if w.sysEx == nil {
		w.sysEx = make(map[reflect.Type][]reflect.Type)
	}

	if !reflect.TypeOf(ex).AssignableTo(reflect.TypeOf([]interface{}{})) {
		ex = []interface{}{ex}
	}
	for _, v := range ex.([]interface{}) {
		w.sysEx[reflect.TypeOf(sys)] = append(w.sysEx[reflect.TypeOf(sys)], reflect.TypeOf(v).Elem())
	}
}

// AddEntity adds the entity to all systems that have been added via
// AddSystemInterface. If the system was added via AddSystem the entity will not be
// added to it.
func (w *World) AddEntity(e Identifier) {
	if w.sysIn == nil {
		w.sysIn = make(map[reflect.Type][]reflect.Type)
	}
	if w.sysEx == nil {
		w.sysEx = make(map[reflect.Type][]reflect.Type)
	}

	search := func(i Identifier, types []reflect.Type) bool {
		for _, t := range types {
			if reflect.TypeOf(i).Implements(t) {
				return true
			}
		}
		return false
	}
	for _, system := range w.systems {
		sys, ok := system.(SystemAddByInterfacer)
		if !ok {
			continue
		}

		if ex, not := w.sysEx[reflect.TypeOf(sys)]; not {
			if search(e, ex) {
				continue
			}
		}
		if in, ok := w.sysIn[reflect.TypeOf(sys)]; ok {
			if search(e, in) {
				sys.AddByInterface(e)
				continue
			}
		}
	}
}

// Systems returns the list of Systems managed by the World.
func (w *World) Systems() []System {
	return w.systems
}

// Update updates each System managed by the World. It is invoked by the engine
// once every frame, with dt being the duration since the previous update.
func (w *World) Update(dt float32) {
	for _, system := range w.Systems() {
		system.Update(dt)
	}
}

// RemoveEntity removes the entity across all systems.
func (w *World) RemoveEntity(e BasicEntity) {
	for _, sys := range w.systems {
		sys.Remove(e)
	}
}
<commit_msg>fix(world): added a `!` to a boolean expression.<commit_after>package ecs

import (
	"reflect"
	"sort"
)

// World contains a bunch of Entities, and a bunch of Systems. It is the
// recommended way to run ecs.
type World struct {
	systems      systems
	sysIn, sysEx map[reflect.Type][]reflect.Type
}

// AddSystem adds the given System to the World, sorted by priority.
func (w *World) AddSystem(system System) {
	if initializer, ok := system.(Initializer); ok {
		initializer.New(w)
	}

	w.systems = append(w.systems, system)
	sort.Sort(w.systems)
}

// AddSystemInterface adds a system to the world, but also adds a filter that allows
// automatic adding of entities that match the provided in interface, and excludes any
// that match the provided ex interface, even if they also match in. in and ex must be
// pointers to the interface or else this panics.
func (w *World) AddSystemInterface(sys SystemAddByInterfacer, in interface{}, ex interface{}) {
	w.AddSystem(sys)

	if w.sysIn == nil {
		w.sysIn = make(map[reflect.Type][]reflect.Type)
	}

	if !reflect.TypeOf(in).AssignableTo(reflect.TypeOf([]interface{}{})) {
		in = []interface{}{in}
	}
	for _, v := range in.([]interface{}) {
		w.sysIn[reflect.TypeOf(sys)] = append(w.sysIn[reflect.TypeOf(sys)], reflect.TypeOf(v).Elem())
	}

	if ex == nil {
		return
	}

	if w.sysEx == nil {
		w.sysEx = make(map[reflect.Type][]reflect.Type)
	}

	if !reflect.TypeOf(ex).AssignableTo(reflect.TypeOf([]interface{}{})) {
		ex = []interface{}{ex}
	}
	for _, v := range ex.([]interface{}) {
		w.sysEx[reflect.TypeOf(sys)] = append(w.sysEx[reflect.TypeOf(sys)], reflect.TypeOf(v).Elem())
	}
}

// AddEntity adds the entity to all systems that have been added via
// AddSystemInterface. If the system was added via AddSystem the entity will not be
// added to it.
func (w *World) AddEntity(e Identifier) {
	if w.sysIn == nil {
		w.sysIn = make(map[reflect.Type][]reflect.Type)
	}
	if w.sysEx == nil {
		w.sysEx = make(map[reflect.Type][]reflect.Type)
	}

	search := func(i Identifier, types []reflect.Type) bool {
		for _, t := range types {
			if reflect.TypeOf(i).Implements(t) {
				return true
			}
		}
		return false
	}
	for _, system := range w.systems {
		sys, ok := system.(SystemAddByInterfacer)
		if !ok {
			continue
		}

		if ex, not := w.sysEx[reflect.TypeOf(sys)]; not {
			if search(e, ex) {
				continue
			}
		}
		if in, ok := w.sysIn[reflect.TypeOf(sys)]; ok {
			if search(e, in) {
				sys.AddByInterface(e)
				continue
			}
		}
	}
}

// Systems returns the list of Systems managed by the World.
func (w *World) Systems() []System {
	return w.systems
}

// Update updates each System managed by the World. It is invoked by the engine
// once every frame, with dt being the duration since the previous update.
func (w *World) Update(dt float32) {
	for _, system := range w.Systems() {
		system.Update(dt)
	}
}

// RemoveEntity removes the entity across all systems.
func (w *World) RemoveEntity(e BasicEntity) {
	for _, sys := range w.systems {
		sys.Remove(e)
	}
}
<|endoftext|>


<commit_before>package weavedns

import (
	"github.com/miekg/dns"
	"math"
	"net"
	"time"
)

// Portions of this code taken from github.com/armon/mdns

const (
	ipv4mdns = "224.0.0.251" // link-local multicast address
	mdnsPort = 5353          // mDNS assigned port
	// We wait this long to hear responses from other mDNS servers on the network.
	// TODO: introduce caching so we don't have to wait this long on every call.
	mDNSTimeout = 500 * time.Millisecond
	MaxDuration = time.Duration(math.MaxInt64)
	MailboxSize = 16
)

var (
	ipv4Addr = &net.UDPAddr{
		IP:   net.ParseIP(ipv4mdns),
		Port: mdnsPort,
	}
)

type ResponseA struct {
	Name string
	Addr net.IP
	Err  error
}

type responseInfo struct {
	timeout time.Time // if no answer by this time, give up
	ch      chan<- *ResponseA
}

// Represents one query that we have sent for one name.
// If we, internally, get several requests for the same name while we have
// a query in flight, then we don't want to send more queries out.
// Invariant on responseInfos: they are in non-descending order of timeout.
type inflightQuery struct {
	name          string
	id            uint16 // the DNS message ID
	responseInfos []*responseInfo
}

type MDNSClient struct {
	server    *dns.Server
	conn      *net.UDPConn
	addr      *net.UDPAddr
	inflight  map[string]*inflightQuery
	queryChan chan<- *MDNSInteraction
}

type mDNSQueryInfo struct {
	name       string
	querytype  uint16
	responseCh chan<- *ResponseA
}

func NewMDNSClient() (*MDNSClient, error) {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return &MDNSClient{
		conn:     conn,
		addr:     ipv4Addr,
		inflight: make(map[string]*inflightQuery)}, nil
}

func (c *MDNSClient) Start(ifi *net.Interface) error {
	multicast, err := LinkLocalMulticastListener(ifi)
	if err != nil {
		return err
	}

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		// Don't want to handle queries here, so filter anything out that isn't a response
		if len(r.Answer) > 0 {
			c.ResponseCallback(r)
		}
	}

	c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
	go c.server.ActivateAndServe()

	queryChan := make(chan *MDNSInteraction, MailboxSize)
	c.queryChan = queryChan
	go c.queryLoop(queryChan)

	return nil
}

func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) {
	conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr)
	return conn, err
}

// ACTOR client API

const (
	CSendQuery       = iota
	CShutdown        = iota
	CMessageReceived = iota
)

type MDNSInteraction struct {
	code       int
	resultChan chan<- interface{}
	payload    interface{}
}

// Async
func (c *MDNSClient) Shutdown() {
	c.queryChan <- &MDNSInteraction{code: CShutdown}
}

// Async
func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) {
	c.queryChan <- &MDNSInteraction{
		code:    CSendQuery,
		payload: mDNSQueryInfo{name, querytype, responseCh},
	}
}

// Async - called from dns library multiplexer
func (c *MDNSClient) ResponseCallback(r *dns.Msg) {
	c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r}
}

// ACTOR server

// Check all in-flight queries, close all that have already timed out,
// and return the duration until the next timeout
func (c *MDNSClient) checkInFlightQueries() time.Duration {
	now := time.Now()
	after := MaxDuration
	for name, query := range c.inflight {
		// Invariant on responseInfos: they are in non-descending order of timeout.
		numClosed := 0
		for _, item := range query.responseInfos {
			duration := item.timeout.Sub(now)
			if duration <= 0 { // timed out
				close(item.ch)
				numClosed++
			} else {
				if duration < after {
					after = duration
				}
				break // don't need to look at any more for this query
			}
		}
		// Remove timed-out items from the slice
		query.responseInfos = query.responseInfos[numClosed:]
		if len(query.responseInfos) == 0 {
			delete(c.inflight, name)
		}
	}
	return after
}

func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() {
		timer.Reset(c.checkInFlightQueries())
	}

	terminate := false
	for !terminate {
		select {
		case query, ok := <-queryChan:
			if !ok {
				break
			}
			switch query.code {
			case CShutdown:
				c.server.Shutdown()
				terminate = true
			case CSendQuery:
				c.handleSendQuery(query.payload.(mDNSQueryInfo))
				run()
			case CMessageReceived:
				c.handleResponse(query.payload.(*dns.Msg))
				run()
			}
		case <-timer.C:
			run()
		}
	}

	// Close all response channels at termination
	for _, query := range c.inflight {
		for _, item := range query.responseInfos {
			close(item.ch)
		}
	}
}

func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) {
	query, found := c.inflight[q.name]
	if !found {
		m := new(dns.Msg)
		m.SetQuestion(q.name, q.querytype)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
		query = &inflightQuery{
			name: q.name,
			id:   m.Id,
		}
		_, err = c.conn.WriteTo(buf, c.addr)
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
		c.inflight[q.name] = query
	}
	info := &responseInfo{
		ch:      q.responseCh,
		timeout: time.Now().Add(mDNSTimeout),
	}
	// Invariant on responseInfos: they are in non-descending order of timeout.
	// Since we use a fixed interval from Now(), this must be after all existing timeouts.
	query.responseInfos = append(query.responseInfos, info)
}

func (c *MDNSClient) handleResponse(r *dns.Msg) {
	for _, answer := range r.Answer {
		switch rr := answer.(type) {
		case *dns.A:
			name := rr.Hdr.Name
			if query, found := c.inflight[name]; found {
				for _, resp := range query.responseInfos {
					resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A}
					close(resp.ch)
				}
				delete(c.inflight, name)
			} else {
				// We've received a response that didn't match a query
				// Do we want to cache it?
			}
		}
	}
}
<commit_msg>Clarify comments<commit_after>package weavedns

import (
	"github.com/miekg/dns"
	"math"
	"net"
	"time"
)

// Portions of this code taken from github.com/armon/mdns

const (
	ipv4mdns = "224.0.0.251" // link-local multicast address
	mdnsPort = 5353          // mDNS assigned port
	// We wait this long to hear a response from other mDNS servers on
	// the network.
	mDNSTimeout = 500 * time.Millisecond
	MaxDuration = time.Duration(math.MaxInt64)
	MailboxSize = 16
)

var (
	ipv4Addr = &net.UDPAddr{
		IP:   net.ParseIP(ipv4mdns),
		Port: mdnsPort,
	}
)

type ResponseA struct {
	Name string
	Addr net.IP
	Err  error
}

type responseInfo struct {
	timeout time.Time // if no answer by this time, give up
	ch      chan<- *ResponseA
}

// Represents one query that we have sent for one name.
// If we, internally, get several requests for the same name while we have
// a query in flight, then we don't want to send more queries out.
// Invariant on responseInfos: they are in ascending order of timeout.
type inflightQuery struct {
	name          string
	id            uint16 // the DNS message ID
	responseInfos []*responseInfo
}

type MDNSClient struct {
	server    *dns.Server
	conn      *net.UDPConn
	addr      *net.UDPAddr
	inflight  map[string]*inflightQuery
	queryChan chan<- *MDNSInteraction
}

type mDNSQueryInfo struct {
	name       string
	querytype  uint16
	responseCh chan<- *ResponseA
}

func NewMDNSClient() (*MDNSClient, error) {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return &MDNSClient{
		conn:     conn,
		addr:     ipv4Addr,
		inflight: make(map[string]*inflightQuery)}, nil
}

func (c *MDNSClient) Start(ifi *net.Interface) error {
	multicast, err := LinkLocalMulticastListener(ifi)
	if err != nil {
		return err
	}

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		// Don't want to handle queries here, so filter anything out that isn't a response
		if len(r.Answer) > 0 {
			c.ResponseCallback(r)
		}
	}

	c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
	go c.server.ActivateAndServe()

	queryChan := make(chan *MDNSInteraction, MailboxSize)
	c.queryChan = queryChan
	go c.queryLoop(queryChan)

	return nil
}

func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) {
	conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr)
	return conn, err
}

// ACTOR client API

const (
	CSendQuery       = iota
	CShutdown        = iota
	CMessageReceived = iota
)

type MDNSInteraction struct {
	code       int
	resultChan chan<- interface{}
	payload    interface{}
}

// Async
func (c *MDNSClient) Shutdown() {
	c.queryChan <- &MDNSInteraction{code: CShutdown}
}

// Async
func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) {
	c.queryChan <- &MDNSInteraction{
		code:    CSendQuery,
		payload: mDNSQueryInfo{name, querytype, responseCh},
	}
}

// Async - called from dns library multiplexer
func (c *MDNSClient) ResponseCallback(r *dns.Msg) {
	c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r}
}

// ACTOR server

// Check all in-flight queries, close all that have already timed out,
// and return the duration until the next timeout
func (c *MDNSClient) checkInFlightQueries() time.Duration {
	now := time.Now()
	after := MaxDuration
	for name, query := range c.inflight {
		// Invariant on responseInfos: they are in ascending order of timeout.
		numClosed := 0
		for _, item := range query.responseInfos {
			duration := item.timeout.Sub(now)
			if duration <= 0 { // timed out
				close(item.ch)
				numClosed++
			} else {
				if duration < after {
					after = duration
				}
				break // don't need to look at any more for this query
			}
		}
		// Remove timed-out items from the slice
		query.responseInfos = query.responseInfos[numClosed:]
		if len(query.responseInfos) == 0 {
			delete(c.inflight, name)
		}
	}
	return after
}

func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() {
		timer.Reset(c.checkInFlightQueries())
	}

	terminate := false
	for !terminate {
		select {
		case query, ok := <-queryChan:
			if !ok {
				break
			}
			switch query.code {
			case CShutdown:
				c.server.Shutdown()
				terminate = true
			case CSendQuery:
				c.handleSendQuery(query.payload.(mDNSQueryInfo))
				run()
			case CMessageReceived:
				c.handleResponse(query.payload.(*dns.Msg))
				run()
			}
		case <-timer.C:
			run()
		}
	}

	// Close all response channels at termination
	for _, query := range c.inflight {
		for _, item := range query.responseInfos {
			close(item.ch)
		}
	}
}

func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) {
	query, found := c.inflight[q.name]
	if !found {
		m := new(dns.Msg)
		m.SetQuestion(q.name, q.querytype)
		m.RecursionDesired = false
		buf, err := m.Pack()
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
		query = &inflightQuery{
			name: q.name,
			id:   m.Id,
		}
		_, err = c.conn.WriteTo(buf, c.addr)
		if err != nil {
			q.responseCh <- &ResponseA{Err: err}
			close(q.responseCh)
			return
		}
		c.inflight[q.name] = query
	}
	info := &responseInfo{
		ch:      q.responseCh,
		timeout: time.Now().Add(mDNSTimeout),
	}
	// Invariant on responseInfos: they are in ascending order of timeout.
	// Since we use a fixed interval from Now(), this must be after all existing timeouts.
	query.responseInfos = append(query.responseInfos, info)
}

func (c *MDNSClient) handleResponse(r *dns.Msg) {
	for _, answer := range r.Answer {
		switch rr := answer.(type) {
		case *dns.A:
			name := rr.Hdr.Name
			if query, found := c.inflight[name]; found {
				for _, resp := range query.responseInfos {
					resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A}
					close(resp.ch)
				}
				delete(c.inflight, name)
			} else {
				// We've received a response that didn't match a query
				// Do we want to cache it?
			}
		}
	}
}
<|endoftext|>


<commit_before>package server_test

import (
	"errors"
	"io/ioutil"
	"os"
	"path"
	"time"

	"code.cloudfoundry.org/lager/lagertest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/garden/client"
	"code.cloudfoundry.org/garden/client/connection"
	fakes "code.cloudfoundry.org/garden/gardenfakes"
	"code.cloudfoundry.org/garden/server"
)

var _ = Describe("The Garden server", func() {
	var logger *lagertest.TestLogger
	var tmpdir string

	BeforeEach(func() {
		logger = lagertest.NewTestLogger("test")
	})

	AfterEach(func() {
		if tmpdir != "" {
			os.RemoveAll(tmpdir)
		}
	})

	Context("when passed a socket", func() {
		It("listens on the given socket path and chmods it to 0777", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			apiServer := server.New("unix", socketPath, 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "unix", socketPath)

			stat, err := os.Stat(socketPath)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(int(stat.Mode() & os.ModePerm)).Should(Equal(0777))
		})

		It("listens on the given socket path, recreating it if it's already present", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			socket, err := os.Create(socketPath)
			Ω(err).ShouldNot(HaveOccurred())
			socket.WriteString("oops")
			socket.Close()

			apiServer := server.New("unix", socketPath, 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "unix", socketPath)
		})
	})

	Context("when passed a tcp addr", func() {
		It("listens on the given addr", func() {
			apiServer := server.New("tcp", ":60123", 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "tcp", "127.0.0.1:60123")
		})
	})

	It("destroys containers that have been idle for their grace time", func() {
		var err error
		tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
		Ω(err).ShouldNot(HaveOccurred())

		socketPath := path.Join(tmpdir, "api.sock")

		fakeBackend := new(fakes.FakeBackend)

		doomedContainer := new(fakes.FakeContainer)

		fakeBackend.ContainersReturns([]garden.Container{doomedContainer}, nil)
		fakeBackend.GraceTimeReturns(100 * time.Millisecond)

		apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)

		before := time.Now()

		listenAndServe(apiServer, "unix", socketPath)

		Ω(fakeBackend.DestroyCallCount()).Should(Equal(0))
		Eventually(fakeBackend.DestroyCallCount).Should(Equal(1))

		Ω(time.Since(before)).Should(BeNumerically(">", 100*time.Millisecond))
	})

	Describe("using the deprecated Start() method", func() {
		It("starts the backend", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			fakeBackend := new(fakes.FakeBackend)

			apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)
			// listenAndServe(apiServer, "unix", socketPath)
			Expect(apiServer.Start()).To(Succeed())

			Ω(fakeBackend.StartCallCount()).Should(Equal(1))
		})

		Context("when starting the backend fails", func() {
			disaster := errors.New("oh no!")

			It("fails to start", func() {
				var err error
				tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
				Ω(err).ShouldNot(HaveOccurred())

				socketPath := path.Join(tmpdir, "api.sock")

				fakeBackend := new(fakes.FakeBackend)
				fakeBackend.StartReturns(disaster)

				apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)
				Expect(apiServer.Start()).To(MatchError(disaster))
			})
		})
	})

	Context("when listening on the socket fails", func() {
		It("fails to start", func() {
			tmpfile, err := ioutil.TempFile(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			apiServer := server.New(
				"unix",
				// weird scenario: /foo/X/api.sock with X being a file
				path.Join(tmpfile.Name(), "api.sock"),
				0,
				new(fakes.FakeBackend),
				logger,
			)

			err = apiServer.ListenAndServe()
			Ω(err).Should(HaveOccurred())
		})
	})

	Describe("shutting down", func() {
		var socketPath string

		var serverBackend garden.Backend
		var fakeBackend *fakes.FakeBackend

		var apiServer *server.GardenServer
		var apiClient garden.Client

		BeforeEach(func() {
			var err error
			tmpdir, err = ioutil.TempDir("", "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath = path.Join(tmpdir, "api.sock")
			fakeBackend = new(fakes.FakeBackend)

			serverBackend = fakeBackend

			apiClient = client.New(connection.New("unix", socketPath))
		})

		JustBeforeEach(func() {
			apiServer = server.New("unix", socketPath, 0, serverBackend, logger)
			listenAndServe(apiServer, "unix", socketPath)
		})

		It("stops accepting new connections", func() {
			apiServer.Stop()

			Eventually(apiClient.Ping).ShouldNot(Succeed())
		})

		It("stops the backend", func() {
			apiServer.Stop()

			Ω(fakeBackend.StopCallCount()).Should(Equal(1))
		})

		Context("when a Create request is in-flight", func() {
			var creating chan struct{}
			var finishCreating chan struct{}

			BeforeEach(func() {
				creating = make(chan struct{})
				finishCreating = make(chan struct{})

				fakeBackend.CreateStub = func(garden.ContainerSpec) (garden.Container, error) {
					close(creating)
					<-finishCreating
					return new(fakes.FakeContainer), nil
				}
			})

			It("waits for it to complete and stops accepting requests", func() {
				created := make(chan garden.Container, 1)

				go func() {
					defer GinkgoRecover()

					container, err := apiClient.Create(garden.ContainerSpec{})
					Ω(err).ShouldNot(HaveOccurred())

					created <- container
				}()

				Eventually(creating).Should(BeClosed())

				stopExited := make(chan struct{})
				go func() {
					apiServer.Stop()
					close(stopExited)
				}()

				Consistently(stopExited).ShouldNot(BeClosed())

				close(finishCreating)

				Eventually(stopExited).Should(BeClosed())
				Eventually(created).Should(Receive())

				err := apiClient.Ping()
				Ω(err).Should(HaveOccurred())
			})
		})

		Context("when a Run request is in-flight", func() {
			It("does not wait for the request to complete", func(done Done) {
				fakeContainer := new(fakes.FakeContainer)

				fakeContainer.RunStub = func(spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
					process := new(fakes.FakeProcess)

					process.WaitStub = func() (int, error) {
						time.Sleep(time.Minute)
						return 0, nil
					}

					go func() {
						defer GinkgoRecover()

						_, err := io.Stdout.Write([]byte("msg 1\n"))
						Ω(err).ShouldNot(HaveOccurred())

						time.Sleep(time.Minute)

						_, err = io.Stdout.Write([]byte("msg 2\n"))
						Ω(err).ShouldNot(HaveOccurred())
					}()

					return process, nil
				}

				fakeBackend.CreateReturns(fakeContainer, nil)

				clientContainer, err := apiClient.Create(garden.ContainerSpec{})
				Ω(err).ShouldNot(HaveOccurred())

				fakeBackend.LookupReturns(fakeContainer, nil)

				stdout := gbytes.NewBuffer()

				process, err := clientContainer.Run(garden.ProcessSpec{
					Path: "some-path",
					Args: []string{"arg1", "arg2"},
				}, garden.ProcessIO{
					Stdout: stdout,
				})
				Ω(err).ShouldNot(HaveOccurred())

				Eventually(stdout).Should(gbytes.Say("msg 1\n"))

				apiServer.Stop()

				_, err = process.Wait()
				Ω(err).Should(HaveOccurred())

				close(done)
			})
		})
	})
})
<commit_msg>Checking a fake's initial value makes no sense<commit_after>package server_test

import (
	"errors"
	"io/ioutil"
	"os"
	"path"
	"time"

	"code.cloudfoundry.org/lager/lagertest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/garden/client"
	"code.cloudfoundry.org/garden/client/connection"
	fakes "code.cloudfoundry.org/garden/gardenfakes"
	"code.cloudfoundry.org/garden/server"
)

var _ = Describe("The Garden server", func() {
	var logger *lagertest.TestLogger
	var tmpdir string

	BeforeEach(func() {
		logger = lagertest.NewTestLogger("test")
	})

	AfterEach(func() {
		if tmpdir != "" {
			os.RemoveAll(tmpdir)
		}
	})

	Context("when passed a socket", func() {
		It("listens on the given socket path and chmods it to 0777", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			apiServer := server.New("unix", socketPath, 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "unix", socketPath)

			stat, err := os.Stat(socketPath)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(int(stat.Mode() & os.ModePerm)).Should(Equal(0777))
		})

		It("listens on the given socket path, recreating it if it's already present", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			socket, err := os.Create(socketPath)
			Ω(err).ShouldNot(HaveOccurred())
			socket.WriteString("oops")
			socket.Close()

			apiServer := server.New("unix", socketPath, 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "unix", socketPath)
		})
	})

	Context("when passed a tcp addr", func() {
		It("listens on the given addr", func() {
			apiServer := server.New("tcp", ":60123", 0, new(fakes.FakeBackend), logger)
			listenAndServe(apiServer, "tcp", "127.0.0.1:60123")
		})
	})

	It("destroys containers that have been idle for their grace time", func() {
		var err error
		tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
		Ω(err).ShouldNot(HaveOccurred())

		socketPath := path.Join(tmpdir, "api.sock")

		fakeBackend := new(fakes.FakeBackend)

		doomedContainer := new(fakes.FakeContainer)

		fakeBackend.ContainersReturns([]garden.Container{doomedContainer}, nil)
		fakeBackend.GraceTimeReturns(100 * time.Millisecond)

		apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)

		before := time.Now()

		listenAndServe(apiServer, "unix", socketPath)

		Eventually(fakeBackend.DestroyCallCount).Should(Equal(1))

		Ω(time.Since(before)).Should(BeNumerically(">", 100*time.Millisecond))
	})

	Describe("using the deprecated Start() method", func() {
		It("starts the backend", func() {
			var err error
			tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath := path.Join(tmpdir, "api.sock")

			fakeBackend := new(fakes.FakeBackend)

			apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)
			// listenAndServe(apiServer, "unix", socketPath)
			Expect(apiServer.Start()).To(Succeed())

			Ω(fakeBackend.StartCallCount()).Should(Equal(1))
		})

		Context("when starting the backend fails", func() {
			disaster := errors.New("oh no!")

			It("fails to start", func() {
				var err error
				tmpdir, err = ioutil.TempDir(os.TempDir(), "api-server-test")
				Ω(err).ShouldNot(HaveOccurred())

				socketPath := path.Join(tmpdir, "api.sock")

				fakeBackend := new(fakes.FakeBackend)
				fakeBackend.StartReturns(disaster)

				apiServer := server.New("unix", socketPath, 0, fakeBackend, logger)
				Expect(apiServer.Start()).To(MatchError(disaster))
			})
		})
	})

	Context("when listening on the socket fails", func() {
		It("fails to start", func() {
			tmpfile, err := ioutil.TempFile(os.TempDir(), "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			apiServer := server.New(
				"unix",
				// weird scenario: /foo/X/api.sock with X being a file
				path.Join(tmpfile.Name(), "api.sock"),
				0,
				new(fakes.FakeBackend),
				logger,
			)

			err = apiServer.ListenAndServe()
			Ω(err).Should(HaveOccurred())
		})
	})

	Describe("shutting down", func() {
		var socketPath string

		var serverBackend garden.Backend
		var fakeBackend *fakes.FakeBackend

		var apiServer *server.GardenServer
		var apiClient garden.Client

		BeforeEach(func() {
			var err error
			tmpdir, err = ioutil.TempDir("", "api-server-test")
			Ω(err).ShouldNot(HaveOccurred())

			socketPath = path.Join(tmpdir, "api.sock")
			fakeBackend = new(fakes.FakeBackend)

			serverBackend = fakeBackend

			apiClient = client.New(connection.New("unix", socketPath))
		})

		JustBeforeEach(func() {
			apiServer = server.New("unix", socketPath, 0, serverBackend, logger)
			listenAndServe(apiServer, "unix", socketPath)
		})

		It("stops accepting new connections", func() {
			apiServer.Stop()

			Eventually(apiClient.Ping).ShouldNot(Succeed())
		})

		It("stops the backend", func() {
			apiServer.Stop()

			Ω(fakeBackend.StopCallCount()).Should(Equal(1))
		})

		Context("when a Create request is in-flight", func() {
			var creating chan struct{}
			var finishCreating chan struct{}

			BeforeEach(func() {
				creating = make(chan struct{})
				finishCreating = make(chan struct{})

				fakeBackend.CreateStub = func(garden.ContainerSpec) (garden.Container, error) {
					close(creating)
					<-finishCreating
					return new(fakes.FakeContainer), nil
				}
			})

			It("waits for it to complete and stops accepting requests", func() {
				created := make(chan garden.Container, 1)

				go func() {
					defer GinkgoRecover()

					container, err := apiClient.Create(garden.ContainerSpec{})
					Ω(err).ShouldNot(HaveOccurred())

					created <- container
				}()

				Eventually(creating).Should(BeClosed())

				stopExited := make(chan struct{})
				go func() {
					apiServer.Stop()
					close(stopExited)
				}()

				Consistently(stopExited).ShouldNot(BeClosed())

				close(finishCreating)

				Eventually(stopExited).Should(BeClosed())
				Eventually(created).Should(Receive())

				err := apiClient.Ping()
				Ω(err).Should(HaveOccurred())
			})
		})

		Context("when a Run request is in-flight", func() {
			It("does not wait for the request to complete", func(done Done) {
				fakeContainer := new(fakes.FakeContainer)

				fakeContainer.RunStub = func(spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
					process := new(fakes.FakeProcess)

					process.WaitStub = func() (int, error) {
						time.Sleep(time.Minute)
						return 0, nil
					}

					go func() {
						defer GinkgoRecover()

						_, err := io.Stdout.Write([]byte("msg 1\n"))
						Ω(err).ShouldNot(HaveOccurred())

						time.Sleep(time.Minute)

						_, err = io.Stdout.Write([]byte("msg 2\n"))
						Ω(err).ShouldNot(HaveOccurred())
					}()

					return process, nil
				}

				fakeBackend.CreateReturns(fakeContainer, nil)

				clientContainer, err := apiClient.Create(garden.ContainerSpec{})
				Ω(err).ShouldNot(HaveOccurred())

				fakeBackend.LookupReturns(fakeContainer, nil)

				stdout := gbytes.NewBuffer()

				process, err := clientContainer.Run(garden.ProcessSpec{
					Path: "some-path",
					Args: []string{"arg1", "arg2"},
				}, garden.ProcessIO{
					Stdout: stdout,
				})
				Ω(err).ShouldNot(HaveOccurred())

				Eventually(stdout).Should(gbytes.Say("msg 1\n"))

				apiServer.Stop()

				_, err = process.Wait()
				Ω(err).Should(HaveOccurred())

				close(done)
			})
		})
	})
})
<|endoftext|>


<commit_before>// +build darwin

package xhyve

// #cgo CFLAGS: -I${SRCDIR}/include -x c -std=c11 -fno-common -arch x86_64 -DXHYVE_CONFIG_ASSERT -DVERSION=v0.2.0 -Os -fstrict-aliasing -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
// #cgo LDFLAGS: -L${SRCDIR} -arch x86_64 -framework Hypervisor -framework vmnet
// #include <xhyve/xhyve.h>
// #include <xhyve/mevent.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"os"
	"runtime"
	"syscall"
	"unsafe"
)

var termios syscall.Termios

// getTermios gets the current settings for the terminal.
func getTermios() syscall.Termios {
	var state syscall.Termios
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0); err != 0 {
		fmt.Fprintln(os.Stderr, err)
	}
	return state
}

// setTermios restores terminal settings.
func setTermios(state syscall.Termios) {
	syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0)
}

// go_callback_exit gets invoked from within xhyve.c whenever a trap
// suspending the VM is triggered. This is so we can clean up resources
// in Go land, restore terminal settings and allow the goroutine to be scheduled
// on multiple OS threads again by Go's scheduler.
//export go_callback_exit
func go_callback_exit(status C.int) {
	// Restores stty settings to the values that existed before running xhyve.
	setTermios(termios)

	fmt.Printf("Exiting with status code %d\n", status)
	fmt.Printf("Releasing allocated memory from Go land... ")
	for _, arg := range argv {
		C.free(unsafe.Pointer(arg))
	}
	fmt.Println("done")

	// Turns exit flag On for mevent busy loop so that the next time kevent
	// receives an event, mevent handles it and exits the loop.
	C.exit_mevent_dispatch_loop = true

	// Forces kevent() to exit by using the self-pipe trick.
	C.mevent_exit()

	// Allows Go's scheduler to move the goroutine to a different OS thread.
	runtime.UnlockOSThread()
}

// go_set_pty_name is called by xhyve whenever a master/slave pseudo-terminal is setup in
// COM1 or COM2.
//export go_set_pty_name
func go_set_pty_name(name *C.char) {
	if newPty == nil {
		return
	}
	newPty <- C.GoString(name)
}

func init() {
	// Saves stty settings
	termios = getTermios()

	// We need to stick the goroutine to its current OS thread so Go's scheduler
	// does not move it to a different thread while xhyve is running. By doing this
	// we make sure that once go_callback_exit is invoked from C land, it in turn
	// invokes C funtions from the same OS thread and thread context.
	runtime.LockOSThread()
}

// newPty is a channel to send through the devices path names
// for new devices created when a LPC device is added with the
// option: autopty. Example: -l com1,autopty
// If you add a LPC device on COM2 with autopty enabled, you might need to
// make sure the guest OS runs getty on the pseudo-terminal device created, so
// a login prompt is shown once you open such pseudo-terminal.
var newPty chan string

var argv []*C.char

// Run runs xhyve hypervisor.
func Run(params []string, newPtyCh chan string) error {
	newPty = newPtyCh

	argc := C.int(len(params))
	argv = make([]*C.char, argc)
	for i, arg := range params {
		argv[i] = C.CString(arg)
	}

	// Runs xhyve and blocks.
	if err := C.run_xhyve(argc, &argv[0]); err != 0 {
		fmt.Printf("ERROR => %s\n", C.GoString(C.strerror(err)))
		return fmt.Errorf("Error initializing hypervisor")
	}

	return nil
}
<commit_msg>Adds some additional logging when exiting the VM<commit_after>// +build darwin

package xhyve

// #cgo CFLAGS: -I${SRCDIR}/include -x c -std=c11 -fno-common -arch x86_64 -DXHYVE_CONFIG_ASSERT -DVERSION=v0.2.0 -Os -fstrict-aliasing -Wno-unknown-warning-option -Wno-reserved-id-macro -pedantic -fmessage-length=152 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0
// #cgo LDFLAGS: -L${SRCDIR} -arch x86_64 -framework Hypervisor -framework vmnet
// #include <xhyve/xhyve.h>
// #include <xhyve/mevent.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"os"
	"runtime"
	"syscall"
	"unsafe"
)

var termios syscall.Termios

// getTermios gets the current settings for the terminal.
func getTermios() syscall.Termios {
	var state syscall.Termios
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0); err != 0 {
		fmt.Fprintln(os.Stderr, err)
	}
	return state
}

// setTermios restores terminal settings.
func setTermios(state syscall.Termios) {
	syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(&state)), 0, 0, 0)
}

// go_callback_exit gets invoked from within xhyve.c whenever a trap
// suspending the VM is triggered. This is so we can clean up resources
// in Go land, restore terminal settings and allow the goroutine to be scheduled
// on multiple OS threads again by Go's scheduler.
//export go_callback_exit
func go_callback_exit(status C.int) {
	exitStatus := map[int]string{
		0:   "Reset",
		1:   "PowerOFF",
		2:   "Halt",
		3:   "TripleFault",
		100: "Internal error",
	}

	// Restores stty settings to the values that existed before running xhyve.
	setTermios(termios)

	fmt.Printf("VM has been suspended by %s event\n", exitStatus[int(status)])
	fmt.Printf("Releasing allocated memory from Go land... ")
	for _, arg := range argv {
		C.free(unsafe.Pointer(arg))
	}
	fmt.Println("done")

	// Turns exit flag On for mevent busy loop so that the next time kevent
	// receives an event, mevent handles it and exits the loop.
	fmt.Print("Signaling xhyve mevent dispatch loop to exit... ")
	C.exit_mevent_dispatch_loop = true

	// Forces kevent() to exit by using the self-pipe trick.
	C.mevent_exit()
	fmt.Println("done")

	// Allows Go's scheduler to move the goroutine to a different OS thread.
	runtime.UnlockOSThread()
}

// go_set_pty_name is called by xhyve whenever a master/slave pseudo-terminal is setup in
// COM1 or COM2.
//export go_set_pty_name
func go_set_pty_name(name *C.char) {
	if newPty == nil {
		return
	}
	newPty <- C.GoString(name)
}

func init() {
	// Saves stty settings
	termios = getTermios()

	// We need to stick the goroutine to its current OS thread so Go's scheduler
	// does not move it to a different thread while xhyve is running. By doing this
	// we make sure that once go_callback_exit is invoked from C land, it in turn
	// invokes C funtions from the same OS thread and thread context.
	runtime.LockOSThread()
}

// newPty is a channel to send through the devices path names
// for new devices created when a LPC device is added with the
// option: autopty. Example: -l com1,autopty
// If you add a LPC device on COM2 with autopty enabled, you might need to
// make sure the guest OS runs getty on the pseudo-terminal device created, so
// a login prompt is shown once you open such pseudo-terminal.
var newPty chan string

var argv []*C.char

// Run runs xhyve hypervisor.
func Run(params []string, newPtyCh chan string) error {
	newPty = newPtyCh

	argc := C.int(len(params))
	argv = make([]*C.char, argc)
	for i, arg := range params {
		argv[i] = C.CString(arg)
	}

	// Runs xhyve and blocks.
	if err := C.run_xhyve(argc, &argv[0]); err != 0 {
		fmt.Printf("ERROR => %s\n", C.GoString(C.strerror(err)))
		return fmt.Errorf("Error initializing hypervisor")
	}

	return nil
}
<|endoftext|>