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

// Package mock is just for test only.
package mock

import (
	"fmt"

	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx/variable"
)

var _ context.Context = (*Context)(nil)

// Context represents mocked context.Context.
type Context struct {
	values map[fmt.Stringer]interface{}
	// mock global variable
}

// SetValue implements context.Context SetValue interface.
func (c *Context) SetValue(key fmt.Stringer, value interface{}) {
	c.values[key] = value
}

// Value implements context.Context Value interface.
func (c *Context) Value(key fmt.Stringer) interface{} {
	value := c.values[key]
	return value
}

// ClearValue implements context.Context ClearValue interface.
func (c *Context) ClearValue(key fmt.Stringer) {
	delete(c.values, key)
}

// GetTxn implements context.Context GetTxn interface.
func (c *Context) GetTxn(forceNew bool) (kv.Transaction, error) {
	return nil, nil
}

// FinishTxn implements context.Context FinishTxn interface.
func (c *Context) FinishTxn(rollback bool) error {
	return nil
}

// GetGlobalStatusVar implements GlobalVarAccessor GetGlobalStatusVar interface.
func (c *Context) GetGlobalStatusVar(ctx context.Context, name string) (string, error) {
	v := variable.GetStatusVar(name)
	if v == nil {
		return "", nil
	}
	return v.Value, nil
}

// SetGlobalStatusVar implements GlobalVarAccessor SetGlobalStatusVar interface.
func (c *Context) SetGlobalStatusVar(ctx context.Context, name string, value string) error {
	v := variable.GetStatusVar(name)
	if v == nil {
		return fmt.Errorf("Unknown status var: %s", name)
	}
	v.Value = value
	return nil
}

// GetGlobalSysVar implements GlobalVarAccessor GetGlobalSysVar interface.
func (c *Context) GetGlobalSysVar(ctx context.Context, name string) (string, error) {
	v := variable.GetSysVar(name)
	if v == nil {
		return "", nil
	}
	return v.Value, nil
}

// SetGlobalSysVar implements GlobalVarAccessor SetGlobalSysVar interface.
func (c *Context) SetGlobalSysVar(ctx context.Context, name string, value string) error {
	v := variable.GetSysVar(name)
	if v == nil {
		return fmt.Errorf("Unknown sys var: %s", name)
	}
	v.Value = value
	return nil
}

// NewContext creates a new mocked context.Context.
func NewContext() *Context {
	return &Context{
		values: make(map[fmt.Stringer]interface{}),
	}
}
<commit_msg>util: update error<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mock is just for test only.
package mock

import (
	"fmt"

	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx/variable"
	"github.com/pingcap/tidb/terror"
)

var _ context.Context = (*Context)(nil)

// Context represents mocked context.Context.
type Context struct {
	values map[fmt.Stringer]interface{}
	// mock global variable
}

// SetValue implements context.Context SetValue interface.
func (c *Context) SetValue(key fmt.Stringer, value interface{}) {
	c.values[key] = value
}

// Value implements context.Context Value interface.
func (c *Context) Value(key fmt.Stringer) interface{} {
	value := c.values[key]
	return value
}

// ClearValue implements context.Context ClearValue interface.
func (c *Context) ClearValue(key fmt.Stringer) {
	delete(c.values, key)
}

// GetTxn implements context.Context GetTxn interface.
func (c *Context) GetTxn(forceNew bool) (kv.Transaction, error) {
	return nil, nil
}

// FinishTxn implements context.Context FinishTxn interface.
func (c *Context) FinishTxn(rollback bool) error {
	return nil
}

// GetGlobalStatusVar implements GlobalVarAccessor GetGlobalStatusVar interface.
func (c *Context) GetGlobalStatusVar(ctx context.Context, name string) (string, error) {
	v := variable.GetStatusVar(name)
	if v == nil {
		return "", nil
	}
	return v.Value, nil
}

// SetGlobalStatusVar implements GlobalVarAccessor SetGlobalStatusVar interface.
func (c *Context) SetGlobalStatusVar(ctx context.Context, name string, value string) error {
	v := variable.GetStatusVar(name)
	if v == nil {
		return terror.UnknownStatusVar.Gen("unknown status variable: %s", name)
	}
	v.Value = value
	return nil
}

// GetGlobalSysVar implements GlobalVarAccessor GetGlobalSysVar interface.
func (c *Context) GetGlobalSysVar(ctx context.Context, name string) (string, error) {
	v := variable.GetSysVar(name)
	if v == nil {
		return "", nil
	}
	return v.Value, nil
}

// SetGlobalSysVar implements GlobalVarAccessor SetGlobalSysVar interface.
func (c *Context) SetGlobalSysVar(ctx context.Context, name string, value string) error {
	v := variable.GetSysVar(name)
	if v == nil {
		return terror.UnknownStatusVar.Gen("unknown sys variable: %s", name)
	}
	v.Value = value
	return nil
}

// NewContext creates a new mocked context.Context.
func NewContext() *Context {
	return &Context{
		values: make(map[fmt.Stringer]interface{}),
	}
}
<|endoftext|>


<commit_before>// +build !android

package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	platformInfo = make(map[string]interface{})
	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = GetArchInfo()

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	// If this errors, swallow the error.
	// It will usually mean that Python is not on the PATH
	// and we don't care about that.
	pythonV, e := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if e == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<commit_msg>Actually fix the nil map dereference<commit_after>// +build !android

package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = GetArchInfo()
	if platformInfo == nil {
		platformInfo = make(map[string]interface{})
	}

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	// If this errors, swallow the error.
	// It will usually mean that Python is not on the PATH
	// and we don't care about that.
	pythonV, e := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if e == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<|endoftext|>


<commit_before>package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = getArchInfo()

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	pythonV, err := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if err == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<commit_msg>swallow python error for python version (#51)<commit_after>package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = getArchInfo()

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	// If this errors, swallow the error.
	// It will usually mean that Python is not on the PATH
	// and we don't care about that.
	pythonV, e := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if e == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !appengine

// Package socket implements an WebSocket-based playground backend.
// Clients connect to a websocket handler and send run/kill commands, and
// the server sends the output and exit status of the running processes.
// Multiple clients running multiple processes may be served concurrently.
// The wire format is JSON and is described by the Message type.
//
// This will not run on App Engine as WebSockets are not supported there.
package socket

import (
	"bytes"
	"encoding/json"
	"errors"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.net/websocket"
)

// Handler implements a WebSocket handler for a client connection.
var Handler = websocket.Handler(socketHandler)

// Environ provides an environment when a binary, such as the go tool, is
// invoked.
var Environ func() []string = os.Environ

const (
	// The maximum number of messages to send per session (avoid flooding).
	msgLimit = 1000

	// Batch messages sent in this interval and send as a single message.
	msgDelay = 10 * time.Millisecond
)

// Message is the wire format for the websocket connection to the browser.
// It is used for both sending output messages and receiving commands, as
// distinguished by the Kind field.
type Message struct {
	Id      string // client-provided unique id for the process
	Kind    string // in: "run", "kill" out: "stdout", "stderr", "end"
	Body    string
	Options *Options `json:",omitempty"`
}

// Options specify additional message options.
type Options struct {
	Race bool // use -race flag when building code (for "run" only)
}

// socketHandler handles the websocket connection for a given present session.
// It handles transcoding Messages to and from JSON format, and starting
// and killing processes.
func socketHandler(c *websocket.Conn) {
	in, out := make(chan *Message), make(chan *Message)
	errc := make(chan error, 1)

	// Decode messages from client and send to the in channel.
	go func() {
		dec := json.NewDecoder(c)
		for {
			var m Message
			if err := dec.Decode(&m); err != nil {
				errc <- err
				return
			}
			in <- &m
		}
	}()

	// Receive messages from the out channel and encode to the client.
	go func() {
		enc := json.NewEncoder(c)
		for m := range out {
			if err := enc.Encode(m); err != nil {
				errc <- err
				return
			}
		}
	}()

	// Start and kill processes and handle errors.
	proc := make(map[string]*process)
	for {
		select {
		case m := <-in:
			switch m.Kind {
			case "run":
				proc[m.Id].Kill()
				lOut := limiter(in, out)
				proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
			case "kill":
				proc[m.Id].Kill()
			}
		case err := <-errc:
			if err != io.EOF {
				// A encode or decode has failed; bail.
				log.Println(err)
			}
			// Shut down any running processes.
			for _, p := range proc {
				p.Kill()
			}
			return
		}
	}
}

// process represents a running process.
type process struct {
	id   string
	out  chan<- *Message
	done chan struct{} // closed when wait completes
	run  *exec.Cmd
	bin  string
}

// startProcess builds and runs the given program, sending its output
// and end event as Messages on the provided channel.
func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
	p := &process{
		id:   id,
		out:  out,
		done: make(chan struct{}),
	}
	var err error
	if path, args := shebang(body); path != "" {
		err = p.startProcess(path, args, body)
	} else {
		err = p.start(body, opt)
	}
	if err != nil {
		p.end(err)
		return nil
	}
	go p.wait()
	return p
}

// Kill stops the process if it is running and waits for it to exit.
func (p *process) Kill() {
	if p == nil {
		return
	}
	p.run.Process.Kill()
	<-p.done // block until process exits
}

// shebang looks for a shebang ('#!') at the beginning of the passed string.
// If found, it returns the path and args after the shebang.
// args includes the command as args[0].
func shebang(body string) (path string, args []string) {
	body = strings.TrimSpace(body)
	if !strings.HasPrefix(body, "#!") {
		return "", nil
	}
	if i := strings.Index(body, "\n"); i >= 0 {
		body = body[:i]
	}
	fs := strings.Fields(body[2:])
	return fs[0], fs
}

// startProcess starts a given program given its path and passing the given body
// to the command standard input.
func (p *process) startProcess(path string, args []string, body string) error {
	cmd := &exec.Cmd{
		Path:   path,
		Args:   args,
		Stdin:  strings.NewReader(body),
		Stdout: &messageWriter{id: p.id, kind: "stdout", out: p.out},
		Stderr: &messageWriter{id: p.id, kind: "stderr", out: p.out},
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	p.run = cmd
	return nil
}

// start builds and starts the given program, sending its output to p.out,
// and stores the running *exec.Cmd in the run field.
func (p *process) start(body string, opt *Options) error {
	// We "go build" and then exec the binary so that the
	// resultant *exec.Cmd is a handle to the user's program
	// (rather than the go tool process).
	// This makes Kill work.

	bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := bin + ".go"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write body to x.go
	defer os.Remove(src)
	err := ioutil.WriteFile(src, []byte(body), 0666)
	if err != nil {
		return err
	}

	// build x.go, creating x
	p.bin = bin // to be removed by p.end
	dir, file := filepath.Split(src)
	args := []string{"go", "build", "-tags", "OMIT"}
	if opt != nil && opt.Race {
		p.out <- &Message{
			Id: p.id, Kind: "stderr",
			Body: "Running with race detector.\n",
		}
		args = append(args, "-race")
	}
	args = append(args, "-o", bin, file)
	cmd := p.cmd(dir, args...)
	cmd.Stdout = cmd.Stderr // send compiler output to stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	// run x
	cmd = p.cmd("", bin)
	if opt != nil && opt.Race {
		cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
	}
	if err := cmd.Start(); err != nil {
		// If we failed to exec, that might be because they built
		// a non-main package instead of an executable.
		// Check and report that.
		if name, err := packageName(body); err == nil && name != "main" {
			return errors.New(`executable programs must use "package main"`)
		}
		return err
	}
	p.run = cmd
	return nil
}

// wait waits for the running process to complete
// and sends its error state to the client.
func (p *process) wait() {
	p.end(p.run.Wait())
	close(p.done) // unblock waiting Kill calls
}

// end sends an "end" message to the client, containing the process id and the
// given error value. It also removes the binary.
func (p *process) end(err error) {
	if p.bin != "" {
		defer os.Remove(p.bin)
	}
	m := &Message{Id: p.id, Kind: "end"}
	if err != nil {
		m.Body = err.Error()
	}
	// Wait for any outstanding reads to finish (potential race here).
	time.AfterFunc(msgDelay, func() { p.out <- m })
}

// cmd builds an *exec.Cmd that writes its standard output and error to the
// process' output channel.
func (p *process) cmd(dir string, args ...string) *exec.Cmd {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Env = Environ()
	cmd.Stdout = &messageWriter{id: p.id, kind: "stdout", out: p.out}
	cmd.Stderr = &messageWriter{id: p.id, kind: "stderr", out: p.out}
	return cmd
}

func packageName(body string) (string, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "prog.go",
		strings.NewReader(body), parser.PackageClauseOnly)
	if err != nil {
		return "", err
	}
	return f.Name.String(), nil
}

// messageWriter is an io.Writer that converts all writes to Message sends on
// the out channel with the specified id and kind.
type messageWriter struct {
	id, kind string
	out      chan<- *Message

	mu   sync.Mutex
	buf  []byte
	send *time.Timer
}

func (w *messageWriter) Write(b []byte) (n int, err error) {
	// Buffer writes that occur in a short period to send as one Message.
	w.mu.Lock()
	w.buf = append(w.buf, b...)
	if w.send == nil {
		w.send = time.AfterFunc(msgDelay, w.sendNow)
	}
	w.mu.Unlock()
	return len(b), nil
}

func (w *messageWriter) sendNow() {
	w.mu.Lock()
	body := safeString(w.buf)
	w.buf, w.send = nil, nil
	w.mu.Unlock()
	w.out <- &Message{Id: w.id, Kind: w.kind, Body: body}
}

// safeString returns b as a valid UTF-8 string.
func safeString(b []byte) string {
	if utf8.Valid(b) {
		return string(b)
	}
	var buf bytes.Buffer
	for len(b) > 0 {
		r, size := utf8.DecodeRune(b)
		b = b[size:]
		buf.WriteRune(r)
	}
	return buf.String()
}

// limiter returns a channel that wraps dest. Messages sent to the channel are
// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
// is sent to the kill channel, and only "end" messages are passed.
func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
	ch := make(chan *Message)
	go func() {
		n := 0
		for m := range ch {
			switch {
			case n < msgLimit || m.Kind == "end":
				dest <- m
				if m.Kind == "end" {
					return
				}
			case n == msgLimit:
				// process produced too much output. Kill it.
				kill <- &Message{Id: m.Id, Kind: "kill"}
			}
			n++
		}
	}()
	return ch
}

var tmpdir string

func init() {
	// find real path to temporary directory
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

var uniq = make(chan int) // a source of numbers for naming temporary files

func init() {
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()
}
<commit_msg>go.tools/playground: provide script-safe option for playground<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !appengine

// Package socket implements an WebSocket-based playground backend.
// Clients connect to a websocket handler and send run/kill commands, and
// the server sends the output and exit status of the running processes.
// Multiple clients running multiple processes may be served concurrently.
// The wire format is JSON and is described by the Message type.
//
// This will not run on App Engine as WebSockets are not supported there.
package socket

import (
	"bytes"
	"encoding/json"
	"errors"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.net/websocket"
)

// RunScripts specifies whether the socket handler should execute shell scripts
// (snippets that start with a shebang).
var RunScripts = true

// Handler implements a WebSocket handler for a client connection.
var Handler = websocket.Handler(socketHandler)

// Environ provides an environment when a binary, such as the go tool, is
// invoked.
var Environ func() []string = os.Environ

const (
	// The maximum number of messages to send per session (avoid flooding).
	msgLimit = 1000

	// Batch messages sent in this interval and send as a single message.
	msgDelay = 10 * time.Millisecond
)

// Message is the wire format for the websocket connection to the browser.
// It is used for both sending output messages and receiving commands, as
// distinguished by the Kind field.
type Message struct {
	Id      string // client-provided unique id for the process
	Kind    string // in: "run", "kill" out: "stdout", "stderr", "end"
	Body    string
	Options *Options `json:",omitempty"`
}

// Options specify additional message options.
type Options struct {
	Race bool // use -race flag when building code (for "run" only)
}

// socketHandler handles the websocket connection for a given present session.
// It handles transcoding Messages to and from JSON format, and starting
// and killing processes.
func socketHandler(c *websocket.Conn) {
	in, out := make(chan *Message), make(chan *Message)
	errc := make(chan error, 1)

	// Decode messages from client and send to the in channel.
	go func() {
		dec := json.NewDecoder(c)
		for {
			var m Message
			if err := dec.Decode(&m); err != nil {
				errc <- err
				return
			}
			in <- &m
		}
	}()

	// Receive messages from the out channel and encode to the client.
	go func() {
		enc := json.NewEncoder(c)
		for m := range out {
			if err := enc.Encode(m); err != nil {
				errc <- err
				return
			}
		}
	}()

	// Start and kill processes and handle errors.
	proc := make(map[string]*process)
	for {
		select {
		case m := <-in:
			switch m.Kind {
			case "run":
				proc[m.Id].Kill()
				lOut := limiter(in, out)
				proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
			case "kill":
				proc[m.Id].Kill()
			}
		case err := <-errc:
			if err != io.EOF {
				// A encode or decode has failed; bail.
				log.Println(err)
			}
			// Shut down any running processes.
			for _, p := range proc {
				p.Kill()
			}
			return
		}
	}
}

// process represents a running process.
type process struct {
	id   string
	out  chan<- *Message
	done chan struct{} // closed when wait completes
	run  *exec.Cmd
	bin  string
}

// startProcess builds and runs the given program, sending its output
// and end event as Messages on the provided channel.
func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
	p := &process{
		id:   id,
		out:  out,
		done: make(chan struct{}),
	}
	var err error
	if path, args := shebang(body); RunScripts && path != "" {
		err = p.startProcess(path, args, body)
	} else {
		err = p.start(body, opt)
	}
	if err != nil {
		p.end(err)
		return nil
	}
	go p.wait()
	return p
}

// Kill stops the process if it is running and waits for it to exit.
func (p *process) Kill() {
	if p == nil {
		return
	}
	p.run.Process.Kill()
	<-p.done // block until process exits
}

// shebang looks for a shebang ('#!') at the beginning of the passed string.
// If found, it returns the path and args after the shebang.
// args includes the command as args[0].
func shebang(body string) (path string, args []string) {
	body = strings.TrimSpace(body)
	if !strings.HasPrefix(body, "#!") {
		return "", nil
	}
	if i := strings.Index(body, "\n"); i >= 0 {
		body = body[:i]
	}
	fs := strings.Fields(body[2:])
	return fs[0], fs
}

// startProcess starts a given program given its path and passing the given body
// to the command standard input.
func (p *process) startProcess(path string, args []string, body string) error {
	cmd := &exec.Cmd{
		Path:   path,
		Args:   args,
		Stdin:  strings.NewReader(body),
		Stdout: &messageWriter{id: p.id, kind: "stdout", out: p.out},
		Stderr: &messageWriter{id: p.id, kind: "stderr", out: p.out},
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	p.run = cmd
	return nil
}

// start builds and starts the given program, sending its output to p.out,
// and stores the running *exec.Cmd in the run field.
func (p *process) start(body string, opt *Options) error {
	// We "go build" and then exec the binary so that the
	// resultant *exec.Cmd is a handle to the user's program
	// (rather than the go tool process).
	// This makes Kill work.

	bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := bin + ".go"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write body to x.go
	defer os.Remove(src)
	err := ioutil.WriteFile(src, []byte(body), 0666)
	if err != nil {
		return err
	}

	// build x.go, creating x
	p.bin = bin // to be removed by p.end
	dir, file := filepath.Split(src)
	args := []string{"go", "build", "-tags", "OMIT"}
	if opt != nil && opt.Race {
		p.out <- &Message{
			Id: p.id, Kind: "stderr",
			Body: "Running with race detector.\n",
		}
		args = append(args, "-race")
	}
	args = append(args, "-o", bin, file)
	cmd := p.cmd(dir, args...)
	cmd.Stdout = cmd.Stderr // send compiler output to stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	// run x
	cmd = p.cmd("", bin)
	if opt != nil && opt.Race {
		cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
	}
	if err := cmd.Start(); err != nil {
		// If we failed to exec, that might be because they built
		// a non-main package instead of an executable.
		// Check and report that.
		if name, err := packageName(body); err == nil && name != "main" {
			return errors.New(`executable programs must use "package main"`)
		}
		return err
	}
	p.run = cmd
	return nil
}

// wait waits for the running process to complete
// and sends its error state to the client.
func (p *process) wait() {
	p.end(p.run.Wait())
	close(p.done) // unblock waiting Kill calls
}

// end sends an "end" message to the client, containing the process id and the
// given error value. It also removes the binary.
func (p *process) end(err error) {
	if p.bin != "" {
		defer os.Remove(p.bin)
	}
	m := &Message{Id: p.id, Kind: "end"}
	if err != nil {
		m.Body = err.Error()
	}
	// Wait for any outstanding reads to finish (potential race here).
	time.AfterFunc(msgDelay, func() { p.out <- m })
}

// cmd builds an *exec.Cmd that writes its standard output and error to the
// process' output channel.
func (p *process) cmd(dir string, args ...string) *exec.Cmd {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Env = Environ()
	cmd.Stdout = &messageWriter{id: p.id, kind: "stdout", out: p.out}
	cmd.Stderr = &messageWriter{id: p.id, kind: "stderr", out: p.out}
	return cmd
}

func packageName(body string) (string, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "prog.go",
		strings.NewReader(body), parser.PackageClauseOnly)
	if err != nil {
		return "", err
	}
	return f.Name.String(), nil
}

// messageWriter is an io.Writer that converts all writes to Message sends on
// the out channel with the specified id and kind.
type messageWriter struct {
	id, kind string
	out      chan<- *Message

	mu   sync.Mutex
	buf  []byte
	send *time.Timer
}

func (w *messageWriter) Write(b []byte) (n int, err error) {
	// Buffer writes that occur in a short period to send as one Message.
	w.mu.Lock()
	w.buf = append(w.buf, b...)
	if w.send == nil {
		w.send = time.AfterFunc(msgDelay, w.sendNow)
	}
	w.mu.Unlock()
	return len(b), nil
}

func (w *messageWriter) sendNow() {
	w.mu.Lock()
	body := safeString(w.buf)
	w.buf, w.send = nil, nil
	w.mu.Unlock()
	w.out <- &Message{Id: w.id, Kind: w.kind, Body: body}
}

// safeString returns b as a valid UTF-8 string.
func safeString(b []byte) string {
	if utf8.Valid(b) {
		return string(b)
	}
	var buf bytes.Buffer
	for len(b) > 0 {
		r, size := utf8.DecodeRune(b)
		b = b[size:]
		buf.WriteRune(r)
	}
	return buf.String()
}

// limiter returns a channel that wraps dest. Messages sent to the channel are
// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
// is sent to the kill channel, and only "end" messages are passed.
func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
	ch := make(chan *Message)
	go func() {
		n := 0
		for m := range ch {
			switch {
			case n < msgLimit || m.Kind == "end":
				dest <- m
				if m.Kind == "end" {
					return
				}
			case n == msgLimit:
				// process produced too much output. Kill it.
				kill <- &Message{Id: m.Id, Kind: "kill"}
			}
			n++
		}
	}()
	return ch
}

var tmpdir string

func init() {
	// find real path to temporary directory
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

var uniq = make(chan int) // a source of numbers for naming temporary files

func init() {
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()
}
<|endoftext|>


<commit_before><commit_msg>Simpler test<commit_after><|endoftext|>


<commit_before>package socket

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/central-event-system/messenger"
	"github.com/byuoitav/common/status"
	"github.com/byuoitav/common/v2/events"
	"github.com/byuoitav/device-monitoring/localsystem"
	"github.com/fatih/color"
	"github.com/labstack/echo"
)

// H is the socket hub
var H *hub

func init() {
	H = &hub{
		broadcast:  make(chan interface{}),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}

	go H.run()
}

// hub is a socket hub
type hub struct {
	// registered clients
	clients map[*Client]bool

	// inbound messages from clients
	broadcast chan interface{}

	// 'register' requests from clients
	register chan *Client

	// 'unregister' requests from clients
	unregister chan *Client

	messenger *messenger.Messenger
}

// SetMessenger .
func SetMessenger(m *messenger.Messenger) {
	H.messenger = m
}

// GetStatus returns the status of the hub along with the program
func GetStatus(context echo.Context) error {
	log.Printf("Status request from %v", context.Request().RemoteAddr)

	var err error
	stat := status.NewStatus()

	stat.Bin = os.Args[0]
	stat.Uptime = status.GetProgramUptime().String()

	stat.Version, err = status.GetMicroserviceVersion()
	if err != nil {
		stat.StatusCode = status.Sick
		stat.Info["error"] = "failed to open version.txt"
		return context.JSON(http.StatusInternalServerError, stat)
	}

	stat.StatusCode = status.Healthy

	stat.Info["websocket-connections"] = len(H.clients)
	var wsInfo []map[string]interface{}

	for client := range H.clients {
		info := make(map[string]interface{})
		localAddr := client.conn.LocalAddr()
		remoteAddr := client.conn.RemoteAddr()
		info["raw-connection"] = fmt.Sprintf("%s => %s", remoteAddr, localAddr)

		resolvedLocal, err := net.LookupAddr(strings.Split(localAddr.String(), ":")[0])
		if err != nil {
			info["resolve-local-error"] = err.Error()
		}

		resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
		if err != nil {
			info["resolve-remote-error"] = err.Error()
		}
		info["resolved-connection"] = fmt.Sprintf("%s => %s", resolvedRemote, resolvedLocal)

		wsInfo = append(wsInfo, info)
	}

	stat.Info["websocket-info"] = wsInfo

	return context.JSON(http.StatusOK, stat)
}

func (h *hub) WriteToSockets(message interface{}) {
	h.broadcast <- message
}

func (h *hub) run() {
	id := localsystem.MustSystemID()
	// id := "ITB-1010-CP1"
	deviceInfo := events.GenerateBasicDeviceInfo(id)
	roomInfo := events.GenerateBasicRoomInfo(deviceInfo.RoomID)

	for {
		select {
		case client := <-h.register:
			remoteAddr := client.conn.RemoteAddr()

			h.clients[client] = true

			color.Set(color.FgYellow, color.Bold)
			log.Printf("New socket connection: %s", remoteAddr)
			color.Unset()

			event := events.Event{
				GeneratingSystem: id,
				Timestamp:        time.Now(),
				EventTags:        []string{events.DetailState},
				TargetDevice:     deviceInfo,
				AffectedRoom:     roomInfo,
				User:             remoteAddr.String(),
				Key:              "websocket",
				Value:            fmt.Sprintf("opened with %s", remoteAddr),
			}

			countEvent := events.Event{
				GeneratingSystem: id,
				Timestamp:        time.Now(),
				EventTags:        []string{events.DetailState},
				TargetDevice:     deviceInfo,
				AffectedRoom:     roomInfo,
				Key:              "websocket-count",
				Value:            fmt.Sprintf("%v", len(h.clients)),
			}

			resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
			if err == nil {
				event.Value = fmt.Sprintf("opened with %s", resolvedRemote)
				event.User = fmt.Sprintf("%s", resolvedRemote)
			}

			if h.messenger != nil {
				h.messenger.SendEvent(event)
				h.messenger.SendEvent(countEvent)
			}
		case client := <-h.unregister:
			if _, ok := h.clients[client]; ok {
				remoteAddr := client.conn.RemoteAddr()

				delete(h.clients, client)
				close(client.send)

				color.Set(color.FgYellow, color.Bold)
				log.Printf("Removed socket connection: %s", remoteAddr)
				color.Unset()

				event := events.Event{
					GeneratingSystem: id,
					Timestamp:        time.Now(),
					EventTags:        []string{events.DetailState},
					TargetDevice:     deviceInfo,
					AffectedRoom:     roomInfo,
					User:             remoteAddr.String(),
					Key:              "websocket",
					Value:            fmt.Sprintf("closed with %s", remoteAddr),
				}

				countEvent := events.Event{
					GeneratingSystem: id,
					Timestamp:        time.Now(),
					EventTags:        []string{events.DetailState},
					TargetDevice:     deviceInfo,
					AffectedRoom:     roomInfo,
					Key:              "websocket-count",
					Value:            fmt.Sprintf("%v", len(h.clients)),
				}

				resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
				if err == nil {
					event.Value = fmt.Sprintf("closed with %s", resolvedRemote)
					event.User = fmt.Sprintf("%s", resolvedRemote)
				}

				if h.messenger != nil {
					h.messenger.SendEvent(event)
					h.messenger.SendEvent(countEvent)
				}
			}
		case message := <-h.broadcast:
			for client := range h.clients {
				select {
				case client.send <- message:
				default:
					close(client.send)
					delete(h.clients, client)
				}
			}
		}
	}
}
<commit_msg>pushing up the websocket count every 5 minutes<commit_after>package socket

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/byuoitav/central-event-system/messenger"
	logger "github.com/byuoitav/common/log"
	"github.com/byuoitav/common/status"
	"github.com/byuoitav/common/v2/events"
	"github.com/byuoitav/device-monitoring/localsystem"
	"github.com/fatih/color"
	"github.com/labstack/echo"
)

// H is the socket hub
var H *hub

func init() {
	H = &hub{
		broadcast:  make(chan interface{}),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}

	go H.run()
}

// hub is a socket hub
type hub struct {
	// registered clients
	clients map[*Client]bool

	// inbound messages from clients
	broadcast chan interface{}

	// 'register' requests from clients
	register chan *Client

	// 'unregister' requests from clients
	unregister chan *Client

	messenger *messenger.Messenger
}

// SetMessenger .
func SetMessenger(m *messenger.Messenger) {
	H.messenger = m
}

// GetStatus returns the status of the hub along with the program
func GetStatus(context echo.Context) error {
	log.Printf("Status request from %v", context.Request().RemoteAddr)

	var err error
	stat := status.NewStatus()

	stat.Bin = os.Args[0]
	stat.Uptime = status.GetProgramUptime().String()

	stat.Version, err = status.GetMicroserviceVersion()
	if err != nil {
		stat.StatusCode = status.Sick
		stat.Info["error"] = "failed to open version.txt"
		return context.JSON(http.StatusInternalServerError, stat)
	}

	stat.StatusCode = status.Healthy

	stat.Info["websocket-connections"] = len(H.clients)
	var wsInfo []map[string]interface{}

	for client := range H.clients {
		info := make(map[string]interface{})
		localAddr := client.conn.LocalAddr()
		remoteAddr := client.conn.RemoteAddr()
		info["raw-connection"] = fmt.Sprintf("%s => %s", remoteAddr, localAddr)

		resolvedLocal, err := net.LookupAddr(strings.Split(localAddr.String(), ":")[0])
		if err != nil {
			info["resolve-local-error"] = err.Error()
		}

		resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
		if err != nil {
			info["resolve-remote-error"] = err.Error()
		}
		info["resolved-connection"] = fmt.Sprintf("%s => %s", resolvedRemote, resolvedLocal)

		wsInfo = append(wsInfo, info)
	}

	stat.Info["websocket-info"] = wsInfo

	return context.JSON(http.StatusOK, stat)
}

func (h *hub) WriteToSockets(message interface{}) {
	h.broadcast <- message
}

func (h *hub) run() {
	id := localsystem.MustSystemID()
	// id := "ITB-1010-CP1"
	deviceInfo := events.GenerateBasicDeviceInfo(id)
	roomInfo := events.GenerateBasicRoomInfo(deviceInfo.RoomID)

	go h.reportWebSocketCount()

	for {
		select {
		case client := <-h.register:
			remoteAddr := client.conn.RemoteAddr()

			h.clients[client] = true

			color.Set(color.FgYellow, color.Bold)
			log.Printf("New socket connection: %s", remoteAddr)
			color.Unset()

			event := events.Event{
				GeneratingSystem: id,
				Timestamp:        time.Now(),
				EventTags:        []string{events.DetailState},
				TargetDevice:     deviceInfo,
				AffectedRoom:     roomInfo,
				User:             remoteAddr.String(),
				Key:              "websocket",
				Value:            fmt.Sprintf("opened with %s", remoteAddr),
			}

			countEvent := events.Event{
				GeneratingSystem: id,
				Timestamp:        time.Now(),
				EventTags:        []string{events.DetailState},
				TargetDevice:     deviceInfo,
				AffectedRoom:     roomInfo,
				Key:              "websocket-count",
				Value:            fmt.Sprintf("%v", len(h.clients)),
			}

			resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
			if err == nil {
				event.Value = fmt.Sprintf("opened with %s", resolvedRemote)
				event.User = fmt.Sprintf("%s", resolvedRemote)
			}

			if h.messenger != nil {
				h.messenger.SendEvent(event)
				h.messenger.SendEvent(countEvent)
			}
		case client := <-h.unregister:
			if _, ok := h.clients[client]; ok {
				remoteAddr := client.conn.RemoteAddr()

				delete(h.clients, client)
				close(client.send)

				color.Set(color.FgYellow, color.Bold)
				log.Printf("Removed socket connection: %s", remoteAddr)
				color.Unset()

				event := events.Event{
					GeneratingSystem: id,
					Timestamp:        time.Now(),
					EventTags:        []string{events.DetailState},
					TargetDevice:     deviceInfo,
					AffectedRoom:     roomInfo,
					User:             remoteAddr.String(),
					Key:              "websocket",
					Value:            fmt.Sprintf("closed with %s", remoteAddr),
				}

				countEvent := events.Event{
					GeneratingSystem: id,
					Timestamp:        time.Now(),
					EventTags:        []string{events.DetailState},
					TargetDevice:     deviceInfo,
					AffectedRoom:     roomInfo,
					Key:              "websocket-count",
					Value:            fmt.Sprintf("%v", len(h.clients)),
				}

				resolvedRemote, err := net.LookupAddr(strings.Split(remoteAddr.String(), ":")[0])
				if err == nil {
					event.Value = fmt.Sprintf("closed with %s", resolvedRemote)
					event.User = fmt.Sprintf("%s", resolvedRemote)
				}

				if h.messenger != nil {
					h.messenger.SendEvent(event)
					h.messenger.SendEvent(countEvent)
				}
			}
		case message := <-h.broadcast:
			for client := range h.clients {
				select {
				case client.send <- message:
				default:
					close(client.send)
					delete(h.clients, client)
				}
			}
		}
	}
}

func (h *hub) reportWebSocketCount() {
	id := localsystem.MustSystemID()
	// id := "ITB-1010-CP1"
	deviceInfo := events.GenerateBasicDeviceInfo(id)
	roomInfo := events.GenerateBasicRoomInfo(deviceInfo.RoomID)

	for {
		logger.L.Debugf("sending websocket count of: %d", len(h.clients))
		countEvent := events.Event{
			GeneratingSystem: id,
			Timestamp:        time.Now(),
			EventTags:        []string{events.DetailState},
			TargetDevice:     deviceInfo,
			AffectedRoom:     roomInfo,
			Key:              "websocket-count",
			Value:            fmt.Sprintf("%v", len(h.clients)),
		}

		if h.messenger != nil {
			h.messenger.SendEvent(countEvent)
		}

		time.Sleep(3 * time.Minute)
	}
}
<|endoftext|>


<commit_before>package vsolver

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
)

var fixtorun string

// TODO regression test ensuring that locks with only revs for projects don't cause errors
func init() {
	flag.StringVar(&fixtorun, "vsolver.fix", "", "A single fixture to run in TestBasicSolves")
}

var stderrlog = log.New(os.Stderr, "", 0)

func fixSolve(o SolveOpts, sm SourceManager) (Result, error) {
	if testing.Verbose() {
		o.Trace = true
		o.TraceLogger = stderrlog
	}

	s, err := prepareSolver(o, sm)
	if err != nil {
		return nil, err
	}

	fixb := &depspecBridge{
		s.b.(*bridge),
	}
	s.b = fixb

	return s.run()
}

// Test all the basic table fixtures.
//
// Or, just the one named in the fix arg.
func TestBasicSolves(t *testing.T) {
	for _, fix := range basicFixtures {
		if fixtorun == "" || fixtorun == fix.n {
			solveBasicsAndCheck(fix, t)
			if testing.Verbose() {
				// insert a line break between tests
				stderrlog.Println("")
			}
		}
	}
}

func solveBasicsAndCheck(fix basicFixture, t *testing.T) (res Result, err error) {
	sm := newdepspecSM(fix.ds, fix.rm)

	o := SolveOpts{
		Root:      string(fix.ds[0].Name()),
		N:         ProjectName(fix.ds[0].Name()),
		M:         fix.ds[0],
		L:         dummyLock{},
		Downgrade: fix.downgrade,
		ChangeAll: fix.changeall,
	}

	if fix.l != nil {
		o.L = fix.l
	}

	if testing.Verbose() {
		stderrlog.Printf("[[fixture %q]]", fix.n)
	}
	res, err = fixSolve(o, sm)

	return fixtureSolveSimpleChecks(fix, res, err, t)
}

// Test all the bimodal table fixtures.
//
// Or, just the one named in the fix arg.
func TestBimodalSolves(t *testing.T) {
	for _, fix := range bimodalFixtures {
		if fixtorun == "" || fixtorun == fix.n {
			solveBimodalAndCheck(fix, t)
			if testing.Verbose() {
				// insert a line break between tests
				stderrlog.Println("")
			}
		}
	}
}

func solveBimodalAndCheck(fix bimodalFixture, t *testing.T) (res Result, err error) {
	sm := newbmSM(fix.ds)

	o := SolveOpts{
		Root:      string(fix.ds[0].Name()),
		N:         ProjectName(fix.ds[0].Name()),
		M:         fix.ds[0],
		L:         dummyLock{},
		Downgrade: fix.downgrade,
		ChangeAll: fix.changeall,
	}

	if fix.l != nil {
		o.L = fix.l
	}

	if testing.Verbose() {
		stderrlog.Printf("[[fixture %q]]", fix.n)
	}
	res, err = fixSolve(o, sm)

	return fixtureSolveSimpleChecks(fix, res, err, t)
}

func fixtureSolveSimpleChecks(fix specfix, res Result, err error, t *testing.T) (Result, error) {
	if err != nil {
		errp := fix.expectErrs()
		if len(errp) == 0 {
			t.Errorf("(fixture: %q) Solver failed; error was type %T, text: %q", fix.name(), err, err)
			return res, err
		}

		switch fail := err.(type) {
		case *BadOptsFailure:
			t.Errorf("(fixture: %q) Unexpected bad opts failure solve error: %s", fix.name(), err)
		case *noVersionError:
			if errp[0] != string(fail.pn.LocalName) { // TODO identifierify
				t.Errorf("(fixture: %q) Expected failure on project %s, but was on project %s", fix.name(), fail.pn.LocalName, errp[0])
			}

			ep := make(map[string]struct{})
			for _, p := range errp[1:] {
				ep[p] = struct{}{}
			}

			found := make(map[string]struct{})
			for _, vf := range fail.fails {
				for _, f := range getFailureCausingProjects(vf.f) {
					found[f] = struct{}{}
				}
			}

			var missing []string
			var extra []string
			for p, _ := range found {
				if _, has := ep[p]; !has {
					extra = append(extra, p)
				}
			}
			if len(extra) > 0 {
				t.Errorf("(fixture: %q) Expected solve failures due to projects %s, but solve failures also arose from %s", fix.name(), strings.Join(errp[1:], ", "), strings.Join(extra, ", "))
			}

			for p, _ := range ep {
				if _, has := found[p]; !has {
					missing = append(missing, p)
				}
			}
			if len(missing) > 0 {
				t.Errorf("(fixture: %q) Expected solve failures due to projects %s, but %s had no failures", fix.name(), strings.Join(errp[1:], ", "), strings.Join(missing, ", "))
			}

		default:
			// TODO round these out
			panic(fmt.Sprintf("unhandled solve failure type: %s", err))
		}
	} else if len(fix.expectErrs()) > 0 {
		t.Errorf("(fixture: %q) Solver succeeded, but expected failure", fix.name())
	} else {
		r := res.(result)
		if fix.maxTries() > 0 && r.att > fix.maxTries() {
			t.Errorf("(fixture: %q) Solver completed in %v attempts, but expected %v or fewer", fix.name(), r.att, fix.maxTries())
		}

		// Dump result projects into a map for easier interrogation
		rp := make(map[string]Version)
		for _, p := range r.p {
			pa := p.toAtom()
			rp[string(pa.Ident.LocalName)] = pa.Version
		}

		fixlen, rlen := len(fix.result()), len(rp)
		if fixlen != rlen {
			// Different length, so they definitely disagree
			t.Errorf("(fixture: %q) Solver reported %v package results, result expected %v", fix.name(), rlen, fixlen)
		}

		// Whether or not len is same, still have to verify that results agree
		// Walk through fixture/expected results first
		for p, v := range fix.result() {
			if av, exists := rp[p]; !exists {
				t.Errorf("(fixture: %q) Project %q expected but missing from results", fix.name(), p)
			} else {
				// delete result from map so we skip it on the reverse pass
				delete(rp, p)
				if v != av {
					t.Errorf("(fixture: %q) Expected version %q of project %q, but actual version was %q", fix.name(), v, p, av)
				}
			}
		}

		// Now walk through remaining actual results
		for p, v := range rp {
			if fv, exists := fix.result()[p]; !exists {
				t.Errorf("(fixture: %q) Unexpected project %q present in results", fix.name(), p)
			} else if v != fv {
				t.Errorf("(fixture: %q) Got version %q of project %q, but expected version was %q", fix.name(), v, p, fv)
			}
		}
	}

	return res, err
}

// This tests that, when a root lock is underspecified (has only a version) we
// don't allow a match on that version from a rev in the manifest. We may allow
// this in the future, but disallow it for now because going from an immutable
// requirement to a mutable lock automagically is a bad direction that could
// produce weird side effects.
func TestRootLockNoVersionPairMatching(t *testing.T) {
	fix := basicFixture{
		n: "does not pair bare revs in manifest with unpaired lock version",
		ds: []depspec{
			dsv("root 0.0.0", "foo *"), // foo's constraint rewritten below to foorev
			dsv("foo 1.0.0", "bar 1.0.0"),
			dsv("foo 1.0.1 foorev", "bar 1.0.1"),
			dsv("foo 1.0.2 foorev", "bar 1.0.2"),
			dsv("bar 1.0.0"),
			dsv("bar 1.0.1"),
			dsv("bar 1.0.2"),
		},
		l: mklock(
			"foo 1.0.1",
		),
		r: mkresults(
			"foo 1.0.2 foorev",
			"bar 1.0.1",
		),
	}

	pd := fix.ds[0].deps[0]
	pd.Constraint = Revision("foorev")
	fix.ds[0].deps[0] = pd
	fix.rm = computeReachMap(fix.ds)

	sm := newdepspecSM(fix.ds, fix.rm)

	l2 := make(fixLock, 1)
	copy(l2, fix.l)
	l2[0].v = nil

	o := SolveOpts{
		Root: string(fix.ds[0].Name()),
		N:    ProjectName(fix.ds[0].Name()),
		M:    fix.ds[0],
		L:    l2,
	}

	res, err := fixSolve(o, sm)

	fixtureSolveSimpleChecks(fix, res, err, t)
}

func getFailureCausingProjects(err error) (projs []string) {
	switch e := err.(type) {
	case *noVersionError:
		projs = append(projs, string(e.pn.LocalName)) // TODO identifierify
	case *disjointConstraintFailure:
		for _, f := range e.failsib {
			projs = append(projs, string(f.Depender.Ident.LocalName))
		}
	case *versionNotAllowedFailure:
		for _, f := range e.failparent {
			projs = append(projs, string(f.Depender.Ident.LocalName))
		}
	case *constraintNotAllowedFailure:
		// No sane way of knowing why the currently selected version is
		// selected, so do nothing
	case *sourceMismatchFailure:
		projs = append(projs, string(e.prob.Ident.LocalName))
		for _, c := range e.sel {
			projs = append(projs, string(c.Depender.Ident.LocalName))
		}
	default:
		panic("unknown failtype")
	}

	return
}

func TestBadSolveOpts(t *testing.T) {
	sm := newdepspecSM(basicFixtures[0].ds, basicFixtures[0].rm)

	o := SolveOpts{}
	_, err := fixSolve(o, sm)
	if err == nil {
		t.Errorf("Should have errored on missing manifest")
	}

	p, _ := sm.GetProjectInfo(basicFixtures[0].ds[0].n, basicFixtures[0].ds[0].v)
	o.M = p.Manifest
	_, err = fixSolve(o, sm)
	if err == nil {
		t.Errorf("Should have errored on empty root")
	}

	o.Root = "root"
	_, err = fixSolve(o, sm)
	if err == nil {
		t.Errorf("Should have errored on empty name")
	}

	o.N = "root"
	_, err = fixSolve(o, sm)
	if err != nil {
		t.Errorf("Basic conditions satisfied, solve should have gone through, err was %s", err)
	}

	o.Trace = true
	_, err = fixSolve(o, sm)
	if err == nil {
		t.Errorf("Should have errored on trace with no logger")
	}

	o.TraceLogger = log.New(ioutil.Discard, "", 0)
	_, err = fixSolve(o, sm)
	if err != nil {
		t.Errorf("Basic conditions re-satisfied, solve should have gone through, err was %s", err)
	}

}
<commit_msg>Make SolveOpts test just use prepareSolver()<commit_after>package vsolver

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
)

var fixtorun string

// TODO regression test ensuring that locks with only revs for projects don't cause errors
func init() {
	flag.StringVar(&fixtorun, "vsolver.fix", "", "A single fixture to run in TestBasicSolves")
}

var stderrlog = log.New(os.Stderr, "", 0)

func fixSolve(o SolveOpts, sm SourceManager) (Result, error) {
	if testing.Verbose() {
		o.Trace = true
		o.TraceLogger = stderrlog
	}

	s, err := prepareSolver(o, sm)
	if err != nil {
		return nil, err
	}

	fixb := &depspecBridge{
		s.b.(*bridge),
	}
	s.b = fixb

	return s.run()
}

// Test all the basic table fixtures.
//
// Or, just the one named in the fix arg.
func TestBasicSolves(t *testing.T) {
	for _, fix := range basicFixtures {
		if fixtorun == "" || fixtorun == fix.n {
			solveBasicsAndCheck(fix, t)
			if testing.Verbose() {
				// insert a line break between tests
				stderrlog.Println("")
			}
		}
	}
}

func solveBasicsAndCheck(fix basicFixture, t *testing.T) (res Result, err error) {
	sm := newdepspecSM(fix.ds, fix.rm)

	o := SolveOpts{
		Root:      string(fix.ds[0].Name()),
		N:         ProjectName(fix.ds[0].Name()),
		M:         fix.ds[0],
		L:         dummyLock{},
		Downgrade: fix.downgrade,
		ChangeAll: fix.changeall,
	}

	if fix.l != nil {
		o.L = fix.l
	}

	if testing.Verbose() {
		stderrlog.Printf("[[fixture %q]]", fix.n)
	}
	res, err = fixSolve(o, sm)

	return fixtureSolveSimpleChecks(fix, res, err, t)
}

// Test all the bimodal table fixtures.
//
// Or, just the one named in the fix arg.
func TestBimodalSolves(t *testing.T) {
	for _, fix := range bimodalFixtures {
		if fixtorun == "" || fixtorun == fix.n {
			solveBimodalAndCheck(fix, t)
			if testing.Verbose() {
				// insert a line break between tests
				stderrlog.Println("")
			}
		}
	}
}

func solveBimodalAndCheck(fix bimodalFixture, t *testing.T) (res Result, err error) {
	sm := newbmSM(fix.ds)

	o := SolveOpts{
		Root:      string(fix.ds[0].Name()),
		N:         ProjectName(fix.ds[0].Name()),
		M:         fix.ds[0],
		L:         dummyLock{},
		Downgrade: fix.downgrade,
		ChangeAll: fix.changeall,
	}

	if fix.l != nil {
		o.L = fix.l
	}

	if testing.Verbose() {
		stderrlog.Printf("[[fixture %q]]", fix.n)
	}
	res, err = fixSolve(o, sm)

	return fixtureSolveSimpleChecks(fix, res, err, t)
}

func fixtureSolveSimpleChecks(fix specfix, res Result, err error, t *testing.T) (Result, error) {
	if err != nil {
		errp := fix.expectErrs()
		if len(errp) == 0 {
			t.Errorf("(fixture: %q) Solver failed; error was type %T, text: %q", fix.name(), err, err)
			return res, err
		}

		switch fail := err.(type) {
		case *BadOptsFailure:
			t.Errorf("(fixture: %q) Unexpected bad opts failure solve error: %s", fix.name(), err)
		case *noVersionError:
			if errp[0] != string(fail.pn.LocalName) { // TODO identifierify
				t.Errorf("(fixture: %q) Expected failure on project %s, but was on project %s", fix.name(), fail.pn.LocalName, errp[0])
			}

			ep := make(map[string]struct{})
			for _, p := range errp[1:] {
				ep[p] = struct{}{}
			}

			found := make(map[string]struct{})
			for _, vf := range fail.fails {
				for _, f := range getFailureCausingProjects(vf.f) {
					found[f] = struct{}{}
				}
			}

			var missing []string
			var extra []string
			for p, _ := range found {
				if _, has := ep[p]; !has {
					extra = append(extra, p)
				}
			}
			if len(extra) > 0 {
				t.Errorf("(fixture: %q) Expected solve failures due to projects %s, but solve failures also arose from %s", fix.name(), strings.Join(errp[1:], ", "), strings.Join(extra, ", "))
			}

			for p, _ := range ep {
				if _, has := found[p]; !has {
					missing = append(missing, p)
				}
			}
			if len(missing) > 0 {
				t.Errorf("(fixture: %q) Expected solve failures due to projects %s, but %s had no failures", fix.name(), strings.Join(errp[1:], ", "), strings.Join(missing, ", "))
			}

		default:
			// TODO round these out
			panic(fmt.Sprintf("unhandled solve failure type: %s", err))
		}
	} else if len(fix.expectErrs()) > 0 {
		t.Errorf("(fixture: %q) Solver succeeded, but expected failure", fix.name())
	} else {
		r := res.(result)
		if fix.maxTries() > 0 && r.att > fix.maxTries() {
			t.Errorf("(fixture: %q) Solver completed in %v attempts, but expected %v or fewer", fix.name(), r.att, fix.maxTries())
		}

		// Dump result projects into a map for easier interrogation
		rp := make(map[string]Version)
		for _, p := range r.p {
			pa := p.toAtom()
			rp[string(pa.Ident.LocalName)] = pa.Version
		}

		fixlen, rlen := len(fix.result()), len(rp)
		if fixlen != rlen {
			// Different length, so they definitely disagree
			t.Errorf("(fixture: %q) Solver reported %v package results, result expected %v", fix.name(), rlen, fixlen)
		}

		// Whether or not len is same, still have to verify that results agree
		// Walk through fixture/expected results first
		for p, v := range fix.result() {
			if av, exists := rp[p]; !exists {
				t.Errorf("(fixture: %q) Project %q expected but missing from results", fix.name(), p)
			} else {
				// delete result from map so we skip it on the reverse pass
				delete(rp, p)
				if v != av {
					t.Errorf("(fixture: %q) Expected version %q of project %q, but actual version was %q", fix.name(), v, p, av)
				}
			}
		}

		// Now walk through remaining actual results
		for p, v := range rp {
			if fv, exists := fix.result()[p]; !exists {
				t.Errorf("(fixture: %q) Unexpected project %q present in results", fix.name(), p)
			} else if v != fv {
				t.Errorf("(fixture: %q) Got version %q of project %q, but expected version was %q", fix.name(), v, p, fv)
			}
		}
	}

	return res, err
}

// This tests that, when a root lock is underspecified (has only a version) we
// don't allow a match on that version from a rev in the manifest. We may allow
// this in the future, but disallow it for now because going from an immutable
// requirement to a mutable lock automagically is a bad direction that could
// produce weird side effects.
func TestRootLockNoVersionPairMatching(t *testing.T) {
	fix := basicFixture{
		n: "does not pair bare revs in manifest with unpaired lock version",
		ds: []depspec{
			dsv("root 0.0.0", "foo *"), // foo's constraint rewritten below to foorev
			dsv("foo 1.0.0", "bar 1.0.0"),
			dsv("foo 1.0.1 foorev", "bar 1.0.1"),
			dsv("foo 1.0.2 foorev", "bar 1.0.2"),
			dsv("bar 1.0.0"),
			dsv("bar 1.0.1"),
			dsv("bar 1.0.2"),
		},
		l: mklock(
			"foo 1.0.1",
		),
		r: mkresults(
			"foo 1.0.2 foorev",
			"bar 1.0.1",
		),
	}

	pd := fix.ds[0].deps[0]
	pd.Constraint = Revision("foorev")
	fix.ds[0].deps[0] = pd
	fix.rm = computeReachMap(fix.ds)

	sm := newdepspecSM(fix.ds, fix.rm)

	l2 := make(fixLock, 1)
	copy(l2, fix.l)
	l2[0].v = nil

	o := SolveOpts{
		Root: string(fix.ds[0].Name()),
		N:    ProjectName(fix.ds[0].Name()),
		M:    fix.ds[0],
		L:    l2,
	}

	res, err := fixSolve(o, sm)

	fixtureSolveSimpleChecks(fix, res, err, t)
}

func getFailureCausingProjects(err error) (projs []string) {
	switch e := err.(type) {
	case *noVersionError:
		projs = append(projs, string(e.pn.LocalName)) // TODO identifierify
	case *disjointConstraintFailure:
		for _, f := range e.failsib {
			projs = append(projs, string(f.Depender.Ident.LocalName))
		}
	case *versionNotAllowedFailure:
		for _, f := range e.failparent {
			projs = append(projs, string(f.Depender.Ident.LocalName))
		}
	case *constraintNotAllowedFailure:
		// No sane way of knowing why the currently selected version is
		// selected, so do nothing
	case *sourceMismatchFailure:
		projs = append(projs, string(e.prob.Ident.LocalName))
		for _, c := range e.sel {
			projs = append(projs, string(c.Depender.Ident.LocalName))
		}
	default:
		panic("unknown failtype")
	}

	return
}

func TestBadSolveOpts(t *testing.T) {
	sm := newdepspecSM(basicFixtures[0].ds, basicFixtures[0].rm)

	o := SolveOpts{}
	_, err := prepareSolver(o, sm)
	if err == nil {
		t.Errorf("Should have errored on missing manifest")
	}

	p, _ := sm.GetProjectInfo(basicFixtures[0].ds[0].n, basicFixtures[0].ds[0].v)
	o.M = p.Manifest
	_, err = prepareSolver(o, sm)
	if err == nil {
		t.Errorf("Should have errored on empty root")
	}

	o.Root = "root"
	_, err = prepareSolver(o, sm)
	if err == nil {
		t.Errorf("Should have errored on empty name")
	}

	o.N = "root"
	_, err = prepareSolver(o, sm)
	if err != nil {
		t.Errorf("Basic conditions satisfied, solve should have gone through, err was %s", err)
	}

	o.Trace = true
	_, err = prepareSolver(o, sm)
	if err == nil {
		t.Errorf("Should have errored on trace with no logger")
	}

	o.TraceLogger = log.New(ioutil.Discard, "", 0)
	_, err = prepareSolver(o, sm)
	if err != nil {
		t.Errorf("Basic conditions re-satisfied, solve should have gone through, err was %s", err)
	}
}
<|endoftext|>


<commit_before>package validate

import (
	"errors"
	"fmt"
	"github.com/graniticio/granitic/ioc"
	"github.com/graniticio/granitic/logging"
	"github.com/graniticio/granitic/types"
	"strings"
)

type ValidationRuleType uint

type parseAndBuild func(string, []string) (Validator, error)

const (
	UnknownRuleType = iota
	StringRule
)

const commandSep = ":"
const escapedCommandSep = "::"
const escapedCommandReplace = "||ESC||"
const StringRuleCode = "STR"
const RuleRefCode = "RULE"

type SubjectContext struct {
	Subject        interface{}
	KnownSetFields types.StringSet
}

type validationContext struct {
	Subject        interface{}
	KnownSetFields types.StringSet
}

type ValidationResult struct {
	ErrorCodes []string
	Unset      bool
}

type Validator interface {
	Validate(vc *validationContext) (result *ValidationResult, unexpected error)
	StopAllOnFail() bool
	CodesInUse() types.StringSet
	DependsOnFields() types.StringSet
}

type validatorLink struct {
	validator Validator
	field     string
}

type UnparsedRuleManager struct {
	Rules map[string][]string
}

func (rm *UnparsedRuleManager) Exists(ref string) bool {
	return rm.Rules[ref] != nil
}

func (rm *UnparsedRuleManager) Rule(ref string) []string {
	return rm.Rules[ref]
}

type FieldErrors struct {
	Field      string
	ErrorCodes []string
}

type ObjectValidator struct {
	jsonConfig       interface{}
	RuleManager      *UnparsedRuleManager
	stringBuilder    *stringValidatorBuilder
	DefaultErrorCode string
	Rules            [][]string
	ComponentFinder  ioc.ComponentByNameFinder
	validatorChain   []*validatorLink
	componentName    string
	codesInUse       types.StringSet
	Log              logging.Logger
}

func (ov *ObjectValidator) Container(container *ioc.ComponentContainer) {
	ov.ComponentFinder = container
}

func (ov *ObjectValidator) ComponentName() string {
	return ov.componentName
}

func (ov *ObjectValidator) SetComponentName(name string) {
	ov.componentName = name
}

func (ov *ObjectValidator) ErrorCodesInUse() (codes types.StringSet, sourceName string) {
	return ov.codesInUse, ov.componentName
}

func (ov *ObjectValidator) Validate(subject *SubjectContext) ([]*FieldErrors, error) {

	log := ov.Log

	fieldErrors := make([]*FieldErrors, 0)
	fieldsWithProblems := types.NewOrderedStringSet([]string{})
	unsetFields := types.NewOrderedStringSet([]string{})

	for _, vl := range ov.validatorChain {

		f := vl.field

		log.LogDebugf("Validating field %s", f)

		vc := new(validationContext)
		vc.Subject = subject.Subject
		vc.KnownSetFields = subject.KnownSetFields

		r, err := vl.validator.Validate(vc)
		ec := r.ErrorCodes

		if err != nil {
			return nil, err
		}

		if r.Unset {
			log.LogDebugf("%s is unset", f)
			unsetFields.Add(f)
		}

		l := len(ec)

		if ec != nil && l > 0 {

			fieldsWithProblems.Add(f)
			log.LogDebugf("%s has %d errors", f, l)

			fe := new(FieldErrors)
			fe.Field = f
			fe.ErrorCodes = ec

			fieldErrors = append(fieldErrors, fe)

			if vl.validator.StopAllOnFail() {
				break
			}

		}

	}

	return fieldErrors, nil

}

func (ov *ObjectValidator) StartComponent() error {

	if ov.Rules == nil {
		return errors.New("No Rules specified for validator.")
	}

	ov.codesInUse = types.NewUnorderedStringSet([]string{})

	if ov.DefaultErrorCode != "" {
		ov.codesInUse.Add(ov.DefaultErrorCode)
	}

	ov.stringBuilder = newStringValidatorBuilder(ov.DefaultErrorCode)
	ov.stringBuilder.componentFinder = ov.ComponentFinder

	ov.validatorChain = make([]*validatorLink, 0)

	return ov.parseRules()

}

func (ov *ObjectValidator) parseRules() error {

	var err error

	for _, rule := range ov.Rules {

		var ruleToParse []string

		if len(rule) < 2 {
			m := fmt.Sprintf("Rule is invlaid (must have at least an identifier and a type). Supplied rule is: %q", rule)
			return errors.New(m)
		}

		field := rule[0]
		ruleType := rule[1]

		if ov.isRuleRef(ruleType) {
			ruleToParse, err = ov.findRule(field, ruleType)

			if err != nil {
				break
			}

		} else {
			ruleToParse = rule[1:]
		}

		err = ov.parseRule(field, ruleToParse)

		if err != nil {
			break
		}

	}

	return err
}

func (ov *ObjectValidator) addValidator(field string, v Validator) {

	vl := new(validatorLink)
	vl.field = field
	vl.validator = v

	ov.validatorChain = append(ov.validatorChain, vl)

	c := v.CodesInUse()

	if c != nil {
		ov.codesInUse.AddAll(c)
	}

}

func (ov *ObjectValidator) isRuleRef(op string) bool {

	s := strings.SplitN(op, commandSep, -1)

	return len(s) == 2 && s[0] == RuleRefCode

}

func (ov *ObjectValidator) findRule(field, op string) ([]string, error) {

	ref := strings.SplitN(op, commandSep, -1)[1]

	rf := ov.RuleManager

	if rf == nil {
		m := fmt.Sprintf("Field %s has its rule specified as a reference to an external rule %s, but RuleManager is not set.\n", field, ref)
		return nil, errors.New(m)

	}

	if !rf.Exists(ref) {
		m := fmt.Sprintf("Field %s has its rule specified as a reference to an external rule %s, but no rule with that reference exists.\n", field, ref)
		return nil, errors.New(m)
	}

	return rf.Rule(ref), nil
}

func (ov *ObjectValidator) parseRule(field string, rule []string) error {

	rt, err := ov.extractType(field, rule)

	if err != nil {
		return err
	}

	switch rt {
	case StringRule:
		err = ov.parseAndAdd(field, rule, ov.stringBuilder.parseStringRule)

	default:
		m := fmt.Sprintf("Unsupported rule type for field %s\n", field)
		return errors.New(m)
	}

	return err

}

func (ov *ObjectValidator) parseAndAdd(field string, rule []string, pf parseAndBuild) error {
	v, err := pf(field, rule)

	if err != nil {
		return err
	} else {
		ov.addValidator(field, v)
		return nil
	}
}

func (ov *ObjectValidator) extractType(field string, rule []string) (ValidationRuleType, error) {

	for _, v := range rule {

		f := DecomposeOperation(v)

		if f[0] == StringRuleCode {
			return StringRule, nil
		}

	}

	m := fmt.Sprintf("Unable to determine the type of rule from the rule definition for field %s: %v/n", field, rule)

	return UnknownRuleType, errors.New(m)
}

func IsTypeIndicator(vType, op string) bool {

	return DecomposeOperation(op)[0] == vType

}

func DetermineDefaultErrorCode(vt string, rule []string, defaultCode string) string {
	for _, v := range rule {

		f := DecomposeOperation(v)

		if f[0] == vt {
			if len(f) > 1 {
				//Error code must be second component of type
				return f[1]
			}
		}

	}

	return defaultCode
}

func DecomposeOperation(r string) []string {

	removeEscaped := strings.Replace(r, escapedCommandSep, escapedCommandReplace, -1)
	split := strings.SplitN(removeEscaped, commandSep, -1)

	decomposed := make([]string, len(split))

	for i, v := range split {
		decomposed[i] = strings.Replace(v, escapedCommandReplace, commandSep, -1)
	}

	return decomposed

}

func determinePathFields(path string) types.StringSet {

	set := types.NewOrderedStringSet([]string{})

	split := strings.SplitN(path, ".", -1)

	l := len(split)

	if l > 1 {

		for i := 1; i < l; i++ {

			set.Add(strings.Join(split[0:i], "."))
		}

	}

	return set
}
<commit_msg>Object validation<commit_after>package validate

import (
	"errors"
	"fmt"
	"github.com/graniticio/granitic/ioc"
	"github.com/graniticio/granitic/logging"
	"github.com/graniticio/granitic/types"
	"strings"
)

type ValidationRuleType uint

type parseAndBuild func(string, []string) (Validator, error)

const (
	UnknownRuleType = iota
	StringRule
)

const commandSep = ":"
const escapedCommandSep = "::"
const escapedCommandReplace = "||ESC||"
const StringRuleCode = "STR"
const RuleRefCode = "RULE"

type SubjectContext struct {
	Subject        interface{}
	KnownSetFields types.StringSet
}

type validationContext struct {
	Subject        interface{}
	KnownSetFields types.StringSet
}

type ValidationResult struct {
	ErrorCodes []string
	Unset      bool
}

type Validator interface {
	Validate(vc *validationContext) (result *ValidationResult, unexpected error)
	StopAllOnFail() bool
	CodesInUse() types.StringSet
	DependsOnFields() types.StringSet
}

type validatorLink struct {
	validator Validator
	field     string
}

type UnparsedRuleManager struct {
	Rules map[string][]string
}

func (rm *UnparsedRuleManager) Exists(ref string) bool {
	return rm.Rules[ref] != nil
}

func (rm *UnparsedRuleManager) Rule(ref string) []string {
	return rm.Rules[ref]
}

type FieldErrors struct {
	Field      string
	ErrorCodes []string
}

type ObjectValidator struct {
	jsonConfig       interface{}
	RuleManager      *UnparsedRuleManager
	stringBuilder    *stringValidatorBuilder
	DefaultErrorCode string
	Rules            [][]string
	ComponentFinder  ioc.ComponentByNameFinder
	validatorChain   []*validatorLink
	componentName    string
	codesInUse       types.StringSet
	Log              logging.Logger
}

func (ov *ObjectValidator) Container(container *ioc.ComponentContainer) {
	ov.ComponentFinder = container
}

func (ov *ObjectValidator) ComponentName() string {
	return ov.componentName
}

func (ov *ObjectValidator) SetComponentName(name string) {
	ov.componentName = name
}

func (ov *ObjectValidator) ErrorCodesInUse() (codes types.StringSet, sourceName string) {
	return ov.codesInUse, ov.componentName
}

func (ov *ObjectValidator) Validate(subject *SubjectContext) ([]*FieldErrors, error) {

	log := ov.Log

	fieldErrors := make([]*FieldErrors, 0)
	fieldsWithProblems := types.NewOrderedStringSet([]string{})
	unsetFields := types.NewOrderedStringSet([]string{})

	for _, vl := range ov.validatorChain {

		f := vl.field

		log.LogDebugf("Validating field %s", f)

		vc := new(validationContext)
		vc.Subject = subject.Subject
		vc.KnownSetFields = subject.KnownSetFields

		v := vl.validator

		if !ov.parentsOkay(v, fieldsWithProblems, unsetFields) {
			log.LogDebugf("Skipping field %s as one or more parent objects invalid", f)
		}

		r, err := vl.validator.Validate(vc)
		ec := r.ErrorCodes

		if err != nil {
			return nil, err
		}

		if r.Unset {
			log.LogDebugf("%s is unset", f)
			unsetFields.Add(f)
		}

		l := len(ec)

		if ec != nil && l > 0 {

			fieldsWithProblems.Add(f)
			log.LogDebugf("%s has %d errors", f, l)

			fe := new(FieldErrors)
			fe.Field = f
			fe.ErrorCodes = ec

			fieldErrors = append(fieldErrors, fe)

			if vl.validator.StopAllOnFail() {
				break
			}

		}

	}

	return fieldErrors, nil

}

func (ov *ObjectValidator) parentsOkay(v Validator, fieldsWithProblems types.StringSet, unsetFields types.StringSet) bool {

	d := v.DependsOnFields()

	if d == nil || d.Size() == 0 {
		return true
	}

	for _, f := range d.Contents() {

		if fieldsWithProblems.Contains(f) || unsetFields.Contains(f) {
			return false
		}

	}

	return true
}

func (ov *ObjectValidator) StartComponent() error {

	if ov.Rules == nil {
		return errors.New("No Rules specified for validator.")
	}

	ov.codesInUse = types.NewUnorderedStringSet([]string{})

	if ov.DefaultErrorCode != "" {
		ov.codesInUse.Add(ov.DefaultErrorCode)
	}

	ov.stringBuilder = newStringValidatorBuilder(ov.DefaultErrorCode)
	ov.stringBuilder.componentFinder = ov.ComponentFinder

	ov.validatorChain = make([]*validatorLink, 0)

	return ov.parseRules()

}

func (ov *ObjectValidator) parseRules() error {

	var err error

	for _, rule := range ov.Rules {

		var ruleToParse []string

		if len(rule) < 2 {
			m := fmt.Sprintf("Rule is invlaid (must have at least an identifier and a type). Supplied rule is: %q", rule)
			return errors.New(m)
		}

		field := rule[0]
		ruleType := rule[1]

		if ov.isRuleRef(ruleType) {
			ruleToParse, err = ov.findRule(field, ruleType)

			if err != nil {
				break
			}

		} else {
			ruleToParse = rule[1:]
		}

		err = ov.parseRule(field, ruleToParse)

		if err != nil {
			break
		}

	}

	return err
}

func (ov *ObjectValidator) addValidator(field string, v Validator) {

	vl := new(validatorLink)
	vl.field = field
	vl.validator = v

	ov.validatorChain = append(ov.validatorChain, vl)

	c := v.CodesInUse()

	if c != nil {
		ov.codesInUse.AddAll(c)
	}

}

func (ov *ObjectValidator) isRuleRef(op string) bool {

	s := strings.SplitN(op, commandSep, -1)

	return len(s) == 2 && s[0] == RuleRefCode

}

func (ov *ObjectValidator) findRule(field, op string) ([]string, error) {

	ref := strings.SplitN(op, commandSep, -1)[1]

	rf := ov.RuleManager

	if rf == nil {
		m := fmt.Sprintf("Field %s has its rule specified as a reference to an external rule %s, but RuleManager is not set.\n", field, ref)
		return nil, errors.New(m)

	}

	if !rf.Exists(ref) {
		m := fmt.Sprintf("Field %s has its rule specified as a reference to an external rule %s, but no rule with that reference exists.\n", field, ref)
		return nil, errors.New(m)
	}

	return rf.Rule(ref), nil
}

func (ov *ObjectValidator) parseRule(field string, rule []string) error {

	rt, err := ov.extractType(field, rule)

	if err != nil {
		return err
	}

	switch rt {
	case StringRule:
		err = ov.parseAndAdd(field, rule, ov.stringBuilder.parseStringRule)

	default:
		m := fmt.Sprintf("Unsupported rule type for field %s\n", field)
		return errors.New(m)
	}

	return err

}

func (ov *ObjectValidator) parseAndAdd(field string, rule []string, pf parseAndBuild) error {
	v, err := pf(field, rule)

	if err != nil {
		return err
	} else {
		ov.addValidator(field, v)
		return nil
	}
}

func (ov *ObjectValidator) extractType(field string, rule []string) (ValidationRuleType, error) {

	for _, v := range rule {

		f := DecomposeOperation(v)

		if f[0] == StringRuleCode {
			return StringRule, nil
		}

	}

	m := fmt.Sprintf("Unable to determine the type of rule from the rule definition for field %s: %v/n", field, rule)

	return UnknownRuleType, errors.New(m)
}

func IsTypeIndicator(vType, op string) bool {

	return DecomposeOperation(op)[0] == vType

}

func DetermineDefaultErrorCode(vt string, rule []string, defaultCode string) string {
	for _, v := range rule {

		f := DecomposeOperation(v)

		if f[0] == vt {
			if len(f) > 1 {
				//Error code must be second component of type
				return f[1]
			}
		}

	}

	return defaultCode
}

func DecomposeOperation(r string) []string {

	removeEscaped := strings.Replace(r, escapedCommandSep, escapedCommandReplace, -1)
	split := strings.SplitN(removeEscaped, commandSep, -1)

	decomposed := make([]string, len(split))

	for i, v := range split {
		decomposed[i] = strings.Replace(v, escapedCommandReplace, commandSep, -1)
	}

	return decomposed

}

func determinePathFields(path string) types.StringSet {

	set := types.NewOrderedStringSet([]string{})

	split := strings.SplitN(path, ".", -1)

	l := len(split)

	if l > 1 {

		for i := 1; i < l; i++ {

			set.Add(strings.Join(split[0:i], "."))
		}

	}

	return set
}
<|endoftext|>


<commit_before>package main

const mainTemplate = `package main

import (
	"fmt"
	"github.com/vulcand/vulcand/service"
	"{{.PackagePath}}/registry"
	"os"
)

func main() {
	r, err := registry.GetRegistry()
	if err != nil {
		fmt.Printf("Service exited with error: %s\n", err)
		os.Exit(255)
	}
	if err := service.Run(r); err != nil {
		fmt.Printf("Service exited with error: %s\n", err)
		os.Exit(255)
	} else {
		fmt.Println("Service exited gracefully")
	}
}
`

const registryTemplate = `package registry

import (
	"github.com/vulcand/vulcand/plugin"
	{{range .Packages}}
	"{{.}}"
	{{end}}
)

func GetRegistry() (*plugin.Registry, error) {
	r := plugin.NewRegistry()

	specs := []*plugin.MiddlewareSpec{
		{{range .Packages}}
		{{.Name}}.GetSpec(),
       {{end}}
	}

	for _, spec := range specs {
		if err := r.AddSpec(spec); err != nil {
			return nil, err
		}
	}
	return r, nil
}
`

const vulcanctlTemplate = `package main

import (
    "github.com/mailgun/log"
	"github.com/vulcand/vulcand/vctl/command"
	"{{.PackagePath}}/registry"
	"os"
)

var vulcanUrl string

func main() {
	log.Init([]*log.LogConfig{&log.LogConfig{Name: "console"}})

    r, err := registry.GetRegistry()
	if err != nil {
		log.Errorf("Error: %s\n", err)
        return
	}
	cmd := command.NewCommand(r)
	if err := cmd.Run(os.Args); err != nil {
		log.Errorf("Error: %s\n", err)
	}
}
`
<commit_msg>Fix incorrect logger initialization in vbundle vctl template<commit_after>package main

const mainTemplate = `package main

import (
	"fmt"
	"github.com/vulcand/vulcand/service"
	"{{.PackagePath}}/registry"
	"os"
)

func main() {
	r, err := registry.GetRegistry()
	if err != nil {
		fmt.Printf("Service exited with error: %s\n", err)
		os.Exit(255)
	}
	if err := service.Run(r); err != nil {
		fmt.Printf("Service exited with error: %s\n", err)
		os.Exit(255)
	} else {
		fmt.Println("Service exited gracefully")
	}
}
`

const registryTemplate = `package registry

import (
	"github.com/vulcand/vulcand/plugin"
	{{range .Packages}}
	"{{.}}"
	{{end}}
)

func GetRegistry() (*plugin.Registry, error) {
	r := plugin.NewRegistry()

	specs := []*plugin.MiddlewareSpec{
		{{range .Packages}}
		{{.Name}}.GetSpec(),
       {{end}}
	}

	for _, spec := range specs {
		if err := r.AddSpec(spec); err != nil {
			return nil, err
		}
	}
	return r, nil
}
`

const vulcanctlTemplate = `package main

import (
    "github.com/mailgun/log"
	"github.com/vulcand/vulcand/vctl/command"
	"{{.PackagePath}}/registry"
	"os"
)

func main() {
	console, err := log.NewLogger(log.Config{"console", "info"})
	if err != nil {
		log.Errorf("Error: %s\n", err)
		return
	}
	log.Init(console)

	r, err := registry.GetRegistry()
	if err != nil {
		log.Errorf("Error: %s\n", err)
		return
	}

	cmd := command.NewCommand(r)
	if err := cmd.Run(os.Args); err != nil {
		log.Errorf("Error: %s\n", err)
	}
}
`
<|endoftext|>


<commit_before>package wallswap

import (
    "math/rand"
    "database/sql"
    "golang.org/x/net/html"
)

/* @see http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang */
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func CheckErr(err error) {
    if err != nil {
        panic(err)
    }
}

func RandString(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}

func GetDBConnection() (*sql.DB) {
    db, err := sql.Open("mysql", "root:root@tcp(localhost:3306)/wallswap")
    CheckErr(err)
    return db
}

func GetWallpapers() (wallpapers map[string]string) {
    wallpapers = make(map[string]string)

    var (
        thumbUrl string
        url string
    )

    db := GetDBConnection()
    defer db.Close()

    rows, err := db.Query("select thumb_url, url from wallpaper")
    CheckErr(err)

    defer rows.Close()

    for rows.Next() {
        err := rows.Scan(&thumbUrl, &url)
        CheckErr(err)
        wallpapers[thumbUrl] = url
    }
    err = rows.Err()
    CheckErr(err)

    return
}

// Helper function to pull the id attribute from a Token
func GetId(t html.Token) (ok bool, id string) {
    // Iterate over all of the Token's attributes until we find an "id"
    for _, attr := range t.Attr {
        if attr.Key == "data-wallpaper-id" {
            id = attr.Val
            ok = true
        }
    }
    return
}
<commit_msg>[*] Refactored for better code style<commit_after>package wallswap

import (
    "math/rand"
    "database/sql"
    "golang.org/x/net/html"
)

/* @see http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang */
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func CheckErr(err error) {
    if err != nil {
        panic(err)
    }
}

func RandString(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}

func GetDBConnection() (*sql.DB) {
    db, err := sql.Open("mysql", "root:root@tcp(localhost:3306)/wallswap")
    CheckErr(err)
    return db
}

func GetWallpapers() (wallpapers map[string]string) {
    wallpapers = make(map[string]string)

    var (
        thumbUrl string
        url string
    )

    db := GetDBConnection()
    defer db.Close()

    rows, err := db.Query("select thumb_url, url from wallpaper")
    CheckErr(err)

    defer rows.Close()

    for rows.Next() {
        err := rows.Scan(&thumbUrl, &url)
        CheckErr(err)
        wallpapers[thumbUrl] = url
    }
    err = rows.Err()
    CheckErr(err)

    return
}

// Helper function to pull the id attribute from a Token
func GetId(t html.Token) (id string, ok bool) {
    // Iterate over all of the Token's attributes until we find an "id"
    for _, attr := range t.Attr {
        if attr.Key == "data-wallpaper-id" {
            id = attr.Val
            ok = true
        }
    }
    return
}
<|endoftext|>


<commit_before>package velobike

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

const (
	baseURL = "http://apivelobike.velobike.ru"
)

// A Client manages communication with the API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// Services used for talking to different parts of the API.
	Parkings      *ParkingsService
	Authorization *AuthorizeService
	Profile       *ProfileService
	History       *HistoryService

	// Session ID for authorized user
	SessionID *string
}

// NewClient returns a new API client.
// If a nil httpClient is provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(baseURL)

	c := &Client{client: httpClient, BaseURL: baseURL}
	c.Parkings = &ParkingsService{client: c}
	c.Authorization = &AuthorizeService{client: c}
	c.Profile = &ProfileService{client: c}
	c.History = &HistoryService{client: c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash.  If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("App-Version", "1.3")

	if c.SessionID != nil {
		req.Header.Add("SessionID", *c.SessionID)
	}

	return req, nil
}

// Response is an API response.
// This wraps the standard http.Response.
type Response struct {
	*http.Response
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	return response
}

// Do sends an API request and returns the API response.  The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred.  If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	// use buffer with TeeReader to duplicate a response body stream
	var buf bytes.Buffer
	body := io.TeeReader(resp.Body, &buf)

	err = CheckResponse(resp, body)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, &buf)
		} else {
			err = json.NewDecoder(&buf).Decode(v)
			if err == io.EOF {
				err = nil // ignore EOF errors caused by empty response body
			}
		}
	}

	return response, err
}

// BasicAuthTransport is an http.RoundTripper that authenticates all requests
// using HTTP Basic Authentication with the provided username and password.
type BasicAuthTransport struct {
	Username string // velobike.ru user id
	Password string // velobike.ru password

	// Transport is the underlying HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes requests that are authenticated
// using HTTP Basic Authentication.
func (t *BasicAuthTransport) Client() *http.Client {
	return &http.Client{Transport: t}
}

func (t *BasicAuthTransport) transport() http.RoundTripper {
	if t.Transport != nil {
		return t.Transport
	}
	return http.DefaultTransport
}

// RoundTrip implements the RoundTripper interface.
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req) // per RoundTrip contract
	req.SetBasicAuth(t.Username, t.Password)

	return t.transport().RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request. The clone is a
// shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header, len(r.Header))
	for k, s := range r.Header {
		r2.Header[k] = append([]string(nil), s...)
	}
	return r2
}

// ErrorResponse indicates an error caused by an API request.
type ErrorResponse struct {
	Code             string `json:"code"`
	ExtCode          int    `json:"extCode"`
	LocalizedMessage string `json:"localizedMessage"`
	Message          string `json:"message"`
	Response         *http.Response
}

// Error implements an error interface.
func (e *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %s %d %s",
		e.Response.Request.Method, e.Response.Request.URL,
		e.Code, e.ExtCode, e.Message,
	)
}

// CheckResponse checks response headers and a copied stream of the body.
func CheckResponse(r *http.Response, teedBody io.Reader) error {
	if r.StatusCode != http.StatusOK {
		return fmt.Errorf("%v %v: %s",
			r.Request.Method, r.Request.URL, r.Status,
		)
	}

	var er ErrorResponse
	err := json.NewDecoder(teedBody).Decode(&er)
	if err == nil && er.Code != "" {
		er.Response = r
		return &er
	}

	return nil
}
<commit_msg>Handle the io.Copy error<commit_after>package velobike

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

const (
	baseURL = "http://apivelobike.velobike.ru"
)

// A Client manages communication with the API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.
	BaseURL *url.URL

	// Services used for talking to different parts of the API.
	Parkings      *ParkingsService
	Authorization *AuthorizeService
	Profile       *ProfileService
	History       *HistoryService

	// Session ID for authorized user
	SessionID *string
}

// NewClient returns a new API client.
// If a nil httpClient is provided, http.DefaultClient will be used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	baseURL, _ := url.Parse(baseURL)

	c := &Client{client: httpClient, BaseURL: baseURL}
	c.Parkings = &ParkingsService{client: c}
	c.Authorization = &AuthorizeService{client: c}
	c.Profile = &ProfileService{client: c}
	c.History = &HistoryService{client: c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash.  If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("App-Version", "1.3")

	if c.SessionID != nil {
		req.Header.Add("SessionID", *c.SessionID)
	}

	return req, nil
}

// Response is an API response.
// This wraps the standard http.Response.
type Response struct {
	*http.Response
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	return response
}

// Do sends an API request and returns the API response.  The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred.  If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	// use buffer with TeeReader to duplicate a response body stream
	var buf bytes.Buffer
	body := io.TeeReader(resp.Body, &buf)

	err = CheckResponse(resp, body)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, &buf)
			if err != nil {
				return response, err
			}
		} else {
			err = json.NewDecoder(&buf).Decode(v)
			if err == io.EOF {
				err = nil // ignore EOF errors caused by empty response body
			}
		}
	}

	return response, err
}

// BasicAuthTransport is an http.RoundTripper that authenticates all requests
// using HTTP Basic Authentication with the provided username and password.
type BasicAuthTransport struct {
	Username string // velobike.ru user id
	Password string // velobike.ru password

	// Transport is the underlying HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes requests that are authenticated
// using HTTP Basic Authentication.
func (t *BasicAuthTransport) Client() *http.Client {
	return &http.Client{Transport: t}
}

func (t *BasicAuthTransport) transport() http.RoundTripper {
	if t.Transport != nil {
		return t.Transport
	}
	return http.DefaultTransport
}

// RoundTrip implements the RoundTripper interface.
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req) // per RoundTrip contract
	req.SetBasicAuth(t.Username, t.Password)

	return t.transport().RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request. The clone is a
// shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header, len(r.Header))
	for k, s := range r.Header {
		r2.Header[k] = append([]string(nil), s...)
	}
	return r2
}

// ErrorResponse indicates an error caused by an API request.
type ErrorResponse struct {
	Code             string `json:"code"`
	ExtCode          int    `json:"extCode"`
	LocalizedMessage string `json:"localizedMessage"`
	Message          string `json:"message"`
	Response         *http.Response
}

// Error implements an error interface.
func (e *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %s %d %s",
		e.Response.Request.Method, e.Response.Request.URL,
		e.Code, e.ExtCode, e.Message,
	)
}

// CheckResponse checks response headers and a copied stream of the body.
func CheckResponse(r *http.Response, teedBody io.Reader) error {
	if r.StatusCode != http.StatusOK {
		return fmt.Errorf("%v %v: %s",
			r.Request.Method, r.Request.URL, r.Status,
		)
	}

	var er ErrorResponse
	err := json.NewDecoder(teedBody).Decode(&er)
	if err == nil && er.Code != "" {
		er.Response = r
		return &er
	}

	return nil
}
<|endoftext|>