<commit_before>package urknall

import (
	"bytes"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/megamsys/urknall/pubsub"
	"github.com/megamsys/urknall/target"
)

// A shortcut creating and running a build from the given target and template.
func Run(target Target, tpl Template) (e error) {
	return (&Build{Target: target, Template: tpl}).Run()
}

// A shortcut creating and runnign a build from the given target and template
// with the DryRun flag set to true. This is quite helpful to actually see
// which commands would be exeucted in the current setting, without actually
// doing anything.
func DryRun(target Target, tpl Template) (e error) {
	return (&Build{Target: target, Template: tpl}).DryRun()
}

// A build is the glue between a target and template.
type Build struct {
	Target            // Where to run the build.
	Template          // What to actually build.
	Env      []string // Environment variables in the form `KEY=VALUE`.
}

// This will render the build's template into a package and run all its tasks.
func (b *Build) Run() error {
	pkg, e := b.prepareBuild()
	if e != nil {
		return e
	}
	m := message(pubsub.MessageTasksProvision, b.hostname(), "")
	m.Publish("started")
	for _, task := range pkg.tasks {
		if e = b.buildTask(task); e != nil {
			m.PublishError(e)
			return e
		}
	}
	m.Publish("finished")
	return nil
}

func (b *Build) DryRun() error {
	pkg, e := b.prepareBuild()
	if e != nil {
		return e
	}

	for _, task := range pkg.tasks {
		for _, command := range task.commands {
			m := message(pubsub.MessageTasksProvisionTask, b.hostname(), task.name)
			m.TaskChecksum = command.Checksum()
			m.Message = command.LogMsg()

			switch {
			case command.cached:
				m.ExecStatus = pubsub.StatusCached
				m.Publish("finished")
			default:
				m.ExecStatus = pubsub.StatusExecStart
				m.Publish("executed")
			}
		}
	}
	return nil
}

func (build *Build) prepareBuild() (*packageImpl, error) {
	pkg, e := renderTemplate(build.Template)
	if e != nil {
		return nil, e
	}

	if e = build.prepareTarget(); e != nil {
		return nil, e
	}

	ct, e := build.buildChecksumTree()
	if e != nil {
		return nil, fmt.Errorf("error building checksum tree: %s", e.Error())
	}

	for _, task := range pkg.tasks {
		if e = build.prepareTask(task, ct); e != nil {
			return nil, e
		}
	}

	return pkg, nil
}

func (build *Build) prepareTarget() error {
	if build.User() == "" {
		return fmt.Errorf("User not set")
	}
	rawCmd := fmt.Sprintf(`{ grep "^%s:" /etc/group | grep %s; } && [ -d %[3]s ] && [ -f %[3]s/.v2 ]`,
		ukGROUP, build.User(), ukCACHEDIR)
	cmd, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return e
	}
	if e := cmd.Run(); e != nil {
		// If user is missing the group, create group (if necessary), add user and restart ssh connection.
		cmds := []string{
			fmt.Sprintf(`{ grep -e '^%[1]s:' /etc/group > /dev/null || { groupadd %[1]s; }; }`, ukGROUP),
			fmt.Sprintf(`{ [ -d %[1]s ] || { mkdir -p -m 2775 %[1]s && chgrp %[2]s %[1]s; }; }`, ukCACHEDIR, ukGROUP),
			fmt.Sprintf("usermod -a -G %s %s", ukGROUP, build.User()),
			fmt.Sprintf(`[ -f %[1]s/.v2 ] || { export DATE=$(date "+%%Y%%m%%d_%%H%%M%%S") && ls %[1]s | while read dir; do ls -t %[1]s/$dir/*.done | tac > %[1]s/$dir/$DATE.run; done && touch %[1]s/.v2;  } `, ukCACHEDIR),
		}

		cmd, e = build.prepareInternalCommand(strings.Join(cmds, " && "))
		if e != nil {
			return e
		}
		out := &bytes.Buffer{}
		err := &bytes.Buffer{}
		cmd.SetStderr(err)
		cmd.SetStdout(out)
		if e := cmd.Run(); e != nil {
			return fmt.Errorf("failed to initiate user %q for provisioning: %s, out=%q err=%q", build.User(), e, out.String(), err.String())
		}
		return build.Reset()
	}
	return nil
}

func (build *Build) prepareTask(tsk *task, ct checksumTree) (e error) {
	cacheKey := tsk.name
	if cacheKey == "" {
		return fmt.Errorf("CacheKey must not be empty")
	}
	checksumDir := fmt.Sprintf(ukCACHEDIR+"/%s", tsk.name)

	var found bool
	var checksumList []string

	if checksumList, found = ct[cacheKey]; !found {
		// Create checksum dir and set group bit (all new files will inherit the directory's group). This allows for
		// different users (being part of that group) to create, modify and delete the contained checksum and log files.
		createChecksumDirCmd := fmt.Sprintf("mkdir -m2775 -p %s", checksumDir)

		cmd, e := build.prepareInternalCommand(createChecksumDirCmd)
		if e != nil {
			return e
		}
		err := &bytes.Buffer{}

		cmd.SetStderr(err)

		if e := cmd.Run(); e != nil {
			return fmt.Errorf(err.String() + ": " + e.Error())
		}
	}

	// find commands that need not be executed
	for i, cmd := range tsk.commands {
		checksum, e := commandChecksum(cmd.command)
		if e != nil {
			return e
		}

		switch {
		case len(checksumList) <= i || checksum != checksumList[i]:
			return nil
		default:
			cmd.cached = true
		}
	}

	return nil
}

func (build *Build) buildTask(tsk *task) (e error) {
	checksumDir := fmt.Sprintf(ukCACHEDIR+"/%s", tsk.name)

	tsk.started = time.Now()

	for _, cmd := range tsk.commands {
		checksum := cmd.Checksum()

		m := message(pubsub.MessageTasksProvisionTask, build.hostname(), tsk.name)
		m.TaskChecksum = checksum
		m.Message = cmd.LogMsg()

		var cmdErr error

		switch {
		case cmd.cached:
			m.ExecStatus = pubsub.StatusCached
		default:
			m.ExecStatus = pubsub.StatusExecStart
			m.Publish("started")

			r := &commandRunner{
				build:       build,
				command:     cmd.command,
				dir:         checksumDir,
				taskName:    tsk.name,
			}
			cmdErr = r.run()

			m.Error = cmdErr
			m.ExecStatus = pubsub.StatusExecFinished
		}
		m.Publish("finished")

		err := build.addCmdToTaskLog(tsk, checksumDir, checksum, cmdErr)
		switch {
		case cmdErr != nil:
			return cmdErr
		case err != nil:
			return err
		}
	}

	return nil
}

// addCmdToTaskLog will manage the log of run commands in a file. This file gets append the path to a file
// for each command, that contains the executed script. The filename contains either ".done" or ".failed" as
// suffix, depending on the err given (nil or not).
func (build *Build) addCmdToTaskLog(tsk *task, checksumDir, checksum string, err error) (e error) {
	prefix := checksumDir + "/" + checksum
	sourceFile := prefix + ".sh"
	targetFile := prefix + ".done"
	if err != nil {
		logError(err)
		targetFile = prefix + ".failed"
	}
	rawCmd := fmt.Sprintf("{ [ -f %[1]s ] || mv %[2]s %[1]s; } && echo %[1]s >> %[3]s/%[4]s.run",
		targetFile, sourceFile, checksumDir, tsk.started.Format("20060102_150405"))
	c, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return e
	}

	return c.Run()
}

type checksumTree map[string][]string

func (build *Build) buildChecksumTree() (ct checksumTree, e error) {
	ct = checksumTree{}

	rawCmd := fmt.Sprintf(
		`[ -d %[1]s ] && { ls %[1]s | while read dir; do ls -t %[1]s/$dir/*.run | head -n1 | xargs cat; done; }`,
		ukCACHEDIR)
	cmd, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return nil, e
	}
	out := &bytes.Buffer{}
	err := &bytes.Buffer{}

	cmd.SetStdout(out)
	cmd.SetStderr(err)

	if e := cmd.Run(); e != nil {
		return nil, fmt.Errorf("%s: out=%s err=%s", e.Error(), out.String(), err.String())
	}

	for _, line := range strings.Split(out.String(), "\n") {
		line = strings.TrimSpace(line)

		if line == "" || !strings.HasSuffix(line, ".done") {
			continue
		}

		pkgname := filepath.Dir(strings.TrimPrefix(line, ukCACHEDIR+"/"))
		checksum := strings.TrimSuffix(filepath.Base(line), ".done")
		if len(checksum) != 64 {
			return nil, fmt.Errorf("invalid checksum %q found for package %q", checksum, pkgname)
		}
		ct[pkgname] = append(ct[pkgname], checksum)
	}

	return ct, nil
}

func (build *Build) prepareCommand(rawCmd string) (target.ExecCommand, error) {
	var sudo string
	if build.User() != "root" {
		sudo = "sudo "
	}
	return build.Command(sudo + rawCmd)
}

func (build *Build) prepareInternalCommand(rawCmd string) (target.ExecCommand, error) {
	rawCmd = fmt.Sprintf("sh -x -e <<\"EOC\"\n%s\nEOC\n", rawCmd)
	return build.prepareCommand(rawCmd)
}

func (build *Build) hostname() string {
	if s, ok := build.Target.(fmt.Stringer); ok {
		return s.String()
	}
	return "MISSING"
}
<commit_msg>status event trigger<commit_after>package urknall

import (
	"bytes"
	"fmt"
	"path/filepath"
	"strings"
	"time"
	constants "github.com/megamsys/libgo/utils"
	"github.com/megamsys/libgo/pairs"
	"github.com/megamsys/libgo/events"
	"github.com/megamsys/libgo/events/alerts"
	"github.com/megamsys/urknall/pubsub"
	"github.com/megamsys/urknall/target"
)
const (
	STARTING = "starting"
	COMPLETED = "completed"
)
// A shortcut creating and running a build from the given target and template.
func Run(target Target, tpl Template,inputs []string) (e error) {
	return (&Build{
		Target: target,
		Template: tpl,
		Inputs: inputs}).Run()
}

// A shortcut creating and runnign a build from the given target and template
// with the DryRun flag set to true. This is quite helpful to actually see
// which commands would be exeucted in the current setting, without actually
// doing anything.
func DryRun(target Target, tpl Template) (e error) {
	return (&Build{Target: target, Template: tpl}).DryRun()
}

// A build is the glue between a target and template.
type Build struct {
	Target            // Where to run the build.
	Template          // What to actually build.
	Env      []string // Environment variables in the form `KEY=VALUE`.
	Inputs   []string // Inputs for OBC like email and status
}

// This will render the build's template into a package and run all its tasks.
func (b *Build) Run() error {
	pkg, e := b.prepareBuild()
	if e != nil {
		return e
	}
	m := message(pubsub.MessageTasksProvision, b.hostname(), "")
	m.Publish("started")
	for _, task := range pkg.tasks {
		if e = b.buildTask(task); e != nil {
			m.PublishError(e)
			return e
		}
	}
	m.Publish("finished")
	return nil
}

func (b *Build) DryRun() error {
	pkg, e := b.prepareBuild()
	if e != nil {
		return e
	}

	for _, task := range pkg.tasks {
		for _, command := range task.commands {
			m := message(pubsub.MessageTasksProvisionTask, b.hostname(), task.name)
			m.TaskChecksum = command.Checksum()
			m.Message = command.LogMsg()

			switch {
			case command.cached:
				m.ExecStatus = pubsub.StatusCached
				m.Publish("finished")
			default:
				m.ExecStatus = pubsub.StatusExecStart
				m.Publish("executed")
			}
		}
	}
	return nil
}

func (build *Build) prepareBuild() (*packageImpl, error) {
	pkg, e := renderTemplate(build.Template)
	if e != nil {
		return nil, e
	}

	if e = build.prepareTarget(); e != nil {
		return nil, e
	}

	ct, e := build.buildChecksumTree()
	if e != nil {
		return nil, fmt.Errorf("error building checksum tree: %s", e.Error())
	}

	for _, task := range pkg.tasks {
		if e = build.prepareTask(task, ct); e != nil {
			return nil, e
		}
	}

	return pkg, nil
}

func (build *Build) prepareTarget() error {
	if build.User() == "" {
		return fmt.Errorf("User not set")
	}
	rawCmd := fmt.Sprintf(`{ grep "^%s:" /etc/group | grep %s; } && [ -d %[3]s ] && [ -f %[3]s/.v2 ]`,
		ukGROUP, build.User(), ukCACHEDIR)
	cmd, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return e
	}
	if e := cmd.Run(); e != nil {
		// If user is missing the group, create group (if necessary), add user and restart ssh connection.
		cmds := []string{
			fmt.Sprintf(`{ grep -e '^%[1]s:' /etc/group > /dev/null || { groupadd %[1]s; }; }`, ukGROUP),
			fmt.Sprintf(`{ [ -d %[1]s ] || { mkdir -p -m 2775 %[1]s && chgrp %[2]s %[1]s; }; }`, ukCACHEDIR, ukGROUP),
			fmt.Sprintf("usermod -a -G %s %s", ukGROUP, build.User()),
			fmt.Sprintf(`[ -f %[1]s/.v2 ] || { export DATE=$(date "+%%Y%%m%%d_%%H%%M%%S") && ls %[1]s | while read dir; do ls -t %[1]s/$dir/*.done | tac > %[1]s/$dir/$DATE.run; done && touch %[1]s/.v2;  } `, ukCACHEDIR),
		}

		cmd, e = build.prepareInternalCommand(strings.Join(cmds, " && "))
		if e != nil {
			return e
		}
		out := &bytes.Buffer{}
		err := &bytes.Buffer{}
		cmd.SetStderr(err)
		cmd.SetStdout(out)
		if e := cmd.Run(); e != nil {
			return fmt.Errorf("failed to initiate user %q for provisioning: %s, out=%q err=%q", build.User(), e, out.String(), err.String())
		}
		return build.Reset()
	}
	return nil
}

func (build *Build) prepareTask(tsk *task, ct checksumTree) (e error) {
	cacheKey := tsk.name
	if cacheKey == "" {
		return fmt.Errorf("CacheKey must not be empty")
	}
	checksumDir := fmt.Sprintf(ukCACHEDIR+"/%s", tsk.name)

	var found bool
	var checksumList []string

	if checksumList, found = ct[cacheKey]; !found {
		// Create checksum dir and set group bit (all new files will inherit the directory's group). This allows for
		// different users (being part of that group) to create, modify and delete the contained checksum and log files.
		createChecksumDirCmd := fmt.Sprintf("mkdir -m2775 -p %s", checksumDir)

		cmd, e := build.prepareInternalCommand(createChecksumDirCmd)
		if e != nil {
			return e
		}
		err := &bytes.Buffer{}

		cmd.SetStderr(err)

		if e := cmd.Run(); e != nil {
			return fmt.Errorf(err.String() + ": " + e.Error())
		}
	}

	// find commands that need not be executed
	for i, cmd := range tsk.commands {
		checksum, e := commandChecksum(cmd.command)
		if e != nil {
			return e
		}

		switch {
		case len(checksumList) <= i || checksum != checksumList[i]:
			return nil
		default:
			cmd.cached = true
		}
	}

	return nil
}

func (build *Build) buildTask(tsk *task) (e error) {
	var status constants.Status
	checksumDir := fmt.Sprintf(ukCACHEDIR+"/%s", tsk.name)
	templateName := strings.Split(tsk.name, ".")[1]
  status = constants.Status(strings.Join([]string{templateName,STARTING},"."))
	_ = eventNotify(status,build.Inputs,build.hostname())
	tsk.started = time.Now()

	for _, cmd := range tsk.commands {
		checksum := cmd.Checksum()

		m := message(pubsub.MessageTasksProvisionTask, build.hostname(), tsk.name)
		m.TaskChecksum = checksum
		m.Message = cmd.LogMsg()

		var cmdErr error

		switch {
		case cmd.cached:
			m.ExecStatus = pubsub.StatusCached
		default:
			m.ExecStatus = pubsub.StatusExecStart
			m.Publish("started")
			status := constants.Status(strings.Join([]string{tsk.name ,STARTING},"."))
			_ = eventNotify(status, build.Inputs,build.hostname())
			r := &commandRunner{
				build:       build,
				command:     cmd.command,
				dir:         checksumDir,
				taskName:    tsk.name,
			}
			cmdErr = r.run()

			m.Error = cmdErr
			m.ExecStatus = pubsub.StatusExecFinished
		}
		m.Publish("finished")
    status = constants.Status(strings.Join([]string{tsk.name,COMPLETED},"."))
		_ = eventNotify(status, build.Inputs,build.hostname())
		err := build.addCmdToTaskLog(tsk, checksumDir, checksum, cmdErr)
		switch {
		case cmdErr != nil:
			return cmdErr
		case err != nil:
			return err
		}
	}
  status = constants.Status(strings.Join([]string{templateName,COMPLETED},"."))
  _ = eventNotify(status,build.Inputs,build.hostname())
	return nil
}

func eventNotify(status constants.Status,inputs []string,host string) error {
	var email string
	for _,v := range inputs {
			input := strings.Split(v,"=")
		switch input[0] {
		case constants.USERMAIL:
			email = input[1]
		}
		}
	mi := make(map[string]string)
	js := make(pairs.JsonPairs, 0)
	m := make(map[string][]string, 2)
	m["status"] = []string{status.String()}
	m["description"] = []string{status.Description(host)}
	js.NukeAndSet(m) //just nuke the matching output key:

	mi[constants.HOST_IP] = host
	mi[constants.ACCOUNT_ID] = email
	mi[constants.EVENT_TYPE] = status.Event_type()
	newEvent := events.NewMulti(
		[]*events.Event{
			&events.Event{
				AccountsId:  email,
				EventAction: alerts.STATUS,
				EventType:   constants.EventUser,
				EventData:   alerts.EventData{M: mi, D: js.ToString()},
				Timestamp:   time.Now().Local(),
			},
		})
	return newEvent.Write()
}

// addCmdToTaskLog will manage the log of run commands in a file. This file gets append the path to a file
// for each command, that contains the executed script. The filename contains either ".done" or ".failed" as
// suffix, depending on the err given (nil or not).
func (build *Build) addCmdToTaskLog(tsk *task, checksumDir, checksum string, err error) (e error) {
	prefix := checksumDir + "/" + checksum
	sourceFile := prefix + ".sh"
	targetFile := prefix + ".done"
	if err != nil {
		logError(err)
		targetFile = prefix + ".failed"
	}
	rawCmd := fmt.Sprintf("{ [ -f %[1]s ] || mv %[2]s %[1]s; } && echo %[1]s >> %[3]s/%[4]s.run",
		targetFile, sourceFile, checksumDir, tsk.started.Format("20060102_150405"))
	c, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return e
	}

	return c.Run()
}

type checksumTree map[string][]string

func (build *Build) buildChecksumTree() (ct checksumTree, e error) {
	ct = checksumTree{}

	rawCmd := fmt.Sprintf(
		`[ -d %[1]s ] && { ls %[1]s | while read dir; do ls -t %[1]s/$dir/*.run | head -n1 | xargs cat; done; }`,
		ukCACHEDIR)
	cmd, e := build.prepareInternalCommand(rawCmd)
	if e != nil {
		return nil, e
	}
	out := &bytes.Buffer{}
	err := &bytes.Buffer{}

	cmd.SetStdout(out)
	cmd.SetStderr(err)

	if e := cmd.Run(); e != nil {
		return nil, fmt.Errorf("%s: out=%s err=%s", e.Error(), out.String(), err.String())
	}

	for _, line := range strings.Split(out.String(), "\n") {
		line = strings.TrimSpace(line)

		if line == "" || !strings.HasSuffix(line, ".done") {
			continue
		}

		pkgname := filepath.Dir(strings.TrimPrefix(line, ukCACHEDIR+"/"))
		checksum := strings.TrimSuffix(filepath.Base(line), ".done")
		if len(checksum) != 64 {
			return nil, fmt.Errorf("invalid checksum %q found for package %q", checksum, pkgname)
		}
		ct[pkgname] = append(ct[pkgname], checksum)
	}

	return ct, nil
}

func (build *Build) prepareCommand(rawCmd string) (target.ExecCommand, error) {
	var sudo string
	if build.User() != "root" {
		sudo = "sudo "
	}
	return build.Command(sudo + rawCmd)
}

func (build *Build) prepareInternalCommand(rawCmd string) (target.ExecCommand, error) {
	rawCmd = fmt.Sprintf("sh -x -e <<\"EOC\"\n%s\nEOC\n", rawCmd)
	return build.prepareCommand(rawCmd)
}

func (build *Build) hostname() string {
	if s, ok := build.Target.(fmt.Stringer); ok {
		return s.String()
	}
	return "MISSING"
}
<|endoftext|>


<commit_before>package interceptor

import (
	"encoding/json"
	"github.com/datawire/teleproxy/internal/pkg/nat"
	rt "github.com/datawire/teleproxy/internal/pkg/route"
	"log"
	"net"
	"strings"
	"sync"
)

type Interceptor struct {
	work       chan func()
	done       chan empty

	translator *nat.Translator
	tables     map[string]rt.Table
	tablesLock sync.RWMutex

	domains     map[string]rt.Route
	domainsLock sync.RWMutex

	search     []string
	searchLock sync.RWMutex
}

type empty struct{}

func NewInterceptor(name string) *Interceptor {
	ret := &Interceptor{
		tables:     make(map[string]rt.Table),
		translator: nat.NewTranslator(name),
		work:       make(chan func()),
		done:       make(chan empty),
		domains:    make(map[string]rt.Route),
		search:     []string{""},
	}
	ret.tablesLock.Lock() // leave it locked until .Start() unlocks it
	return ret
}

func (i *Interceptor) Start() {
	go func() {
		defer close(i.done)
		i.translator.Enable()
		i.tablesLock.Unlock()
		defer func() {
			i.tablesLock.Lock()
			i.translator.Disable()
		}()
		for {
			action, ok := <-i.work
			if ok {
				action()
			} else {
				return
			}
		}
	}()
}

func (i *Interceptor) Stop() {
	close(i.work)
	<-i.done
}

// Resolve looks up the given query in the (FIXME: somewhere), trying
// all the suffixes in the search path, and returns a Route on success
// or nil on failure. This implementation does not count the number of
// dots in the query.
func (i *Interceptor) Resolve(query string) *rt.Route {
	if !strings.HasSuffix(query, ".") {
		query += "."
	}

	i.searchLock.RLock()
	defer i.searchLock.RUnlock()
	i.domainsLock.RLock()
	defer i.domainsLock.RUnlock()

	for _, suffix := range i.search {
		name := query + suffix
		value, ok := i.domains[strings.ToLower(name)]
		if ok {
			return &value
		}
	}
	return nil
}

func (i *Interceptor) Destination(conn *net.TCPConn) (string, error) {
	_, host, err := i.translator.GetOriginalDst(conn)
	return host, err
}

func (i *Interceptor) Render(table string) string {
	if true {
		var obj interface{}

		if table == "" {
			var tables []rt.Table
			i.tablesLock.RLock()
			for _, t := range i.tables {
				tables = append(tables, t)
			}
			i.tablesLock.RUnlock()
			obj = tables
		} else {
			var ok bool
			i.tablesLock.RLock()
			obj, ok = i.tables[table]
			i.tablesLock.RUnlock()
			if !ok {
				return ""
			}
		}

		bytes, err := json.MarshalIndent(obj, "", "  ")
		if err != nil {
			return err.Error()
		} else {
			return string(bytes)
		}
	}
	panic("not reached")
}

func (i *Interceptor) Delete(table string) bool {
	i.tablesLock.Lock()
	defer i.tablesLock.Unlock()
	i.domainsLock.Lock()
	defer i.domainsLock.Unlock()

	if true {
		var names []string
		if table == "" {
			for name := range i.tables {
				names = append(names, name)
			}
		} else if _, ok := i.tables[table]; ok {
			names = []string{table}
		} else {
			return false
		}

		for _, name := range names {
			if name != "bootstrap" {
				i.update(rt.Table{Name: name})
			}
		}

		return true
	}
	panic("not reached")
}

func (i *Interceptor) Update(table rt.Table) {
	i.tablesLock.Lock()
	defer i.tablesLock.Unlock()
	i.domainsLock.Lock()
	defer i.domainsLock.Unlock()

	i.update(table)
}

// .update() assumes that both .tablesLock and .domainsLock are held
// for writing.  Ensuring that is the case is the caller's
// responsibility.
func (i *Interceptor) update(table rt.Table) {
	oldTable, ok := i.tables[table.Name]

	oldRoutes := make(map[string]rt.Route)
	if ok {
		for _, route := range oldTable.Routes {
			oldRoutes[route.Name] = route
		}
	}

	for _, newRoute := range table.Routes {
		oldRoute, oldRouteOk := oldRoutes[newRoute.Name]
		// A nil Route (when oldRouteOk != true) will compare
		// inequal to any valid new Route.
		if newRoute != oldRoute {
			// delete the old version
			if oldRouteOk {
				switch newRoute.Proto {
				case "tcp":
					i.translator.ClearTCP(oldRoute.Ip)
				case "udp":
					i.translator.ClearUDP(oldRoute.Ip)
				default:
					log.Printf("INT: unrecognized protocol: %v", newRoute)
				}
			}
			// and add the new version
			if newRoute.Target != "" {
				switch newRoute.Proto {
				case "tcp":
					i.translator.ForwardTCP(newRoute.Ip, newRoute.Target)
				case "udp":
					i.translator.ForwardUDP(newRoute.Ip, newRoute.Target)
				default:
					log.Printf("INT: unrecognized protocol: %v", newRoute)
				}
			}

			if newRoute.Name != "" {
				log.Printf("INT: STORE %v->%v", newRoute.Domain(), newRoute)
				i.domains[newRoute.Domain()] = newRoute
			}
		}

		// remove the route from our map of old routes so we
		// don't end up deleting it below
		delete(oldRoutes, newRoute.Name)
	}

	for _, route := range oldRoutes {
		log.Printf("INT: CLEAR %v->%v", route.Domain(), route)
		delete(i.domains, route.Domain())

		switch route.Proto {
		case "tcp":
			i.translator.ClearTCP(route.Ip)
		case "udp":
			i.translator.ClearUDP(route.Ip)
		default:
			log.Printf("INT: unrecognized protocol: %v", route)
		}

	}

	if table.Routes == nil || len(table.Routes) == 0 {
		delete(i.tables, table.Name)
	} else {
		i.tables[table.Name] = table
	}
}

// SetSearchPath updates the DNS search path used by the resolver
func (i *Interceptor) SetSearchPath(paths []string) {
	i.searchLock.Lock()
	defer i.searchLock.Unlock()

	i.search = paths
}

// GetSearchPath retrieves the current search path
func (i *Interceptor) GetSearchPath() []string {
	i.searchLock.RLock()
	defer i.searchLock.RUnlock()

	return i.search
}
<commit_msg>interceptor: Remove pointless `if true` blocks<commit_after>package interceptor

import (
	"encoding/json"
	"github.com/datawire/teleproxy/internal/pkg/nat"
	rt "github.com/datawire/teleproxy/internal/pkg/route"
	"log"
	"net"
	"strings"
	"sync"
)

type Interceptor struct {
	work chan func()
	done chan empty

	translator *nat.Translator
	tables     map[string]rt.Table
	tablesLock sync.RWMutex

	domains     map[string]rt.Route
	domainsLock sync.RWMutex

	search     []string
	searchLock sync.RWMutex
}

type empty struct{}

func NewInterceptor(name string) *Interceptor {
	ret := &Interceptor{
		tables:     make(map[string]rt.Table),
		translator: nat.NewTranslator(name),
		work:       make(chan func()),
		done:       make(chan empty),
		domains:    make(map[string]rt.Route),
		search:     []string{""},
	}
	ret.tablesLock.Lock() // leave it locked until .Start() unlocks it
	return ret
}

func (i *Interceptor) Start() {
	go func() {
		defer close(i.done)
		i.translator.Enable()
		i.tablesLock.Unlock()
		defer func() {
			i.tablesLock.Lock()
			i.translator.Disable()
		}()
		for {
			action, ok := <-i.work
			if ok {
				action()
			} else {
				return
			}
		}
	}()
}

func (i *Interceptor) Stop() {
	close(i.work)
	<-i.done
}

// Resolve looks up the given query in the (FIXME: somewhere), trying
// all the suffixes in the search path, and returns a Route on success
// or nil on failure. This implementation does not count the number of
// dots in the query.
func (i *Interceptor) Resolve(query string) *rt.Route {
	if !strings.HasSuffix(query, ".") {
		query += "."
	}

	i.searchLock.RLock()
	defer i.searchLock.RUnlock()
	i.domainsLock.RLock()
	defer i.domainsLock.RUnlock()

	for _, suffix := range i.search {
		name := query + suffix
		value, ok := i.domains[strings.ToLower(name)]
		if ok {
			return &value
		}
	}
	return nil
}

func (i *Interceptor) Destination(conn *net.TCPConn) (string, error) {
	_, host, err := i.translator.GetOriginalDst(conn)
	return host, err
}

func (i *Interceptor) Render(table string) string {
	var obj interface{}

	if table == "" {
		var tables []rt.Table
		i.tablesLock.RLock()
		for _, t := range i.tables {
			tables = append(tables, t)
		}
		i.tablesLock.RUnlock()
		obj = tables
	} else {
		var ok bool
		i.tablesLock.RLock()
		obj, ok = i.tables[table]
		i.tablesLock.RUnlock()
		if !ok {
			return ""
		}
	}

	bytes, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		return err.Error()
	} else {
		return string(bytes)
	}
}

func (i *Interceptor) Delete(table string) bool {
	i.tablesLock.Lock()
	defer i.tablesLock.Unlock()
	i.domainsLock.Lock()
	defer i.domainsLock.Unlock()

	var names []string
	if table == "" {
		for name := range i.tables {
			names = append(names, name)
		}
	} else if _, ok := i.tables[table]; ok {
		names = []string{table}
	} else {
		return false
	}

	for _, name := range names {
		if name != "bootstrap" {
			i.update(rt.Table{Name: name})
		}
	}

	return true
}

func (i *Interceptor) Update(table rt.Table) {
	i.tablesLock.Lock()
	defer i.tablesLock.Unlock()
	i.domainsLock.Lock()
	defer i.domainsLock.Unlock()

	i.update(table)
}

// .update() assumes that both .tablesLock and .domainsLock are held
// for writing.  Ensuring that is the case is the caller's
// responsibility.
func (i *Interceptor) update(table rt.Table) {
	oldTable, ok := i.tables[table.Name]

	oldRoutes := make(map[string]rt.Route)
	if ok {
		for _, route := range oldTable.Routes {
			oldRoutes[route.Name] = route
		}
	}

	for _, newRoute := range table.Routes {
		oldRoute, oldRouteOk := oldRoutes[newRoute.Name]
		// A nil Route (when oldRouteOk != true) will compare
		// inequal to any valid new Route.
		if newRoute != oldRoute {
			// delete the old version
			if oldRouteOk {
				switch newRoute.Proto {
				case "tcp":
					i.translator.ClearTCP(oldRoute.Ip)
				case "udp":
					i.translator.ClearUDP(oldRoute.Ip)
				default:
					log.Printf("INT: unrecognized protocol: %v", newRoute)
				}
			}
			// and add the new version
			if newRoute.Target != "" {
				switch newRoute.Proto {
				case "tcp":
					i.translator.ForwardTCP(newRoute.Ip, newRoute.Target)
				case "udp":
					i.translator.ForwardUDP(newRoute.Ip, newRoute.Target)
				default:
					log.Printf("INT: unrecognized protocol: %v", newRoute)
				}
			}

			if newRoute.Name != "" {
				log.Printf("INT: STORE %v->%v", newRoute.Domain(), newRoute)
				i.domains[newRoute.Domain()] = newRoute
			}
		}

		// remove the route from our map of old routes so we
		// don't end up deleting it below
		delete(oldRoutes, newRoute.Name)
	}

	for _, route := range oldRoutes {
		log.Printf("INT: CLEAR %v->%v", route.Domain(), route)
		delete(i.domains, route.Domain())

		switch route.Proto {
		case "tcp":
			i.translator.ClearTCP(route.Ip)
		case "udp":
			i.translator.ClearUDP(route.Ip)
		default:
			log.Printf("INT: unrecognized protocol: %v", route)
		}

	}

	if table.Routes == nil || len(table.Routes) == 0 {
		delete(i.tables, table.Name)
	} else {
		i.tables[table.Name] = table
	}
}

// SetSearchPath updates the DNS search path used by the resolver
func (i *Interceptor) SetSearchPath(paths []string) {
	i.searchLock.Lock()
	defer i.searchLock.Unlock()

	i.search = paths
}

// GetSearchPath retrieves the current search path
func (i *Interceptor) GetSearchPath() []string {
	i.searchLock.RLock()
	defer i.searchLock.RUnlock()

	return i.search
}
<|endoftext|>


<commit_before>package gitosis

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"os"
	"path"
	"strings"
	"time"
)

func (s *S) TestShouldHaveConstantForAddKey(c *C) {
	c.Assert(AddKey, Equals, 0)
}

func (s *S) TestShouldHaveConstantForRemoveKey(c *C) {
	c.Assert(RemoveKey, Equals, 1)
}

func (s *S) TestAddKeyReturnsTheKeyFileNameInTheResponseChannel(c *C) {
	response := make(chan string)
	change := Change{
		Kind: AddKey,
		Args: map[string]string{
			"key":    "so-pure",
			"member": "alanis-morissette",
		},
		Response: response,
	}
	Changes <- change
	select {
	k := <-response
	c.Assert(k, Equals, "alanis-morissette_key1.pub")
}

func (s *S) TestRemoveKeyChangeRemovesTheKey(c *C) {
	keyfile, err := buildAndStoreKeyFile("alanis-morissette", "your-house")
	c.Assert(err, IsNil)
	change := Change{
		Kind: RemoveKey,
		Args: map[string]string{"key": keyfile},
	}
	Changes <- change
	time.Sleep(1e9)
	p, err := getKeydirPath()
	c.Assert(err, IsNil)
	keypath := path.Join(p, keyfile)
	_, err = os.Stat(keypath)
	c.Assert(err, NotNil)
	c.Assert(os.IsNotExist(err), Equals, true)
}

func (s *S) TestShouldHaveConstantForAddMember(c *C) {
	c.Assert(AddMember, Equals, 2)
}

func (s *S) TestShouldHaveConstantForRemoveMember(c *C) {
	c.Assert(RemoveMember, Equals, 3)
}

func (s *S) TestAddMemberChangeAddsTheMemberToTheFile(c *C) {
	err := addGroup("dream-theater")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     AddMember,
		Args:     map[string]string{"group": "dream-theater", "member": "octavarium"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "members = octavarium"), Equals, true)
}

func (s *S) TestRemoveMemberChangeRemovesTheMemberFromTheFile(c *C) {
	err := addGroup("dream-theater")
	c.Assert(err, IsNil)
	err = addMember("dream-theater", "the-glass-prision")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     RemoveMember,
		Args:     map[string]string{"group": "dream-theater", "member": "the-glass-prision"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "members = the-glass-prision"), Equals, false)
}

func (s *S) TestShouldHaveConstantForAddGroup(c *C) {
	c.Assert(AddGroup, Equals, 4)
}

func (s *S) TestAddGroupChangeAddsAGroupToGitosisConf(c *C) {
	change := Change{
		Kind:     AddGroup,
		Args:     map[string]string{"group": "dream-theater"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "[group dream-theater]"), Equals, true)
}

func (s *S) TestShouldHaveConstantForRemoveGroup(c *C) {
	c.Assert(RemoveGroup, Equals, 5)
}

func (s *S) TestRemoveGroupChangeRemovesTheGroupFromGitosisConf(c *C) {
	err := addGroup("steve-lee")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     RemoveGroup,
		Args:     map[string]string{"group": "steve-lee"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "[group steve-lee]"), Equals, false)
}
<commit_msg>gitosis: fix silly error<commit_after>package gitosis

import (
	"io/ioutil"
	. "launchpad.net/gocheck"
	"os"
	"path"
	"strings"
	"time"
)

func (s *S) TestShouldHaveConstantForAddKey(c *C) {
	c.Assert(AddKey, Equals, 0)
}

func (s *S) TestShouldHaveConstantForRemoveKey(c *C) {
	c.Assert(RemoveKey, Equals, 1)
}

func (s *S) TestAddKeyReturnsTheKeyFileNameInTheResponseChannel(c *C) {
	response := make(chan string)
	change := Change{
		Kind: AddKey,
		Args: map[string]string{
			"key":    "so-pure",
			"member": "alanis-morissette",
		},
		Response: response,
	}
	Changes <- change
	k := <-response
	c.Assert(k, Equals, "alanis-morissette_key1.pub")
}

func (s *S) TestRemoveKeyChangeRemovesTheKey(c *C) {
	keyfile, err := buildAndStoreKeyFile("alanis-morissette", "your-house")
	c.Assert(err, IsNil)
	change := Change{
		Kind: RemoveKey,
		Args: map[string]string{"key": keyfile},
	}
	Changes <- change
	time.Sleep(1e9)
	p, err := getKeydirPath()
	c.Assert(err, IsNil)
	keypath := path.Join(p, keyfile)
	_, err = os.Stat(keypath)
	c.Assert(err, NotNil)
	c.Assert(os.IsNotExist(err), Equals, true)
}

func (s *S) TestShouldHaveConstantForAddMember(c *C) {
	c.Assert(AddMember, Equals, 2)
}

func (s *S) TestShouldHaveConstantForRemoveMember(c *C) {
	c.Assert(RemoveMember, Equals, 3)
}

func (s *S) TestAddMemberChangeAddsTheMemberToTheFile(c *C) {
	err := addGroup("dream-theater")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     AddMember,
		Args:     map[string]string{"group": "dream-theater", "member": "octavarium"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "members = octavarium"), Equals, true)
}

func (s *S) TestRemoveMemberChangeRemovesTheMemberFromTheFile(c *C) {
	err := addGroup("dream-theater")
	c.Assert(err, IsNil)
	err = addMember("dream-theater", "the-glass-prision")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     RemoveMember,
		Args:     map[string]string{"group": "dream-theater", "member": "the-glass-prision"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "members = the-glass-prision"), Equals, false)
}

func (s *S) TestShouldHaveConstantForAddGroup(c *C) {
	c.Assert(AddGroup, Equals, 4)
}

func (s *S) TestAddGroupChangeAddsAGroupToGitosisConf(c *C) {
	change := Change{
		Kind:     AddGroup,
		Args:     map[string]string{"group": "dream-theater"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "[group dream-theater]"), Equals, true)
}

func (s *S) TestShouldHaveConstantForRemoveGroup(c *C) {
	c.Assert(RemoveGroup, Equals, 5)
}

func (s *S) TestRemoveGroupChangeRemovesTheGroupFromGitosisConf(c *C) {
	err := addGroup("steve-lee")
	c.Assert(err, IsNil)
	change := Change{
		Kind:     RemoveGroup,
		Args:     map[string]string{"group": "steve-lee"},
		Response: make(chan string),
	}
	Changes <- change
	<-change.Response
	gitosis, err := os.Open(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	defer gitosis.Close()
	content, err := ioutil.ReadAll(gitosis)
	c.Assert(err, IsNil)
	c.Assert(strings.Contains(string(content), "[group steve-lee]"), Equals, false)
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2018 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/loadimpact/k6/core"
	"github.com/loadimpact/k6/core/local"
	"github.com/loadimpact/k6/js"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
	"github.com/manyminds/api2go/jsonapi"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	null "gopkg.in/guregu/null.v3"
)

func TestSetupData(t *testing.T) {
	t.Parallel()
	var testCases = []struct {
		name      string
		script    []byte
		setupRuns [][3]string
	}{
		{
			name: "setupReturns",
			script: []byte(`
			export function setup() {
				return {"v": 1};
			}

			export default function(data) {
				if (data !== undefined) {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (data !== undefined) {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{"data": {"v":1}}`},
				{"GET", "", `{"data": {"v":1}}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "", `{}`},
				{"GET", "", `{}`},
			},
		}, {

			name: "noSetup",
			script: []byte(`
			export default function(data) {
				if (!data || data.v != 2) {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (!data || data.v != 2) {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
			},
		}, {
			name: "setupNoReturn",
			script: []byte(`
			export function setup() {
				let a = {"v": 1};
			}
			export default function(data) {
				if (data === undefined || data !== "") {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (data === undefined || data !== "") {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "\"\"", `{"data": ""}`},
				{"GET", "", `{"data": ""}`},
			},
		},
	}
	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			runner, err := js.New(
				&lib.SourceData{Filename: "/script.js", Data: testCase.script},
				afero.NewMemMapFs(),
				lib.RuntimeOptions{},
			)
			require.NoError(t, err)
			runner.SetOptions(lib.Options{
				Paused:          null.BoolFrom(true),
				VUs:             null.IntFrom(2),
				VUsMax:          null.IntFrom(2),
				Iterations:      null.IntFrom(3),
				SetupTimeout:    types.NullDurationFrom(1 * time.Second),
				TeardownTimeout: types.NullDurationFrom(1 * time.Second),
			})
			executor := local.New(runner)
			executor.SetRunSetup(false)
			engine, err := core.NewEngine(executor, runner.GetOptions())
			require.NoError(t, err)

			ctx, cancel := context.WithCancel(context.Background())
			errC := make(chan error)
			go func() { errC <- engine.Run(ctx) }()

			handler := NewHandler()

			checkSetup := func(method, body, expResult string) {
				rw := httptest.NewRecorder()
				handler.ServeHTTP(rw, newRequestWithEngine(engine, method, "/v1/setup", bytes.NewBufferString(body)))
				res := rw.Result()
				assert.Equal(t, http.StatusOK, res.StatusCode)

				var doc jsonapi.Document
				assert.NoError(t, json.Unmarshal(rw.Body.Bytes(), &doc))
				if !assert.NotNil(t, doc.Data.DataObject) {
					return
				}
				assert.Equal(t, "setupData", doc.Data.DataObject.Type)
				assert.JSONEq(t, expResult, string(doc.Data.DataObject.Attributes))
			}

			for _, setupRun := range testCase.setupRuns {
				checkSetup(setupRun[0], setupRun[1], setupRun[2])
			}

			engine.Executor.SetPaused(false)

			select {
			case <-time.After(10 * time.Second):
				cancel()
				t.Fatal("Test timed out")
			case err := <-errC:
				cancel()
				require.NoError(t, err)
			}
		})
	}
}
<commit_msg>Fix nil pointer in test<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2018 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/loadimpact/k6/core"
	"github.com/loadimpact/k6/core/local"
	"github.com/loadimpact/k6/js"
	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
	"github.com/manyminds/api2go/jsonapi"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	null "gopkg.in/guregu/null.v3"
)

func TestSetupData(t *testing.T) {
	t.Parallel()
	var testCases = []struct {
		name      string
		script    []byte
		setupRuns [][3]string
	}{
		{
			name: "setupReturns",
			script: []byte(`
			export function setup() {
				return {"v": 1};
			}

			export default function(data) {
				if (data !== undefined) {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (data !== undefined) {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{"data": {"v":1}}`},
				{"GET", "", `{"data": {"v":1}}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "", `{}`},
				{"GET", "", `{}`},
			},
		}, {

			name: "noSetup",
			script: []byte(`
			export default function(data) {
				if (!data || data.v != 2) {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (!data || data.v != 2) {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
			},
		}, {
			name: "setupNoReturn",
			script: []byte(`
			export function setup() {
				let a = {"v": 1};
			}
			export default function(data) {
				if (data === undefined || data !== "") {
					throw new Error("incorrect data: " + JSON.stringify(data));
				}
			};

			export function teardown(data) {
				if (data === undefined || data !== "") {
					throw new Error("incorrect teardown data: " + JSON.stringify(data));
				}
			} `),
			setupRuns: [][3]string{
				{"GET", "", "{}"},
				{"POST", "", `{}`},
				{"GET", "", `{}`},
				{"PUT", `{"v":2, "test":"mest"}`, `{"data": {"v":2, "test":"mest"}}`},
				{"GET", "", `{"data": {"v":2, "test":"mest"}}`},
				{"PUT", "\"\"", `{"data": ""}`},
				{"GET", "", `{"data": ""}`},
			},
		},
	}
	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			runner, err := js.New(
				&lib.SourceData{Filename: "/script.js", Data: testCase.script},
				afero.NewMemMapFs(),
				lib.RuntimeOptions{},
			)
			require.NoError(t, err)
			runner.SetOptions(lib.Options{
				Paused:          null.BoolFrom(true),
				VUs:             null.IntFrom(2),
				VUsMax:          null.IntFrom(2),
				Iterations:      null.IntFrom(3),
				SetupTimeout:    types.NullDurationFrom(1 * time.Second),
				TeardownTimeout: types.NullDurationFrom(1 * time.Second),
			})
			executor := local.New(runner)
			executor.SetRunSetup(false)
			engine, err := core.NewEngine(executor, runner.GetOptions())
			require.NoError(t, err)

			ctx, cancel := context.WithCancel(context.Background())
			errC := make(chan error)
			go func() { errC <- engine.Run(ctx) }()

			handler := NewHandler()

			checkSetup := func(method, body, expResult string) {
				rw := httptest.NewRecorder()
				handler.ServeHTTP(rw, newRequestWithEngine(engine, method, "/v1/setup", bytes.NewBufferString(body)))
				res := rw.Result()
				assert.Equal(t, http.StatusOK, res.StatusCode)

				var doc jsonapi.Document
				assert.NoError(t, json.Unmarshal(rw.Body.Bytes(), &doc))
				require.NotNil(t, doc.Data)
				require.NotNil(t, doc.Data.DataObject)
				assert.Equal(t, "setupData", doc.Data.DataObject.Type)
				assert.JSONEq(t, expResult, string(doc.Data.DataObject.Attributes))
			}

			for _, setupRun := range testCase.setupRuns {
				checkSetup(setupRun[0], setupRun[1], setupRun[2])
			}

			engine.Executor.SetPaused(false)

			select {
			case <-time.After(10 * time.Second):
				cancel()
				t.Fatal("Test timed out")
			case err := <-errC:
				cancel()
				require.NoError(t, err)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Package autosite hosts websites automatically based on file
// structure.
//
// See https://github.com/hkjn/hkjnweb for a setup (that implements
// http://www.hkjn.me / http://blog.hkjn.me) using this package.
//
// Example usage:
//   mysite := New(
//     "Some title",   // for HTML <head>
//     "pages/*.tmpl", // pattern for pages on disk
//     "domain.com",   // live domain
//     []string{       // shared templates
//       "base.tmpl",
//       "other.tmpl",
//     },
//     func(r *http.Request) Logger {
//       return appengine.NewContext(r)
//     },
//     !appengine.IsDevAppServer(),
//     template.FuncMap{
//       "live": func() bool { !appengine.IsDevAppServer() }
//     },
//   )
//   mysite.Register()
//
// This will host pages like domain.com/Foo and /Bar if there's
// files pages/Foo.tmpl and pages/Bar.tmpl relative to the calling
// package, also using "base.tmpl" and "other.tmpl" to compile the
// templates for rendering those pages.
//
// The template functions specified are are available within templates, in addition
// to the built-in ones. In the example above, {{live}} could be used.
//
// The following data is available within each template:
//   {{.Title}}: The <title> of the page.
//   {{.Date.Year}}, {{.Date.Month}}: Year and month that the page was
//      published, if file pattern includes it.
//   {{.URI}}: URI to the page.
package autosite // import "hkjn.me/autosite"

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var BaseTemplate = "base" // name of top-level template to invoke for each page

// New creates a new autosite.
//
// New panics on errors reading templates.
func New(title, glob, liveDomain string, templates []string, logger LoggerFunc, isLive bool, tmplFuncs template.FuncMap) Site {
	s := internalNew(title, glob, liveDomain, templates, logger, isLive, tmplFuncs)
	return &s
}

// internalNew creates the concrete site.
func internalNew(title, glob, liveDomain string, templates []string, logger LoggerFunc, isLive bool, tmplFuncs template.FuncMap) site {
	s := site{
		liveDomain: liveDomain,
		title:      title,
		glob:       glob,
		templates:  templates,
		isLive:     isLive,
		logger:     logger,
		tmplFuncs:  tmplFuncs,
	}
	err := s.read()
	if err != nil {
		log.Fatalf(err.Error())
	}
	return s
}

// ChangeURI changes the URI a page will be served on.
//
// ChangeURI panics if the old URI is not registered.
func (s *site) ChangeURI(uri, newURI string) {
	p, ok := s.pages[uri]
	if !ok {
		log.Fatalf("no page with URI %v\n", uri)
	}
	p.URI = newURI
	delete(s.pages, uri)
	s.pages[newURI] = p
	log.Printf("remapped %v to %v\n", p, newURI)
}

// AddRedirect registers an URI that redirects.
//
// AddRedirect panics if the URI already is taken.
func (s *site) AddRedirect(uri, redirectURI string) {
	p, exists := s.pages[uri]
	if exists {
		log.Fatalf("page %v already is registered for URI %s\n", p, uri)
	}
	s.pages[uri] = page{
		Title:       s.title,
		URI:         uri,
		redirectURI: redirectURI,
		logger:      s.logger,
	}
	log.Printf("added redirect on %s to %s\n", uri, redirectURI)
}

// Register registers the HTTP handlers for the site.
func (s *site) Register() {
	for uri, p := range s.pages {
		if s.isLive {
			uri = fmt.Sprintf("%s%s", s.liveDomain, p.URI)
		}
		http.Handle(uri, p)
		log.Printf("registered handler %s: %+v\n", uri, p)
	}
}

// Site represents a website.
type Site interface {
	Register()                           // Registers the HTTP handlers for the site.
	ChangeURI(oldURI, newURI string)     // Changes an URI.
	AddRedirect(uri, redirectURI string) // Adds a redirect URI.
}

// site is a website representation.
type site struct {
	liveDomain string           // live domain of the site
	title      string           // title of the site, for HTML <head>
	glob       string           // file glob for page templates
	templates  []string         // templates needed for all endpoints
	isLive     bool             // whether the site is live
	logger     LoggerFunc       // func to retrieve logger
	pages      map[string]page  // URI -> page mapping
	tmplFuncs  template.FuncMap // extra template funcs
}

// page is a HTML resource.
type page struct {
	Title string      // title, for <head>
	Date  date        // publishing date
	URI   string      // URI path
	Data  interface{} // custom data, if any

	redirectURI string             // URI to redirect to
	tmpl        *template.Template // backing template
	logger      LoggerFunc         // func to retrieve lgoger
}

type year int

// date is a rough point in time.
type date struct {
	Year  year
	Month time.Month
}

// before says whether this date is before other date.
func (d date) before(other date) bool {
	if d.Year < other.Year {
		return true
	} else if d.Year == other.Year {
		return d.Month < other.Month
	}
	return false
}

// ServeHTTP serves the page.
func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	l := p.logger(r)
	l.Infof("%+v will ServeHTTP for URI %s\n", p, r.RequestURI)

	if p.URI != r.RequestURI {
		l.Errorf("bad request URI %s, want %s; serving 404\n", r.RequestURI, p.URI)
		http.NotFound(w, r)
		return
	}
	if p.redirectURI != "" {
		l.Infof("redirecting %s to %s\n", r.RequestURI, p.redirectURI)
		http.Redirect(w, r, p.redirectURI, http.StatusFound)
		return
	}

	err := p.tmpl.ExecuteTemplate(w, BaseTemplate, p)
	if err != nil {
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		l.Criticalf(err.Error())
		return
	}
}

// String provides a string representation of the page.
func (p page) String() string {
	r := fmt.Sprintf("page [%s]", p.URI)
	if p.Date.Year != 0 {
		r += fmt.Sprintf(", published on %v", p.Date.Year)
		if p.Date.Month != 0 {
			r += fmt.Sprintf(", %v", p.Date.Month)
		}
	}
	return r
}

// read reads pages to serve on the autosite from disk
func (s *site) read() error {
	filePaths, err := s.getFiles()
	if err != nil {
		return err
	}
	s.pages = make(map[string]page)
	for _, tmplPath := range filePaths {
		uri, d, err := parsePath(tmplPath)
		if err != nil {
			return err
		}
		s.addPage(uri, d, nil, append(s.templates, tmplPath))
	}
	return nil
}

// getFiles retrieves all pages' file paths from disk.
func (s *site) getFiles() ([]string, error) {
	paths, err := filepath.Glob(s.glob)
	if err != nil {
		return []string{}, err
	}
	if len(paths) == 0 {
		return []string{}, fmt.Errorf("no pages found")
	}

	// Skip files with dot prefixes (e.g. .#foo.tmpl).
	r := make([]string, len(paths))
	i := 0
	for _, p := range paths {
		if strings.Contains(p, ".#") {
			continue
		}
		r[i] = p
		i++
	}
	return r[0:i], nil
}

// parsePath extracts URI and date from a template file path.
func parsePath(p string) (uri string, d date, err error) {
	parts := strings.Split(p, "/")
	if len(parts) == 2 {
		// Assumes [dir]/*.tmpl; i.e. no date.
		uri = fmt.Sprintf("/%s", strings.TrimSuffix(parts[1], ".tmpl"))
	} else if len(parts) == 4 {
		// Assumes [dir]/[yyyy]/[mm]/*.tmpl; i.e. date is present.
		uri = "/" + strings.Join([]string{
			parts[1],
			parts[2],
			strings.TrimSuffix(parts[3], ".tmpl")}, "/")
		d, err = getDate(parts[1], parts[2])
		if err != nil {
			return
		}
	} else {
		err = fmt.Errorf("bad template path: %s", p)
		return
	}
	return uri, d, nil
}

// addPage adds a page to the autosite.
func (s *site) addPage(uri string, d date, data interface{}, tmpls []string) {
	var t *template.Template
	if len(tmpls) > 0 {
		t = template.Must(template.New(BaseTemplate).Funcs(s.tmplFuncs).ParseFiles(tmpls...))
	}
	s.pages[uri] = page{
		Title:  s.title,
		URI:    uri,
		Data:   data,
		Date:   d,
		tmpl:   t,
		logger: s.logger,
	}
}

// getDate extracts the date of the post from year and month strings.
func getDate(y, m string) (date, error) {
	y64, err := strconv.ParseInt(y, 10, 0)
	if err != nil || y64 <= 1900 || y64 >= 99999 {
		return date{}, fmt.Errorf("bad year: %v", y)
	}
	month, err := strconv.ParseInt(m, 10, 0)
	if err != nil || month < 1 || month > 12 {
		return date{}, fmt.Errorf("bad month: %v", m)
	}
	return date{
		Year:  year(y64),
		Month: time.Month(month),
	}, nil
}
<commit_msg>Drop canonical package restriction<commit_after>// Package autosite hosts websites automatically based on file
// structure.
//
// See https://github.com/hkjn/hkjnweb for a setup (that implements
// http://www.hkjn.me / http://blog.hkjn.me) using this package.
//
// Example usage:
//   mysite := New(
//     "Some title",   // for HTML <head>
//     "pages/*.tmpl", // pattern for pages on disk
//     "domain.com",   // live domain
//     []string{       // shared templates
//       "base.tmpl",
//       "other.tmpl",
//     },
//     func(r *http.Request) Logger {
//       return appengine.NewContext(r)
//     },
//     !appengine.IsDevAppServer(),
//     template.FuncMap{
//       "live": func() bool { !appengine.IsDevAppServer() }
//     },
//   )
//   mysite.Register()
//
// This will host pages like domain.com/Foo and /Bar if there's
// files pages/Foo.tmpl and pages/Bar.tmpl relative to the calling
// package, also using "base.tmpl" and "other.tmpl" to compile the
// templates for rendering those pages.
//
// The template functions specified are are available within templates, in addition
// to the built-in ones. In the example above, {{live}} could be used.
//
// The following data is available within each template:
//   {{.Title}}: The <title> of the page.
//   {{.Date.Year}}, {{.Date.Month}}: Year and month that the page was
//      published, if file pattern includes it.
//   {{.URI}}: URI to the page.
package autosite

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var BaseTemplate = "base" // name of top-level template to invoke for each page

// New creates a new autosite.
//
// New panics on errors reading templates.
func New(title, glob, liveDomain string, templates []string, logger LoggerFunc, isLive bool, tmplFuncs template.FuncMap) Site {
	s := internalNew(title, glob, liveDomain, templates, logger, isLive, tmplFuncs)
	return &s
}

// internalNew creates the concrete site.
func internalNew(title, glob, liveDomain string, templates []string, logger LoggerFunc, isLive bool, tmplFuncs template.FuncMap) site {
	s := site{
		liveDomain: liveDomain,
		title:      title,
		glob:       glob,
		templates:  templates,
		isLive:     isLive,
		logger:     logger,
		tmplFuncs:  tmplFuncs,
	}
	err := s.read()
	if err != nil {
		log.Fatalf(err.Error())
	}
	return s
}

// ChangeURI changes the URI a page will be served on.
//
// ChangeURI panics if the old URI is not registered.
func (s *site) ChangeURI(uri, newURI string) {
	p, ok := s.pages[uri]
	if !ok {
		log.Fatalf("no page with URI %v\n", uri)
	}
	p.URI = newURI
	delete(s.pages, uri)
	s.pages[newURI] = p
	log.Printf("remapped %v to %v\n", p, newURI)
}

// AddRedirect registers an URI that redirects.
//
// AddRedirect panics if the URI already is taken.
func (s *site) AddRedirect(uri, redirectURI string) {
	p, exists := s.pages[uri]
	if exists {
		log.Fatalf("page %v already is registered for URI %s\n", p, uri)
	}
	s.pages[uri] = page{
		Title:       s.title,
		URI:         uri,
		redirectURI: redirectURI,
		logger:      s.logger,
	}
	log.Printf("added redirect on %s to %s\n", uri, redirectURI)
}

// Register registers the HTTP handlers for the site.
func (s *site) Register() {
	for uri, p := range s.pages {
		if s.isLive {
			uri = fmt.Sprintf("%s%s", s.liveDomain, p.URI)
		}
		http.Handle(uri, p)
		log.Printf("registered handler %s: %+v\n", uri, p)
	}
}

// Site represents a website.
type Site interface {
	Register()                           // Registers the HTTP handlers for the site.
	ChangeURI(oldURI, newURI string)     // Changes an URI.
	AddRedirect(uri, redirectURI string) // Adds a redirect URI.
}

// site is a website representation.
type site struct {
	liveDomain string           // live domain of the site
	title      string           // title of the site, for HTML <head>
	glob       string           // file glob for page templates
	templates  []string         // templates needed for all endpoints
	isLive     bool             // whether the site is live
	logger     LoggerFunc       // func to retrieve logger
	pages      map[string]page  // URI -> page mapping
	tmplFuncs  template.FuncMap // extra template funcs
}

// page is a HTML resource.
type page struct {
	Title string      // title, for <head>
	Date  date        // publishing date
	URI   string      // URI path
	Data  interface{} // custom data, if any

	redirectURI string             // URI to redirect to
	tmpl        *template.Template // backing template
	logger      LoggerFunc         // func to retrieve lgoger
}

type year int

// date is a rough point in time.
type date struct {
	Year  year
	Month time.Month
}

// before says whether this date is before other date.
func (d date) before(other date) bool {
	if d.Year < other.Year {
		return true
	} else if d.Year == other.Year {
		return d.Month < other.Month
	}
	return false
}

// ServeHTTP serves the page.
func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	l := p.logger(r)
	l.Infof("%+v will ServeHTTP for URI %s\n", p, r.RequestURI)

	if p.URI != r.RequestURI {
		l.Errorf("bad request URI %s, want %s; serving 404\n", r.RequestURI, p.URI)
		http.NotFound(w, r)
		return
	}
	if p.redirectURI != "" {
		l.Infof("redirecting %s to %s\n", r.RequestURI, p.redirectURI)
		http.Redirect(w, r, p.redirectURI, http.StatusFound)
		return
	}

	err := p.tmpl.ExecuteTemplate(w, BaseTemplate, p)
	if err != nil {
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		l.Criticalf(err.Error())
		return
	}
}

// String provides a string representation of the page.
func (p page) String() string {
	r := fmt.Sprintf("page [%s]", p.URI)
	if p.Date.Year != 0 {
		r += fmt.Sprintf(", published on %v", p.Date.Year)
		if p.Date.Month != 0 {
			r += fmt.Sprintf(", %v", p.Date.Month)
		}
	}
	return r
}

// read reads pages to serve on the autosite from disk
func (s *site) read() error {
	filePaths, err := s.getFiles()
	if err != nil {
		return err
	}
	s.pages = make(map[string]page)
	for _, tmplPath := range filePaths {
		uri, d, err := parsePath(tmplPath)
		if err != nil {
			return err
		}
		s.addPage(uri, d, nil, append(s.templates, tmplPath))
	}
	return nil
}

// getFiles retrieves all pages' file paths from disk.
func (s *site) getFiles() ([]string, error) {
	paths, err := filepath.Glob(s.glob)
	if err != nil {
		return []string{}, err
	}
	if len(paths) == 0 {
		return []string{}, fmt.Errorf("no pages found")
	}

	// Skip files with dot prefixes (e.g. .#foo.tmpl).
	r := make([]string, len(paths))
	i := 0
	for _, p := range paths {
		if strings.Contains(p, ".#") {
			continue
		}
		r[i] = p
		i++
	}
	return r[0:i], nil
}

// parsePath extracts URI and date from a template file path.
func parsePath(p string) (uri string, d date, err error) {
	parts := strings.Split(p, "/")
	if len(parts) == 2 {
		// Assumes [dir]/*.tmpl; i.e. no date.
		uri = fmt.Sprintf("/%s", strings.TrimSuffix(parts[1], ".tmpl"))
	} else if len(parts) == 4 {
		// Assumes [dir]/[yyyy]/[mm]/*.tmpl; i.e. date is present.
		uri = "/" + strings.Join([]string{
			parts[1],
			parts[2],
			strings.TrimSuffix(parts[3], ".tmpl")}, "/")
		d, err = getDate(parts[1], parts[2])
		if err != nil {
			return
		}
	} else {
		err = fmt.Errorf("bad template path: %s", p)
		return
	}
	return uri, d, nil
}

// addPage adds a page to the autosite.
func (s *site) addPage(uri string, d date, data interface{}, tmpls []string) {
	var t *template.Template
	if len(tmpls) > 0 {
		t = template.Must(template.New(BaseTemplate).Funcs(s.tmplFuncs).ParseFiles(tmpls...))
	}
	s.pages[uri] = page{
		Title:  s.title,
		URI:    uri,
		Data:   data,
		Date:   d,
		tmpl:   t,
		logger: s.logger,
	}
}

// getDate extracts the date of the post from year and month strings.
func getDate(y, m string) (date, error) {
	y64, err := strconv.ParseInt(y, 10, 0)
	if err != nil || y64 <= 1900 || y64 >= 99999 {
		return date{}, fmt.Errorf("bad year: %v", y)
	}
	month, err := strconv.ParseInt(m, 10, 0)
	if err != nil || month < 1 || month > 12 {
		return date{}, fmt.Errorf("bad month: %v", m)
	}
	return date{
		Year:  year(y64),
		Month: time.Month(month),
	}, nil
}
<|endoftext|>


<commit_before>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		frm, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		HasArg := func(n int) bool {
			return Nth(frm, n-1).Cdr() != nil
		}
		Arg := func(n int) interface{} {
			return Nth(frm, n).Car()
		}
		ArgL := func(n int) Lister {
			arg, ok := Arg(n).(Lister)
			Assert(ok, fmt.Sprintf("WTF! Stack frame argument %d isn't a list", n))
			return arg
		}
		exp, ok := frm.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(frm.Car())
		} else {
			switch t := exp.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if frm.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					frm.SetCar(frm.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if exp.Cdr() == nil {
						fmt.Print("{0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("{1 ")
						frm.SetCdr(&List{exp.Cdr(), nil})
					} else if Arg(1) != nil {
						fmt.Print("{2 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
						frm.SetCdr(&List{ArgL(1).Cdr(), nil})
					} else {
						fmt.Print("{3 ")
						Ret(Arg(2))
					}
				case "q'":
					fmt.Print("' ")
					Assert(exp.Cdr() != nil, "WTF! Missing argument to quote")
					Ret(exp.Cdr().Car())
				case ":^'", ":>'", ":|'": // op, ret
					if !HasArg(1) {
						fmt.Print(":1 ")
						Assert(exp.Cdr() != nil, "WTF! Missing argument to "+t)
						C_ = &List{&List{exp.Cdr().Car(), nil}, C_}
					} else if Arg(1) == nil {
						fmt.Print(":2 ")
						Ret(nil)
					} else { // TODO: better error message if not a list
						fmt.Print(":3 ")
						switch t {
						case ":^'":
							Ret(ArgL(1).Car())
						case ":>'":
							Ret(ArgL(1).Cdr())
						case ":|'":
							Ret(ArgL(1).Last())
						}
					}
				case "lt'": // lt', args...
					if exp.Cdr() == nil {
						fmt.Print("lt0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("lt1 ")
						frm.SetCdr(&List{exp.Cdr(), nil})
					} else if Arg(1) != nil {
						fmt.Print("lt2 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
						frm.Cdr().SetCar(ArgL(1).Cdr())
					} else {
						fmt.Print("lt3 ")
						switch t2 := frm.Last().Car().(type) {
						case nil:
							Nth(frm, -2).SetCdr(nil)
						case Lister:
							Nth(frm, -2).SetCdr(t2)
						default:
							Assert(false, "WTF! Last argument to lt' must be a list")
						}
						Ret(Nth(frm, 2))
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if exp.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("?1 ")
						frm.SetCdr(&List{exp.Cdr(), &List{exp.Cdr().Cdr(), nil}})
					} else if !HasArg(3) {
						fmt.Print("?2 ")
						C_ = &List{&List{ArgL(1).Car(), nil}, C_}
					} else {
						if Arg(2) == nil {
							fmt.Print("?3 ")
							Ret(Arg(3))
						} else if Arg(3) != nil {
							fmt.Print("?4 ")
							frm.SetCdr(&List{ArgL(1).Cdr(), &List{nil, nil}})
						} else if ArgL(2).Cdr() == nil {
							fmt.Print("?5 ")
							Ret(nil)
						} else {
							fmt.Print("?6 ")
							frm.SetCdr(&List{ArgL(2).Cdr(), &List{ArgL(2).Cdr().Cdr(), nil}})
						}
					}
				case "pr'":
					// pr', ret
					// TODO: print all arguments
					if exp.Cdr() == nil {
						fmt.Print("pr0 ")
						Ret(nil)
					} else if !HasArg(1) {
						fmt.Print("pr1 ")
						C_ = &List{&List{exp.Cdr().Car(), nil}, C_}
					} else if Arg(1) == nil {
						fmt.Print("pr2 ")
						Ret(nil)
					} else {
						fmt.Print("pr3 ")
						s := make([]uint8, Len(ArgL(1))) // TODO: better error message if not a list
						for i, arg := 0, ArgL(1); arg != nil; i, arg = i+1, arg.Cdr() {
							c, ok := arg.Car().(Inter)
							Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
								"WTF! pr' takes a string")
							s[i] = uint8(c.Int64())
						}
						fmt.Print(string(s))
						Ret(Arg(1))
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func Nth(ls Lister, n int) Lister {
	if ls == nil {
		return nil
	}
	if n > 0 {
		return Nth(ls.Cdr(), n-1)
	}
	if n < 0 {
		return Nth(ls, Len(ls)+n)
	}
	return ls
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<commit_msg>Arg* -> NC*r<commit_after>package anylisp

import (
	"fmt"
	"math/big"
	"os"
)

type Lister interface {
	Car() interface{}
	SetCar(v interface{}) interface{}
	Cdr() Lister
	SetCdr(v Lister) Lister
	Last() Lister
}

type List struct {
	car interface{}
	cdr Lister
}

type Inter interface {
	Cmp(y *big.Int) (r int)
	Int64() int64
}

var (
	Ps_      Lister
	C_       Lister
	TempRoot Lister
)

func Parse(code string) {
	TempRoot = &List{"sx'", nil}
	Ps_ = &List{TempRoot, nil}
	C_ = &List{&List{TempRoot, nil}, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Cdr() != nil, "Parse WTF! Too many )s")
				Ps_ = Ps_.Cdr()
			} else if len(tok) > 0 {
				var ls Lister
				if tok == "(" { // list
					ls = &List{nil, nil}
				} else if tok[0] == '[' && tok[len(tok)-1] == ']' { // number
					for j := 1; j < len(tok)-1; j++ {
						Assert(tok[j] == '-' || (tok[j] >= '0' && tok[j] <= '9') || (tok[j] >= 'a' && tok[j] <= 'f'),
							"Parse WTF! Bad character in number")
					}
					bi := new(big.Int)
					_, err := fmt.Sscanf(tok[1:len(tok)-1], "%x", bi)
					Assert(err == nil, "Parse WTF! Bad number")
					ls = &List{bi, nil}
				} else { // symbol
					ls = &List{tok, nil}
				}
				if Ps_.Car() == nil {
					Ps_.Cdr().Car().(Lister).SetCar(ls) // 1st token in list
				} else {
					Ps_.Car().(Lister).SetCdr(ls)
				}
				Ps_.SetCar(ls)
				if tok == "(" {
					Ps_ = &List{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Cdr() == nil, "Parse WTF! Too few )s")
}

func Run() {
	for C_ != nil {
		f, ok := C_.Car().(Lister)
		Assert(ok, "WTF! Bad stack frame")
		e, ok := f.Car().(Lister)
		if !ok {
			fmt.Print("0 ")
			Ret(f.Car())
		} else {
			switch t := e.Car().(type) {
			case nil:
				Assert(false, "WTF! Can't call the empty list")
			case Inter:
				Assert(false, "WTF! Can't call an int")
			case Lister:
				Assert(false, "WTF! Can't call a list")
				// I kind of like the behavior below, but it causes strange error messages if there's a bug
				/*if f.Cdr() == nil {
					fmt.Println("a")
					C_ = &List{&List{t, nil}, C_}
				} else {
					fmt.Println("b")
					f.SetCar(f.Cdr().Car())
				}*/
			case string:
				switch t {
				case "sx'": // sx', arg, ret
					if e.Cdr() == nil {
						fmt.Print("{0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("{1 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("{2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.SetCdr(&List{NCarL(f, 1).Cdr(), nil})
					} else {
						fmt.Print("{3 ")
						Ret(NCar(f, 2))
					}
				case "q'":
					fmt.Print("' ")
					Assert(e.Cdr() != nil, "WTF! Missing argument to quote")
					Ret(e.Cdr().Car())
				case ":^'", ":>'", ":|'": // op, ret
					if f.Cdr() == nil {
						fmt.Print(":1 ")
						Assert(e.Cdr() != nil, "WTF! Missing argument to "+t)
						C_ = &List{&List{e.Cdr().Car(), nil}, C_}
					} else if f.Cdr().Car() == nil {
						fmt.Print(":2 ")
						Ret(nil)
					} else {
						fmt.Print(":3 ")
						arg := NCarLA(f, 1, "WTF! "+t+" takes a list")
						switch t {
						case ":^'":
							Ret(arg.Car())
						case ":>'":
							Ret(arg.Cdr())
						case ":|'":
							Ret(arg.Last())
						}
					}
				case "lt'": // lt', args...
					if e.Cdr() == nil {
						fmt.Print("lt0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("lt1 ")
						f.SetCdr(&List{e.Cdr(), nil})
					} else if f.Cdr().Car() != nil {
						fmt.Print("lt2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
						f.Cdr().SetCar(NCarL(f, 1).Cdr())
					} else {
						fmt.Print("lt3 ")
						switch t2 := f.Last().Car().(type) {
						case nil:
							NCdr(f, -2).SetCdr(nil)
						case Lister:
							NCdr(f, -2).SetCdr(t2)
						default:
							Assert(false, "WTF! Last argument to lt' must be a list")
						}
						Ret(NCdr(f, 2))
					}
				case "?'":
					// ?', if part, then part, ret
					// ?', then part, nil, ret
					if e.Cdr() == nil {
						fmt.Print("?0 ")
						Ret(nil)
					} else if NCdr(f, 1) == nil {
						fmt.Print("?1 ")
						f.SetCdr(&List{e.Cdr(), &List{e.Cdr().Cdr(), nil}})
					} else if NCdr(f, 3) == nil {
						fmt.Print("?2 ")
						C_ = &List{&List{NCarL(f, 1).Car(), nil}, C_}
					} else if NCar(f, 2) == nil {
						fmt.Print("?3 ")
						Ret(NCar(f, 3))
					} else if NCar(f, 3) != nil {
						fmt.Print("?4 ")
						f.SetCdr(&List{NCarL(f, 1).Cdr(), &List{nil, nil}})
					} else if NCarL(f, 2).Cdr() == nil {
						fmt.Print("?5 ")
						Ret(nil)
					} else {
						fmt.Print("?6 ")
						f.SetCdr(&List{NCarL(f, 2).Cdr(), &List{NCarL(f, 2).Cdr().Cdr(), nil}})
					}
				case "pr'":
					// pr', ret
					// TODO: print all arguments
					if e.Cdr() == nil {
						fmt.Print("pr0 ")
						Ret(nil)
					} else if f.Cdr() == nil {
						fmt.Print("pr1 ")
						C_ = &List{&List{e.Cdr().Car(), nil}, C_}
					} else if f.Cdr().Car() == nil {
						fmt.Print("pr2 ")
						Ret(nil)
					} else {
						fmt.Print("pr3 ")
						s := make([]uint8, Len(NCarLA(f, 1, "WTF! pr' takes a string")))
						for i, arg := 0, NCarL(f, 1); arg != nil; i, arg = i+1, arg.Cdr() {
							c, ok := arg.Car().(Inter)
							Assert(ok && c.Cmp(big.NewInt(-1)) == 1 && c.Cmp(big.NewInt(256)) == -1,
								"WTF! pr' takes a string")
							s[i] = uint8(c.Int64())
						}
						fmt.Print(string(s))
						Ret(f.Cdr().Car())
					}
				default:
					Assert(false, "WTF! Can't call undefined function \""+t+"\"")
				}
			default:
				Assert(false, "WTF! Unrecognized function type (probably an interpreter bug)")
			}
		}
	}
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case nil:
		fmt.Print("( ) ")
	case Inter:
		fmt.Printf("[%x] ", t)
	case Lister:
		fmt.Print("( ")
		for ls != nil {
			PrintTree(ls.(Lister).Car())
			ls = ls.(Lister).Cdr()
		}
		fmt.Print(") ")
	case string:
		fmt.Print(t + " ")
	default:
		Assert(false, "Unrecognized object in tree")
	}
}

func Ret(v interface{}) {
	if C_.Cdr() != nil {
		C_.Cdr().Car().(Lister).Last().SetCdr(&List{v, nil})
	}
	C_ = C_.Cdr()
}

func Len(ls Lister) int {
	if ls == nil {
		return 0
	}
	if ls.Cdr() == nil {
		return 1
	}
	return Len(ls.Cdr()) + 1
}

func NCar(ls Lister, n int) interface{} {
	nCdr := NCdr(ls, n)
	Assert(nCdr != nil, "WTF! Out of bounds when calling :'")
	return nCdr.Car()
}

func NCarL(ls Lister, n int) Lister {
	return NCarLA(ls, n, "WTF! Requested list element isn't a list")
}

func NCarLA(ls Lister, n int, msg string) Lister {
	nCar, ok := NCar(ls, n).(Lister)
	Assert(ok, msg)
	return nCar
}

func NCdr(ls Lister, n int) Lister {
	if ls == nil {
		return nil
	}
	if n > 0 {
		return NCdr(ls.Cdr(), n-1)
	}
	if n < 0 {
		return NCdr(ls, Len(ls)+n)
	}
	return ls
}

func (ls *List) Car() interface{} {
	return ls.car
}

func (ls *List) SetCar(v interface{}) interface{} {
	ls.car = v
	return v
}

func (ls *List) Cdr() Lister {
	return ls.cdr
}

func (ls *List) SetCdr(v Lister) Lister {
	ls.cdr = v
	return v
}

func (ls *List) Last() Lister {
	if ls.cdr == nil {
		return ls
	}
	return ls.cdr.Last()
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/travis-ci/cloud-brain/cbcontext"
	"github.com/travis-ci/cloud-brain/cloudbrain"
)

var (
	errCouldntGetInstance = fmt.Errorf("couldn't get instance")
	errNoURLPrefix        = fmt.Errorf("no url prefix")
	errNoURLPath          = fmt.Errorf("no path in url")
	errInstanceIsNil      = fmt.Errorf("instance is nil")
)

func handleInstances(ctx context.Context, core *cloudbrain.Core) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx = cbcontext.FromRequestID(ctx, r.Header.Get("X-Request-ID"))

		switch r.Method {
		case "GET":
			handleInstancesGet(ctx, core, w, r)
		case "POST":
			handleInstancesPost(ctx, core, w, r)
		case "DELETE":
			handleInstancesDelete(ctx, core, w, r)
		default:
			respondError(ctx, w, http.StatusMethodNotAllowed, nil)
		}
	})
}

func handleInstancesGet(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	// Determine the path...
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(ctx, w, http.StatusNotFound, errNoURLPrefix)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(ctx, w, http.StatusNotFound, errNoURLPath)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}
	if instance == nil {
		respondError(ctx, w, http.StatusNotFound, errInstanceIsNil)
		return
	}

	respondOk(ctx, w, instanceToResponse(instance))
}

func handleInstancesPost(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	var req CreateInstanceRequest

	if err := parseRequest(ctx, r, &req); err != nil {
		respondError(ctx, w, http.StatusBadRequest, err)
		return
	}

	instance, err := core.CreateInstance(ctx, req.Provider, cloudbrain.CreateInstanceAttributes{
		ImageName:    req.Image,
		InstanceType: req.InstanceType,
		PublicSSHKey: req.PublicSSHKey,
	})
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}

	respondOk(ctx, w, instanceToResponse(instance))
}

func handleInstancesDelete(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(ctx, w, http.StatusNotFound, errNoURLPrefix)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(ctx, w, http.StatusNotFound, errNoURLPath)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}
	if instance == nil {
		respondError(ctx, w, http.StatusNotFound, errInstanceIsNil)
		return
	}

	err = core.RemoveInstance(ctx, cloudbrain.DeleteInstanceAttributes{
		InstanceID: instance.ID,
	})
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}

	respondOk(ctx, w, nil)
}

func instanceToResponse(instance *cloudbrain.Instance) *InstanceResponse {
	body := &InstanceResponse{
		ID:           instance.ID,
		ProviderName: instance.ProviderName,
		Image:        instance.Image,
		State:        instance.State,
	}
	if instance.IPAddress != "" {
		body.IPAddress = &instance.IPAddress
	}

	return body
}

// An InstanceResponse is returned by the HTTP API that contains information
// about an instance.
type InstanceResponse struct {
	ID           string  `json:"id"`
	ProviderName string  `json:"provider"`
	Image        string  `json:"image"`
	IPAddress    *string `json:"ip_address"`
	State        string  `json:"state"`
}

// CreateInstanceRequest contains the data in the request body for a create
// instance request.
type CreateInstanceRequest struct {
	Provider     string `json:"provider"`
	Image        string `json:"image"`
	InstanceType string `json:"instance_type"`
	PublicSSHKey string `json:"public_ssh_key"`
}
<commit_msg>Add UpstreamID to JSON output<commit_after>package http

import (
	"fmt"
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/travis-ci/cloud-brain/cbcontext"
	"github.com/travis-ci/cloud-brain/cloudbrain"
)

var (
	errCouldntGetInstance = fmt.Errorf("couldn't get instance")
	errNoURLPrefix        = fmt.Errorf("no url prefix")
	errNoURLPath          = fmt.Errorf("no path in url")
	errInstanceIsNil      = fmt.Errorf("instance is nil")
)

func handleInstances(ctx context.Context, core *cloudbrain.Core) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx = cbcontext.FromRequestID(ctx, r.Header.Get("X-Request-ID"))

		switch r.Method {
		case "GET":
			handleInstancesGet(ctx, core, w, r)
		case "POST":
			handleInstancesPost(ctx, core, w, r)
		case "DELETE":
			handleInstancesDelete(ctx, core, w, r)
		default:
			respondError(ctx, w, http.StatusMethodNotAllowed, nil)
		}
	})
}

func handleInstancesGet(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	// Determine the path...
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(ctx, w, http.StatusNotFound, errNoURLPrefix)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(ctx, w, http.StatusNotFound, errNoURLPath)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}
	if instance == nil {
		respondError(ctx, w, http.StatusNotFound, errInstanceIsNil)
		return
	}

	respondOk(ctx, w, instanceToResponse(instance))
}

func handleInstancesPost(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	var req CreateInstanceRequest

	if err := parseRequest(ctx, r, &req); err != nil {
		respondError(ctx, w, http.StatusBadRequest, err)
		return
	}

	instance, err := core.CreateInstance(ctx, req.Provider, cloudbrain.CreateInstanceAttributes{
		ImageName:    req.Image,
		InstanceType: req.InstanceType,
		PublicSSHKey: req.PublicSSHKey,
	})
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}

	respondOk(ctx, w, instanceToResponse(instance))
}

func handleInstancesDelete(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(ctx, w, http.StatusNotFound, errNoURLPrefix)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(ctx, w, http.StatusNotFound, errNoURLPath)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}
	if instance == nil {
		respondError(ctx, w, http.StatusNotFound, errInstanceIsNil)
		return
	}

	err = core.RemoveInstance(ctx, cloudbrain.DeleteInstanceAttributes{
		InstanceID: instance.ID,
	})
	if err != nil {
		cbcontext.CaptureError(ctx, err)
		respondError(ctx, w, http.StatusInternalServerError, err)
		return
	}

	respondOk(ctx, w, nil)
}

func instanceToResponse(instance *cloudbrain.Instance) *InstanceResponse {
	body := &InstanceResponse{
		ID:           instance.ID,
		ProviderName: instance.ProviderName,
		Image:        instance.Image,
		State:        instance.State,
	}
	if instance.IPAddress != "" {
		body.IPAddress = &instance.IPAddress
	}
	if instance.UpstreamID != "" {
		body.UpstreamID = &instance.UpstreamID
	}

	return body
}

// An InstanceResponse is returned by the HTTP API that contains information
// about an instance.
type InstanceResponse struct {
	ID           string  `json:"id"`
	ProviderName string  `json:"provider"`
	Image        string  `json:"image"`
	IPAddress    *string `json:"ip_address"`
	UpstreamID   *string `json:"upstream_id"`
	State        string  `json:"state"`
}

// CreateInstanceRequest contains the data in the request body for a create
// instance request.
type CreateInstanceRequest struct {
	Provider     string `json:"provider"`
	Image        string `json:"image"`
	InstanceType string `json:"instance_type"`
	PublicSSHKey string `json:"public_ssh_key"`
}
<|endoftext|>


<commit_before>package hood

import (
	"fmt"
	_ "github.com/bmizerany/pq"
	"strings"
	"time"
)

func init() {
	RegisterDialect("postgres", &Postgres{})
}

type Postgres struct {
	Base
}

func NewPostgres() Dialect {
	d := &Postgres{}
	d.Base.Dialect = d
	return d
}

func (d *Base) SqlType(f interface{}, size int) string {
	switch f.(type) {
	case Id:
		return "bigserial"
	case VarChar:
		if size < 1 {
			size = 255
		}
		return fmt.Sprintf("varchar(%d)", size)
	case time.Time:
		return "timestamp"
	case bool:
		return "boolean"
	case int, int8, int16, int32, uint, uint8, uint16, uint32:
		return "integer"
	case int64, uint64:
		return "bigint"
	case float32, float64:
		return "double precision"
	case []byte:
		return "bytea"
	case string:
		return "text"
	}
	panic("invalid sql type")
}

func (d *Postgres) Insert(hood *Hood, model *Model) (Id, error) {
	sql, args := d.Dialect.InsertSql(model)
	var id int64
	err := hood.QueryRow(sql, args...).Scan(&id)
	return Id(id), err
}

func (d *Postgres) InsertSql(model *Model) (string, []interface{}) {
	m := 0
	columns, markers, values := columnsMarkersAndValuesForModel(d.Dialect, model, &m)
	sql := fmt.Sprintf(
		"INSERT INTO %v (%v) VALUES (%v) RETURNING %v",
		model.Table,
		strings.Join(columns, ", "),
		strings.Join(markers, ", "),
		model.Pk.Name,
	)
	return sql, values
}

func (d *Postgres) KeywordAutoIncrement() string {
	// postgres has not auto increment keyword, uses SERIAL type
	return ""
}
<commit_msg>fixed init<commit_after>package hood

import (
	"fmt"
	_ "github.com/bmizerany/pq"
	"strings"
	"time"
)

func init() {
	RegisterDialect("postgres", NewPostgres())
}

type Postgres struct {
	Base
}

func NewPostgres() Dialect {
	d := &Postgres{}
	d.Base.Dialect = d
	return d
}

func (d *Base) SqlType(f interface{}, size int) string {
	switch f.(type) {
	case Id:
		return "bigserial"
	case VarChar:
		if size < 1 {
			size = 255
		}
		return fmt.Sprintf("varchar(%d)", size)
	case time.Time:
		return "timestamp"
	case bool:
		return "boolean"
	case int, int8, int16, int32, uint, uint8, uint16, uint32:
		return "integer"
	case int64, uint64:
		return "bigint"
	case float32, float64:
		return "double precision"
	case []byte:
		return "bytea"
	case string:
		return "text"
	}
	panic("invalid sql type")
}

func (d *Postgres) Insert(hood *Hood, model *Model) (Id, error) {
	sql, args := d.Dialect.InsertSql(model)
	var id int64
	err := hood.QueryRow(sql, args...).Scan(&id)
	return Id(id), err
}

func (d *Postgres) InsertSql(model *Model) (string, []interface{}) {
	m := 0
	columns, markers, values := columnsMarkersAndValuesForModel(d.Dialect, model, &m)
	sql := fmt.Sprintf(
		"INSERT INTO %v (%v) VALUES (%v) RETURNING %v",
		model.Table,
		strings.Join(columns, ", "),
		strings.Join(markers, ", "),
		model.Pk.Name,
	)
	return sql, values
}

func (d *Postgres) KeywordAutoIncrement() string {
	// postgres has not auto increment keyword, uses SERIAL type
	return ""
}
<|endoftext|>