<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
	"github.com/BytemarkHosting/row"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
)

// OutputJSON is an OutputFn which outputs a nicely-indented JSON object that represents obj
func (c *Context) OutputJSON(obj interface{}) error {
	js, err := json.MarshalIndent(obj, "", "    ")
	if err != nil {
		return err
	}
	fmt.Fprintf(global.App.Writer, string(js))
	return nil
}

func trimAllSpace(strs []string) {
	for i, s := range strs {
		strs[i] = strings.TrimSpace(s)
	}
}

func (c *Context) determineTableFields(obj interface{}) []string {
	chosenFields := strings.Split(c.String("table-fields"), ",")
	trimAllSpace(chosenFields)

	if len(chosenFields) > 0 && chosenFields[0] != "" {
		return chosenFields
	}

	fieldsList := row.FieldsFrom(obj)
	return fieldsList
}

// OutputTable is an OutputFn which outputs the object in table form, using github.com/BytemarkHosting/row and github.com/olekukonko/tablewriter
func (c *Context) OutputTable(obj interface{}) error {
	if c.String("table-fields") == "help" {
		fieldsList := row.FieldsFrom(obj)
		fmt.Fprintf(global.App.Writer, "Table fields available for this command: \r\n  %s\r\n\r\n", strings.Join(fieldsList, "\r\n  "))
		return nil
	}
	fields := c.determineTableFields(obj)
	return RenderTable(obj, fields)
}

// RenderTable creates a table for the given object. This makes
// most sense when it's an array, but a regular struct-y object works fine too.
func RenderTable(obj interface{}, fields []string) error {
	table := tablewriter.NewWriter(global.App.Writer)
	// don't autowrap because fields that are slices output one element per line
	// and autowrap
	table.SetAutoWrapText(false)
	// lines between rows!
	table.SetRowLine(true)
	// don't autoformat the headers - autoformat makes them ALLCAPS which makes
	// it hard to figure out what to set --table-fields to.
	// with autoformat off, --table-fields can be set by copying and pasting
	// from the table header.
	table.SetAutoFormatHeaders(false)

	table.SetHeader(fields)
	v := reflect.ValueOf(obj)

	// indirect pointers so we can switch on Kind()
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	// output a single table row for a struct, or several for a slice / array
	switch v.Kind() {
	case reflect.Struct:
		r, err := row.From(obj, fields)
		if err != nil {
			return err
		}
		table.Append(r)
	case reflect.Slice, reflect.Array:
		length := v.Len()
		for i := 0; i < length; i++ {
			el := v.Index(i)
			r, err := row.From(el.Interface(), fields)
			if err != nil {
				return err
			}
			table.Append(r)
		}
	default:
		return fmt.Errorf("%T is not a struct or slice type - please file a bug report", obj)
	}

	table.Render()
	return nil
}

// OutputFlags creates some cli.Flags for when you wanna use OutputInDesiredForm
// thing should be like "server", "servers", "group", "groups"
// jsonType should be "array" or "object"
func OutputFlags(thing string, jsonType string, defaultTableFields string) []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name:  "json",
			Usage: fmt.Sprintf("Output the %s as a JSON %s", thing, jsonType),
		},
		cli.BoolFlag{
			Name:  "table",
			Usage: fmt.Sprintf("Output the %s as a table", thing),
		},
		cli.StringFlag{
			Name:  "table-fields",
			Usage: fmt.Sprintf("The fields of the %s to output in the table, comma separated. set to 'help' for a list of fields for this command", thing),
			Value: defaultTableFields,
		},
	}
}

// OutputFn is a function for outputting an object to the terminal in some way
// See the OutputFormatFns map to see examples
type OutputFn func(context *Context, obj interface{}) error

// OutputFormatFns is a map which contains all the supported output format functions -- except 'human' because that's implemented in the OutputInDesiredForm method, by necessity.
var OutputFormatFns = map[string]OutputFn{
	"debug": func(c *Context, obj interface{}) error {
		fmt.Fprintf(global.App.Writer, "%#v", obj)
		return nil
	},
	"json":  (*Context).OutputJSON,
	"table": (*Context).OutputTable,
}

// SupportedOutputTypes returns a list of all suppported output forms, including 'human'
func SupportedOutputTypes() (outputTypes []string) {
	outputTypes = make([]string, 0, len(OutputFormatFns)+1)
	for k := range OutputFormatFns {
		outputTypes = append(outputTypes, k)
	}
	outputTypes = append(outputTypes, "human")
	return
}

type outputtable interface {
	prettyprint.PrettyPrinter
	output.DefaultFieldsHaver
}

// OutputInDesiredForm outputs obj as a JSON object if --json is set,
// or as a table / table row if --table is set
// otherwise calls humanOutputFn (which should output it in a very human form - PrettyPrint or such
// defaultFormat is an optional string stating that the default format should be
func (c *Context) OutputInDesiredForm(obj interface{}, humanOutputFn func() error, defaultFormat ...string) error {
	format, err := global.Config.GetV("output-format")
	if err != nil {
		return err
	}
	if len(defaultFormat) > 0 && format.Source == "CODE" {
		format.Value = defaultFormat[0]
	}

	if c.Bool("json") {
		format.Value = "json"
	} else if c.Bool("table") || c.Context.IsSet("table-fields") {
		format.Value = "table"
	}

	if format.Value == "" || format.Value == "human" {
		return humanOutputFn()
	}

	if fn, ok := OutputFormatFns[format.Value]; ok {
		return fn(c, obj)
	}

	return fmt.Errorf("%s isn't a supported output type. Use one of the following instead:\r\n%s", format.Value, strings.Join(SupportedOutputTypes(), "\r\n"))
}
<commit_msg>unfinished stuff<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
	"strings"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
	"github.com/BytemarkHosting/row"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
)

type outputtable interface {
	prettyprint.PrettyPrinter
	output.DefaultFieldsHaver
}

// OutputJSON is an OutputFn which outputs a nicely-indented JSON object that represents obj
func (c *Context) OutputJSON(obj outputtable) error {
	js, err := json.MarshalIndent(obj, "", "    ")
	if err != nil {
		return err
	}
	fmt.Fprintf(global.App.Writer, string(js))
	return nil
}

func trimAllSpace(strs []string) {
	for i, s := range strs {
		strs[i] = strings.TrimSpace(s)
	}
}

func (c *Context) determineTableFields(obj output.DefaultFieldsHaver) []string {
	fields := strings.Split(c.String("table-fields"), ",")
	trimAllSpace(fields)

	if len(fields) > 0 && fields[0] != "" {
		return fields
	}
	fields = strings.Split(obj.DefaultFields(output.Table), ",")
	trimAllSpace(fields)
	return fields
}

// OutputTable is an OutputFn which outputs the object in table form, using github.com/BytemarkHosting/row and github.com/olekukonko/tablewriter
func OutputTable(wr io.Writer, cfg OutputConfig, obj output.DefaultFieldsHaver) error {
	if cfg.Fields == "help" {
		fieldsList := row.FieldsFrom(obj)
		fmt.Fprintf(global.App.Writer, "Fields available for this command: \r\n  %s\r\n\r\n", strings.Join(fieldsList, "\r\n  "))
		return nil
	}
	fields := determineFields(cfg, obj)
	return RenderTable(wr, fields, obj)
}

func SetupTable(wr io.Writer, cfg OutputConfig) *table.Table {

}

// RenderTable creates a table for the given object. This makes
// most sense when it's an array, but a regular struct-y object works fine too.
func RenderTable(wr io.Writer, fields []string, obj interface{}) error {
	table := tablewriter.NewWriter(wr)
	// don't autowrap because fields that are slices output one element per line
	// and autowrap
	table.SetAutoWrapText(false)
	// lines between rows!
	table.SetRowLine(true)
	// don't autoformat the headers - autoformat makes them ALLCAPS which makes
	// it hard to figure out what to set --table-fields to.
	// with autoformat off, --table-fields can be set by copying and pasting
	// from the table header.
	table.SetAutoFormatHeaders(false)

	table.SetHeader(fields)
	v := reflect.ValueOf(obj)

	// indirect pointers so we can switch on Kind()
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	// output a single table row for a struct, or several for a slice / array
	switch v.Kind() {
	case reflect.Struct:
		r, err := row.From(obj, fields)
		if err != nil {
			return err
		}
		table.Append(r)
	case reflect.Slice, reflect.Array:
		length := v.Len()
		for i := 0; i < length; i++ {
			el := v.Index(i)
			r, err := row.From(el.Interface(), fields)
			if err != nil {
				return err
			}
			table.Append(r)
		}
	default:
		return fmt.Errorf("%T is not a struct or slice type - please file a bug report", obj)
	}

	table.Render()
	return nil
}

// OutputFlags creates some cli.Flags for when you wanna use OutputInDesiredForm
// thing should be like "server", "servers", "group", "groups"
// jsonType should be "array" or "object"
func OutputFlags(thing string, jsonType string, defaultTableFields string) []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name:  "json",
			Usage: fmt.Sprintf("Output the %s as a JSON %s", thing, jsonType),
		},
		cli.BoolFlag{
			Name:  "table",
			Usage: fmt.Sprintf("Output the %s as a table", thing),
		},
		cli.StringFlag{
			Name:  "table-fields",
			Usage: fmt.Sprintf("The fields of the %s to output in the table, comma separated. set to 'help' for a list of fields for this command", thing),
			Value: defaultTableFields,
		},
	}
}

type OutputConfig struct {
	Fields string
	Format output.Format
}

// OutputFn is a function for outputting an object to the terminal in some way
// See the OutputFormatFns map to see examples
type OutputFn func(wr io.Writer, fields string, obj outputtable) error

// OutputFormatFns is a map which contains all the supported output format functions -- except 'human' because that's implemented in the OutputInDesiredForm method, by necessity.
var OutputFormatFns = map[string]OutputFn{
	"debug": func(wr io.Writer, cfg OutputConfig, obj outputtable) error {
		fmt.Fprintf(wr, "%#v", obj)
		return nil
	},
	"json":  OutputJSON,
	"table": OutputTable,
	"human": func(wr io.Writer, cfg OutputConfig, obj outputtable) error {
		return obj.PrettyPrint(wr, prettyprint.Full)
	},
}

// SupportedOutputTypes returns a list of all suppported output forms, including 'human'
func SupportedOutputTypes() (outputTypes []string) {
	outputTypes = make([]string, 0, len(OutputFormatFns)+1)
	for k := range OutputFormatFns {
		outputTypes = append(outputTypes, k)
	}
	outputTypes = append(outputTypes, "human")
	return
}

// OutputInDesiredForm outputs obj as a JSON object if --json is set,
// or as a table / table row if --table is set
// otherwise calls humanOutputFn (which should output it in a very human form - PrettyPrint or such
// defaultFormat is an optional string stating that the default format should be
func (c *Context) OutputInDesiredForm(obj interface{}, humanOutputFn func() error, defaultFormat ...string) error {
	format, err := global.Config.GetV("output-format")
	if err != nil {
		return err
	}
	if len(defaultFormat) > 0 && format.Source == "CODE" {
		format.Value = defaultFormat[0]
	}

	if c.Bool("json") {
		format.Value = "json"
	} else if c.Bool("table") || c.Context.IsSet("table-fields") {
		format.Value = "table"
	}

	if format.Value == "" || format.Value == "human" {
		return humanOutputFn()
	}

	if fn, ok := OutputFormatFns[format.Value]; ok {
		return fn(c, obj)
	}

	return fmt.Errorf("%s isn't a supported output type. Use one of the following instead:\r\n%s", format.Value, strings.Join(SupportedOutputTypes(), "\r\n"))
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"

	"github.com/fatih/color"
)

type myWriter struct {
	out io.Writer
	color color.Attribute
}

func (m myWriter) Write(p []byte) (int, error) {
	return color.New(m.color).Fprintf(m.out, string(p))
}

func runScript(dir, command string, envs []string) error {
	fmt.Println(infoPrefix + " Running command: " + color.BlueString(command))

	cmd := exec.Command("/bin/bash", "-c", "set -euo pipefail; set -x; " + command)
	cmd.Env = envs
	cmd.Dir = dir
	cmd.Stdout = myWriter{os.Stdout, color.FgGreen}
	cmd.Stderr = myWriter{os.Stderr, color.FgRed}
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

func runScripts(dir string, commands, envs []string) error {
	for _, command := range commands {
		err := runScript(dir, command, envs)
		if err != nil {
			return fmt.Errorf("failed to execute command[%s]: %v", command, err)
		}
	}

	return nil
}
<commit_msg>use gray for hook script outputs (#131)<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"

	"github.com/fatih/color"
)

type myWriter struct {
	out io.Writer
	color color.Attribute
}

func (m myWriter) Write(p []byte) (int, error) {
	return color.New(m.color).Fprintf(m.out, string(p))
}

func runScript(dir, command string, envs []string) error {
	fmt.Println(infoPrefix + " Running command: " + color.BlueString(command))

	cmd := exec.Command("/bin/bash", "-c", "set -euo pipefail; set -x; " + command)
	cmd.Env = envs
	cmd.Dir = dir
	cmd.Stdout = myWriter{os.Stdout, color.FgHiBlack}
	cmd.Stderr = myWriter{os.Stderr, color.FgHiBlack}
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

func runScripts(dir string, commands, envs []string) error {
	for _, command := range commands {
		err := runScript(dir, command, envs)
		if err != nil {
			return fmt.Errorf("failed to execute command[%s]: %v", command, err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"time"

	"golang.org/x/crypto/ssh"
)

type BeaconConfig struct {
	Host             string   `long:"host" default:"127.0.0.1" description:"TSA host to forward the worker through."`
	Port             int      `long:"port" default:"2222" description:"TSA port to connect to."`
	PublicKey        FileFlag `long:"public-key" description:"File containing a public key to expect from the TSA."`
	WorkerPrivateKey FileFlag `long:"worker-private-key" description:"File containing the private key to use when authenticating to the TSA."`
}

func (config BeaconConfig) Dial() (*ssh.Client, error) {
	workerPrivateKeyBytes, err := ioutil.ReadFile(string(config.WorkerPrivateKey))
	if err != nil {
		return nil, fmt.Errorf("failed to read worker private key: %s", err)
	}

	workerPrivateKey, err := ssh.ParsePrivateKey(workerPrivateKeyBytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse worker private key: %s", err)
	}

	tsaAddr := fmt.Sprintf("%s:%d", config.Host, config.Port)

	conn, err := net.DialTimeout("tcp", tsaAddr, 10*time.Second)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to TSA:", err)
	}

	clientConfig := &ssh.ClientConfig{
		User: "beacon", // doesn't matter

		HostKeyCallback: config.checkHostKey,

		Auth: []ssh.AuthMethod{ssh.PublicKeys(workerPrivateKey)},
	}

	clientConn, chans, reqs, err := ssh.NewClientConn(conn, tsaAddr, clientConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to construct client connection:", err)
	}

	return ssh.NewClient(clientConn, chans, reqs), nil
}

func (config BeaconConfig) checkHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
	hostPublicKeyBytes, err := ioutil.ReadFile(string(config.PublicKey))
	if err != nil {
		return fmt.Errorf("failed to read host public key: %s", err)
	}

	hostPublicKey, _, _, _, err := ssh.ParseAuthorizedKey(hostPublicKeyBytes)
	if err != nil {
		return fmt.Errorf("failed to parse host public key: %s", err)
	}

	// note: hostname/addr are not verified; they may be behind a load balancer
	// so the definition gets a bit fuzzy

	if hostPublicKey.Type() != key.Type() || !bytes.Equal(hostPublicKey.Marshal(), key.Marshal()) {
		return errors.New("remote host public key mismatch")
	}

	return nil
}
<commit_msg>fix error message<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"time"

	"golang.org/x/crypto/ssh"
)

type BeaconConfig struct {
	Host             string   `long:"host" default:"127.0.0.1" description:"TSA host to forward the worker through."`
	Port             int      `long:"port" default:"2222" description:"TSA port to connect to."`
	PublicKey        FileFlag `long:"public-key" description:"File containing a public key to expect from the TSA."`
	WorkerPrivateKey FileFlag `long:"worker-private-key" description:"File containing the private key to use when authenticating to the TSA."`
}

func (config BeaconConfig) Dial() (*ssh.Client, error) {
	workerPrivateKeyBytes, err := ioutil.ReadFile(string(config.WorkerPrivateKey))
	if err != nil {
		return nil, fmt.Errorf("failed to read worker private key: %s", err)
	}

	workerPrivateKey, err := ssh.ParsePrivateKey(workerPrivateKeyBytes)
	if err != nil {
		return nil, fmt.Errorf("failed to parse worker private key: %s", err)
	}

	tsaAddr := fmt.Sprintf("%s:%d", config.Host, config.Port)

	conn, err := net.DialTimeout("tcp", tsaAddr, 10*time.Second)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to TSA: %s", err)
	}

	clientConfig := &ssh.ClientConfig{
		User: "beacon", // doesn't matter

		HostKeyCallback: config.checkHostKey,

		Auth: []ssh.AuthMethod{ssh.PublicKeys(workerPrivateKey)},
	}

	clientConn, chans, reqs, err := ssh.NewClientConn(conn, tsaAddr, clientConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to construct client connection:", err)
	}

	return ssh.NewClient(clientConn, chans, reqs), nil
}

func (config BeaconConfig) checkHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
	hostPublicKeyBytes, err := ioutil.ReadFile(string(config.PublicKey))
	if err != nil {
		return fmt.Errorf("failed to read host public key: %s", err)
	}

	hostPublicKey, _, _, _, err := ssh.ParseAuthorizedKey(hostPublicKeyBytes)
	if err != nil {
		return fmt.Errorf("failed to parse host public key: %s", err)
	}

	// note: hostname/addr are not verified; they may be behind a load balancer
	// so the definition gets a bit fuzzy

	if hostPublicKey.Type() != key.Type() || !bytes.Equal(hostPublicKey.Marshal(), key.Marshal()) {
		return errors.New("remote host public key mismatch")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	fits "github.com/astrogo/fitsio"
)

func main() {
	rc := run()
	os.Exit(rc)
}

func run() int {
	var single bool

	flag.Usage = func() {
		const msg = `Usage: go-fitsio-listhead filename[ext]

List the FITS header keywords in a single extension, or, if
ext is not given, list the keywords in all the extensions.

Examples:

   go-fitsio-listhead file.fits      - list every header in the file
   go-fitsio-listhead file.fits[0]   - list primary array header
   go-fitsio-listhead file.fits[2]   - list header of 2nd extension
   go-fitsio-listhead file.fits+2    - same as above
   go-fitsio-listhead file.fits[GTI] - list header of GTI extension

Note that it may be necessary to enclose the input file
name in single quote characters on the Unix command line.
`
		fmt.Fprintf(os.Stderr, "%v\n", msg)
		flag.PrintDefaults()
	}

	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		return 1
	}

	fname := flag.Arg(0)
	r, err := os.Open(fname)
	if err != nil {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}
	defer r.Close()

	f, err := fits.Open(r)
	if err != nil {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}
	defer f.Close()

	// list only a single header if a specific extension was given
	if strings.Contains(fname, "[") {
		single = true
	}

	for i := 0; i < len(f.HDUs()); i++ {
		hdu := f.HDU(i)
		hdr := hdu.Header()
		fmt.Printf("Header listing for HDU #%d:\n", i)

		for _, n := range hdr.Keys() {
			card := hdr.Get(n)
			if card == nil {
				fmt.Fprintf(os.Stderr, "**error** could not retrieve card [%v]", n)
				return 1
			}
			fmt.Printf(
				"%-8s= %-29s / %s\n",
				card.Name,
				fmt.Sprintf("%v", card.Value),
				card.Comment)
		}
		fmt.Printf("END\n\n")

		// quit if only listing a single header
		if single {
			break
		}
	}
	if err != nil && err != io.EOF {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}

	return 0
}
<commit_msg>cmd/go-fitsio-listhead: use Header.Card for compatibility with C-fitshdr<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	fits "github.com/astrogo/fitsio"
)

func main() {
	rc := run()
	os.Exit(rc)
}

func run() int {
	var single bool

	flag.Usage = func() {
		const msg = `Usage: go-fitsio-listhead filename[ext]

List the FITS header keywords in a single extension, or, if
ext is not given, list the keywords in all the extensions.

Examples:

   go-fitsio-listhead file.fits      - list every header in the file
   go-fitsio-listhead file.fits[0]   - list primary array header
   go-fitsio-listhead file.fits[2]   - list header of 2nd extension
   go-fitsio-listhead file.fits+2    - same as above
   go-fitsio-listhead file.fits[GTI] - list header of GTI extension

Note that it may be necessary to enclose the input file
name in single quote characters on the Unix command line.
`
		fmt.Fprintf(os.Stderr, "%v\n", msg)
		flag.PrintDefaults()
	}

	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		return 1
	}

	fname := flag.Arg(0)
	r, err := os.Open(fname)
	if err != nil {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}
	defer r.Close()

	f, err := fits.Open(r)
	if err != nil {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}
	defer f.Close()

	// list only a single header if a specific extension was given
	if strings.Contains(fname, "[") {
		single = true
	}

	for i := 0; i < len(f.HDUs()); i++ {
		hdu := f.HDU(i)
		hdr := hdu.Header()
		fmt.Printf("Header listing for HDU #%d:\n", i)

		for k := range hdr.Keys() {
			card := hdr.Card(k)
			fmt.Printf(
				"%-8s= %-29s / %s\n",
				card.Name,
				fmt.Sprintf("%v", card.Value),
				card.Comment)
		}
		fmt.Printf("END\n\n")

		// quit if only listing a single header
		if single {
			break
		}
	}
	if err != nil && err != io.EOF {
		fmt.Fprintf(os.Stderr, "**error** %v\n", err)
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/klog"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/apis/kops/validation"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/pkg/kopscodecs"
	"k8s.io/kops/pkg/try"
	"k8s.io/kops/upup/pkg/fi/cloudup"
	"k8s.io/kubectl/pkg/cmd/util/editor"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

var (
	editInstancegroupLong = templates.LongDesc(i18n.T(`Edit a cluster configuration.

	This command changes the instancegroup desired configuration in the registry.

    	To set your preferred editor, you can define the EDITOR environment variable.
    	When you have done this, kops will use the editor that you have set.

	kops edit does not update the cloud resources, to apply the changes use "kops update cluster".`))

	editInstancegroupExample = templates.Examples(i18n.T(`
	# Edit an instancegroup desired configuration.
	kops edit ig --name k8s-cluster.example.com nodes --state=s3://kops-state-1234
	`))

	editInstancegroupShort = i18n.T(`Edit instancegroup.`)
)

type EditInstanceGroupOptions struct {
}

func NewCmdEditInstanceGroup(f *util.Factory, out io.Writer) *cobra.Command {
	options := &EditInstanceGroupOptions{}

	cmd := &cobra.Command{
		Use:     "instancegroup",
		Aliases: []string{"instancegroups", "ig"},
		Short:   editInstancegroupShort,
		Long:    editInstancegroupLong,
		Example: editInstancegroupExample,
		Run: func(cmd *cobra.Command, args []string) {
			ctx := context.TODO()

			err := RunEditInstanceGroup(ctx, f, cmd, args, os.Stdout, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	return cmd
}

func RunEditInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, options *EditInstanceGroupOptions) error {
	if len(args) == 0 {
		return fmt.Errorf("Specify name of instance group to edit")
	}
	if len(args) != 1 {
		return fmt.Errorf("Can only edit one instance group at a time")
	}

	groupName := args[0]

	cluster, err := rootCommand.Cluster(ctx)
	if err != nil {
		return err
	}

	channel, err := cloudup.ChannelForCluster(cluster)
	if err != nil {
		klog.Warningf("%v", err)
	}

	clientset, err := rootCommand.Clientset()
	if err != nil {
		return err
	}

	if groupName == "" {
		return fmt.Errorf("name is required")
	}

	oldGroup, err := clientset.InstanceGroupsFor(cluster).Get(ctx, groupName, metav1.GetOptions{})
	if err != nil {
		return fmt.Errorf("error reading InstanceGroup %q: %v", groupName, err)
	}
	if oldGroup == nil {
		return fmt.Errorf("InstanceGroup %q not found", groupName)
	}

	var (
		edit = editor.NewDefaultEditor(editorEnvs)
	)

	ext := "yaml"
	raw, err := kopscodecs.ToVersionedYaml(oldGroup)
	if err != nil {
		return err
	}

	// launch the editor
	edited, file, err := edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, bytes.NewReader(raw))
	defer func() {
		if file != "" {
			try.RemoveFile(file)
		}
	}()
	if err != nil {
		return fmt.Errorf("error launching editor: %v", err)
	}

	if bytes.Equal(edited, raw) {
		fmt.Fprintln(os.Stderr, "Edit cancelled, no changes made.")
		return nil
	}

	newObj, _, err := kopscodecs.Decode(edited, nil)
	if err != nil {
		return fmt.Errorf("error parsing InstanceGroup: %v", err)
	}

	newGroup, ok := newObj.(*api.InstanceGroup)
	if !ok {
		return fmt.Errorf("object was not of expected type: %T", newObj)
	}

	err = validation.ValidateInstanceGroup(newGroup).ToAggregate()
	if err != nil {
		return err
	}

	fullGroup, err := cloudup.PopulateInstanceGroupSpec(cluster, newGroup, channel)
	if err != nil {
		return err
	}

	// We need the full cluster spec to perform deep validation
	// Note that we don't write it back though
	err = cloudup.PerformAssignments(cluster)
	if err != nil {
		return fmt.Errorf("error populating configuration: %v", err)
	}

	assetBuilder := assets.NewAssetBuilder(cluster, "")
	fullCluster, err := cloudup.PopulateClusterSpec(clientset, cluster, assetBuilder)
	if err != nil {
		return err
	}

	err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster).ToAggregate()
	if err != nil {
		return err
	}

	// Note we perform as much validation as we can, before writing a bad config
	_, err = clientset.InstanceGroupsFor(cluster).Update(ctx, fullGroup, metav1.UpdateOptions{})
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Remove redundant ValidateInstanceGroup call<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/klog"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/apis/kops/validation"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/pkg/kopscodecs"
	"k8s.io/kops/pkg/try"
	"k8s.io/kops/upup/pkg/fi/cloudup"
	"k8s.io/kubectl/pkg/cmd/util/editor"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

var (
	editInstancegroupLong = templates.LongDesc(i18n.T(`Edit a cluster configuration.

	This command changes the instancegroup desired configuration in the registry.

    	To set your preferred editor, you can define the EDITOR environment variable.
    	When you have done this, kops will use the editor that you have set.

	kops edit does not update the cloud resources, to apply the changes use "kops update cluster".`))

	editInstancegroupExample = templates.Examples(i18n.T(`
	# Edit an instancegroup desired configuration.
	kops edit ig --name k8s-cluster.example.com nodes --state=s3://kops-state-1234
	`))

	editInstancegroupShort = i18n.T(`Edit instancegroup.`)
)

type EditInstanceGroupOptions struct {
}

func NewCmdEditInstanceGroup(f *util.Factory, out io.Writer) *cobra.Command {
	options := &EditInstanceGroupOptions{}

	cmd := &cobra.Command{
		Use:     "instancegroup",
		Aliases: []string{"instancegroups", "ig"},
		Short:   editInstancegroupShort,
		Long:    editInstancegroupLong,
		Example: editInstancegroupExample,
		Run: func(cmd *cobra.Command, args []string) {
			ctx := context.TODO()

			err := RunEditInstanceGroup(ctx, f, cmd, args, os.Stdout, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	return cmd
}

func RunEditInstanceGroup(ctx context.Context, f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, options *EditInstanceGroupOptions) error {
	if len(args) == 0 {
		return fmt.Errorf("Specify name of instance group to edit")
	}
	if len(args) != 1 {
		return fmt.Errorf("Can only edit one instance group at a time")
	}

	groupName := args[0]

	cluster, err := rootCommand.Cluster(ctx)
	if err != nil {
		return err
	}

	channel, err := cloudup.ChannelForCluster(cluster)
	if err != nil {
		klog.Warningf("%v", err)
	}

	clientset, err := rootCommand.Clientset()
	if err != nil {
		return err
	}

	if groupName == "" {
		return fmt.Errorf("name is required")
	}

	oldGroup, err := clientset.InstanceGroupsFor(cluster).Get(ctx, groupName, metav1.GetOptions{})
	if err != nil {
		return fmt.Errorf("error reading InstanceGroup %q: %v", groupName, err)
	}
	if oldGroup == nil {
		return fmt.Errorf("InstanceGroup %q not found", groupName)
	}

	var (
		edit = editor.NewDefaultEditor(editorEnvs)
	)

	ext := "yaml"
	raw, err := kopscodecs.ToVersionedYaml(oldGroup)
	if err != nil {
		return err
	}

	// launch the editor
	edited, file, err := edit.LaunchTempFile(fmt.Sprintf("%s-edit-", filepath.Base(os.Args[0])), ext, bytes.NewReader(raw))
	defer func() {
		if file != "" {
			try.RemoveFile(file)
		}
	}()
	if err != nil {
		return fmt.Errorf("error launching editor: %v", err)
	}

	if bytes.Equal(edited, raw) {
		fmt.Fprintln(os.Stderr, "Edit cancelled, no changes made.")
		return nil
	}

	newObj, _, err := kopscodecs.Decode(edited, nil)
	if err != nil {
		return fmt.Errorf("error parsing InstanceGroup: %v", err)
	}

	newGroup, ok := newObj.(*api.InstanceGroup)
	if !ok {
		return fmt.Errorf("object was not of expected type: %T", newObj)
	}

	fullGroup, err := cloudup.PopulateInstanceGroupSpec(cluster, newGroup, channel)
	if err != nil {
		return err
	}

	// We need the full cluster spec to perform deep validation
	// Note that we don't write it back though
	err = cloudup.PerformAssignments(cluster)
	if err != nil {
		return fmt.Errorf("error populating configuration: %v", err)
	}

	assetBuilder := assets.NewAssetBuilder(cluster, "")
	fullCluster, err := cloudup.PopulateClusterSpec(clientset, cluster, assetBuilder)
	if err != nil {
		return err
	}

	err = validation.CrossValidateInstanceGroup(fullGroup, fullCluster).ToAggregate()
	if err != nil {
		return err
	}

	// Note we perform as much validation as we can, before writing a bad config
	_, err = clientset.InstanceGroupsFor(cluster).Update(ctx, fullGroup, metav1.UpdateOptions{})
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/spf13/pflag"

	"k8s.io/kubectl/pkg/util/templates"
	"k8s.io/kubernetes/test/e2e/upgrades"

	"github.com/openshift/origin/pkg/test/ginkgo"
	"github.com/openshift/origin/test/e2e/upgrade"
	"github.com/openshift/origin/test/extended/util/disruption/controlplane"
)

// upgradeSuites are all known upgade test suites this binary should run
var upgradeSuites = []*ginkgo.TestSuite{
	{
		Name: "all",
		Description: templates.LongDesc(`
		Run all tests.
		`),
		Matches: func(name string) bool {
			return strings.Contains(name, "[Feature:ClusterUpgrade]") && strings.Contains(name, "[Suite:openshift]")
		},
		Init: func(opt map[string]string) error {
			return upgradeInitArguments(opt, func(string) bool { return true })
		},
		TestTimeout: 240 * time.Minute,
	},
	{
		Name: "platform",
		Description: templates.LongDesc(`
		Run only the tests that verify the platform remains available.
		`),
		Matches: func(name string) bool {
			return strings.Contains(name, "[Feature:ClusterUpgrade]") && strings.Contains(name, "[Suite:openshift]")
		},
		Init: func(opt map[string]string) error {
			return upgradeInitArguments(opt, func(name string) bool {
				return name == controlplane.NewKubeAvailableTest().Name() || name == controlplane.NewKubeAvailableTest().Name()
			})
		},
		TestTimeout: 240 * time.Minute,
	},
}

func upgradeInitArguments(opt map[string]string, filterFn func(name string) bool) error {
	for k, v := range opt {
		switch k {
		case "abort-at":
			if err := upgrade.SetUpgradeAbortAt(v); err != nil {
				return err
			}
		case "disrupt-reboot":
			if err := upgrade.SetUpgradeDisruptReboot(v); err != nil {
				return err
			}
		default:
			return fmt.Errorf("unrecognized upgrade option: %s", k)
		}
	}
	return filterUpgrade(upgrade.AllTests(), filterFn)
}

type UpgradeOptions struct {
	Suite    string
	ToImage  string
	JUnitDir string

	TestOptions []string
}

func (o *UpgradeOptions) OptionsMap() (map[string]string, error) {
	options := make(map[string]string)
	for _, option := range o.TestOptions {
		parts := strings.SplitN(option, "=", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("test option %q is not valid, must be KEY=VALUE", option)
		}
		if len(parts[0]) == 0 {
			return nil, fmt.Errorf("test option %q is not valid, must be KEY=VALUE", option)
		}
		_, exists := options[parts[0]]
		if exists {
			return nil, fmt.Errorf("option %q declared twice", parts[0])
		}
		options[parts[0]] = parts[1]
	}
	return options, nil
}

func (o *UpgradeOptions) ToEnv() string {
	out, err := json.Marshal(o)
	if err != nil {
		panic(err)
	}
	return string(out)
}

func initUpgrade(value string) (*UpgradeOptions, error) {
	var opt UpgradeOptions
	if len(value) == 0 {
		return &opt, nil
	}
	if err := json.Unmarshal([]byte(value), &opt); err != nil {
		return nil, err
	}
	for _, suite := range upgradeSuites {
		if suite.Name == opt.Suite {
			o, err := opt.OptionsMap()
			if err != nil {
				return nil, err
			}
			if suite.Init != nil {
				if err := suite.Init(o); err != nil {
					return nil, err
				}
			}
			upgrade.SetToImage(opt.ToImage)
			return &opt, nil
		}
	}
	return nil, fmt.Errorf("unrecognized upgrade info")
}

func filterUpgrade(tests []upgrades.Test, match func(string) bool) error {
	var scope []upgrades.Test
	for _, test := range tests {
		if match(test.Name()) {
			scope = append(scope, test)
		}
	}
	upgrade.SetTests(scope)
	return nil
}

func bindUpgradeOptions(opt *UpgradeOptions, flags *pflag.FlagSet) {
	flags.StringVar(&opt.ToImage, "to-image", opt.ToImage, "Specify the image to test an upgrade to.")
	flags.StringSliceVar(&opt.TestOptions, "options", opt.TestOptions, "A set of KEY=VALUE options to control the test. See the help text.")
}
<commit_msg>Update upgrade testmatching from [Suite:openshift] => not [Suite:k8s]<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/spf13/pflag"

	"k8s.io/kubectl/pkg/util/templates"
	"k8s.io/kubernetes/test/e2e/upgrades"

	"github.com/openshift/origin/pkg/test/ginkgo"
	"github.com/openshift/origin/test/e2e/upgrade"
	"github.com/openshift/origin/test/extended/util/disruption/controlplane"
)

// upgradeSuites are all known upgade test suites this binary should run
var upgradeSuites = []*ginkgo.TestSuite{
	{
		Name: "all",
		Description: templates.LongDesc(`
		Run all tests.
		`),
		Matches: func(name string) bool {
			return strings.Contains(name, "[Feature:ClusterUpgrade]") && !strings.Contains(name, "[Suite:k8s]")
		},
		Init: func(opt map[string]string) error {
			return upgradeInitArguments(opt, func(string) bool { return true })
		},
		TestTimeout: 240 * time.Minute,
	},
	{
		Name: "platform",
		Description: templates.LongDesc(`
		Run only the tests that verify the platform remains available.
		`),
		Matches: func(name string) bool {
			return strings.Contains(name, "[Feature:ClusterUpgrade]") && !strings.Contains(name, "[Suite:k8s]")
		},
		Init: func(opt map[string]string) error {
			return upgradeInitArguments(opt, func(name string) bool {
				return name == controlplane.NewKubeAvailableTest().Name() || name == controlplane.NewKubeAvailableTest().Name()
			})
		},
		TestTimeout: 240 * time.Minute,
	},
}

func upgradeInitArguments(opt map[string]string, filterFn func(name string) bool) error {
	for k, v := range opt {
		switch k {
		case "abort-at":
			if err := upgrade.SetUpgradeAbortAt(v); err != nil {
				return err
			}
		case "disrupt-reboot":
			if err := upgrade.SetUpgradeDisruptReboot(v); err != nil {
				return err
			}
		default:
			return fmt.Errorf("unrecognized upgrade option: %s", k)
		}
	}
	return filterUpgrade(upgrade.AllTests(), filterFn)
}

type UpgradeOptions struct {
	Suite    string
	ToImage  string
	JUnitDir string

	TestOptions []string
}

func (o *UpgradeOptions) OptionsMap() (map[string]string, error) {
	options := make(map[string]string)
	for _, option := range o.TestOptions {
		parts := strings.SplitN(option, "=", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("test option %q is not valid, must be KEY=VALUE", option)
		}
		if len(parts[0]) == 0 {
			return nil, fmt.Errorf("test option %q is not valid, must be KEY=VALUE", option)
		}
		_, exists := options[parts[0]]
		if exists {
			return nil, fmt.Errorf("option %q declared twice", parts[0])
		}
		options[parts[0]] = parts[1]
	}
	return options, nil
}

func (o *UpgradeOptions) ToEnv() string {
	out, err := json.Marshal(o)
	if err != nil {
		panic(err)
	}
	return string(out)
}

func initUpgrade(value string) (*UpgradeOptions, error) {
	var opt UpgradeOptions
	if len(value) == 0 {
		return &opt, nil
	}
	if err := json.Unmarshal([]byte(value), &opt); err != nil {
		return nil, err
	}
	for _, suite := range upgradeSuites {
		if suite.Name == opt.Suite {
			o, err := opt.OptionsMap()
			if err != nil {
				return nil, err
			}
			if suite.Init != nil {
				if err := suite.Init(o); err != nil {
					return nil, err
				}
			}
			upgrade.SetToImage(opt.ToImage)
			return &opt, nil
		}
	}
	return nil, fmt.Errorf("unrecognized upgrade info")
}

func filterUpgrade(tests []upgrades.Test, match func(string) bool) error {
	var scope []upgrades.Test
	for _, test := range tests {
		if match(test.Name()) {
			scope = append(scope, test)
		}
	}
	upgrade.SetTests(scope)
	return nil
}

func bindUpgradeOptions(opt *UpgradeOptions, flags *pflag.FlagSet) {
	flags.StringVar(&opt.ToImage, "to-image", opt.ToImage, "Specify the image to test an upgrade to.")
	flags.StringSliceVar(&opt.TestOptions, "options", opt.TestOptions, "A set of KEY=VALUE options to control the test. See the help text.")
}
<|endoftext|>


<commit_before>package options

import "time"

const DEFAULT_MAX_RETRY_ATTEMPTS = 3
const DEFAULT_SLEEP = 5 * time.Second

// List of recurring transient errors encountered when calling terraform
// If any of these match, we'll retry the command
var RETRYABLE_ERRORS = []string{
	"(?s).*Failed to load state.*tcp.*timeout.*",
	"(?s).*Failed to load backend.*TLS handshake timeout.*",
	"(?s).*Creating metric alarm failed.*request to update this alarm is in progress.*",
	"(?s).*Error installing provider.*TLS handshake timeout.*",
	"(?s).*Error configuring the backend.*TLS handshake timeout.*",
	"(?s).*Error installing provider.*tcp.*timeout.*",
	"(?s).*Error installing provider "*tcp*->*connection reset by peer.*",
	"NoSuchBucket: The specified bucket does not exist",
}
<commit_msg>added .<commit_after>package options

import "time"

const DEFAULT_MAX_RETRY_ATTEMPTS = 3
const DEFAULT_SLEEP = 5 * time.Second

// List of recurring transient errors encountered when calling terraform
// If any of these match, we'll retry the command
var RETRYABLE_ERRORS = []string{
	"(?s).*Failed to load state.*tcp.*timeout.*",
	"(?s).*Failed to load backend.*TLS handshake timeout.*",
	"(?s).*Creating metric alarm failed.*request to update this alarm is in progress.*",
	"(?s).*Error installing provider.*TLS handshake timeout.*",
	"(?s).*Error configuring the backend.*TLS handshake timeout.*",
	"(?s).*Error installing provider.*tcp.*timeout.*",
	"(?s).*Error installing provider.*tcp.*connection reset by peer.*",
	"NoSuchBucket: The specified bucket does not exist",
}
<|endoftext|>


<commit_before>package orderbook_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	ob "github.com/mdebrouwer/exchange/orderbook"
)

var _ = Describe("PriceLevel", func() {
	var (
		priceLevel *ob.PriceLevel
	)

	BeforeEach(func() {
		priceLevel = ob.NewPriceLevel(100)
	})

	Describe("Creating a new PriceLevel", func() {
		It("should have the correct price", func() {
			Expect(priceLevel.GetPrice()).Should(BeNumerically("==", 100))
		})
		It("should have empty Bids", func() {
			var bids = priceLevel.GetBids()
			Expect(bids).ShouldNot(BeNil())
			Expect(len(bids)).To(Equal(0))
		})
		It("should have empty Asks", func() {
			Expect(priceLevel.GetAsks()).ShouldNot(BeNil())
			Expect(len(priceLevel.GetAsks())).To(Equal(0))
		})
	})

	Describe("Inserting a new Order to empty PriceLevel", func() {
		Context("If side is BUY", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.BUY, 100, 1))
			})
			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})
			It("should be added to the PriceLevel and available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(1))
			})
			It("should be added to the PriceLevel and not available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(0))
			})
		})

		Context("If side is SELL", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.SELL, 100, 1))
			})
			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})
			It("should be added to the PriceLevel and available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(1))
			})
			It("should be added to the PriceLevel and not available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(0))
			})
		})

		Context("If the Order price does not match the level", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.SELL, 101, 1))
			})
			It("should error", func() {
				Expect(err).To(HaveOccurred())
			})
			It("not be available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(0))
			})
			It("not available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(0))
			})
		})
	})
})
<commit_msg>Added tests for Delete<commit_after>package orderbook_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	ob "github.com/mdebrouwer/exchange/orderbook"
)

var _ = Describe("PriceLevel", func() {
	var (
		priceLevel *ob.PriceLevel
	)

	BeforeEach(func() {
		priceLevel = ob.NewPriceLevel(100)
	})

	Describe("Creating a new PriceLevel", func() {
		It("should have the correct price", func() {
			Expect(priceLevel.GetPrice()).Should(BeNumerically("==", 100))
		})
		It("should have empty Bids", func() {
			var bids = priceLevel.GetBids()
			Expect(bids).ShouldNot(BeNil())
			Expect(len(bids)).To(Equal(0))
		})
		It("should have empty Asks", func() {
			Expect(priceLevel.GetAsks()).ShouldNot(BeNil())
			Expect(len(priceLevel.GetAsks())).To(Equal(0))
		})
	})

	Describe("Inserting a new Order to empty PriceLevel", func() {
		Context("If side is BUY", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.BUY, 100, 1))
			})
			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})
			It("should not cause trades", func() {
				Expect(trades).Should(BeNil())
			})
			It("should be added to the PriceLevel and available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(1))
			})
			It("should be added to the PriceLevel and not available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(0))
			})
		})

		Context("If side is SELL", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.SELL, 100, 1))
			})

			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("should not cause trades", func() {
				Expect(trades).Should(BeNil())
			})

			It("should be added to the PriceLevel and available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(1))
			})

			It("should be added to the PriceLevel and not available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(0))
			})
		})

		Context("If the Order price does not match the level", func() {
			var err error
			var trades []*ob.Trade
			BeforeEach(func() {
				trades, err = priceLevel.InsertOrder(ob.NewOrder("CPTY1", ob.SELL, 101, 1))
			})

			It("should error", func() {
				Expect(err).To(HaveOccurred())
			})

			It("should not cause trades", func() {
				Expect(trades).Should(BeNil())
			})

			It("not be available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(bids).ShouldNot(BeNil())
				Expect(len(bids)).To(Equal(0))
			})

			It("not available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(asks).ShouldNot(BeNil())
				Expect(len(asks)).To(Equal(0))
			})
		})
	})

	Describe("Deleting an Order", func() {
		var err error
		var buyOrder *ob.Order = ob.NewOrder("CPTY2", ob.BUY, 100, 1)
		var sellOrder *ob.Order = ob.NewOrder("CPTY2", ob.SELL, 101, 1)
		BeforeEach(func() {
			_, err = priceLevel.InsertOrder(buyOrder)
			_, err = priceLevel.InsertOrder(sellOrder)
		})

		Context("If side is BUY", func() {
			var err error
			BeforeEach(func() {
				err = priceLevel.DeleteOrder(buyOrder.OrderId())
			})

			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("should be removed from the PriceLevel and not available from GetBids", func() {
				var bids = priceLevel.GetBids()
				Expect(len(bids)).To(Equal(0))
			})
		})

		Context("If side is SELL", func() {
			var err error
			BeforeEach(func() {
				err = priceLevel.DeleteOrder(sellOrder.OrderId())
			})

			It("should not error", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("should be removed from the PriceLevel and not available from GetAsks", func() {
				var asks = priceLevel.GetAsks()
				Expect(len(asks)).To(Equal(0))
			})
		})

		Context("If the Order does not exist", func() {
			var err error
			BeforeEach(func() {
				err = priceLevel.DeleteOrder(7)
			})

			It("should error", func() {
				Expect(err).To(HaveOccurred())
			})
		})
	})
})
<|endoftext|>


<commit_before>package peerreader

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peerprotocol"
	"github.com/cenkalti/rain/internal/piece"
)

const (
	// maxBlockSize is the max size of block data that we accept from peers.
	maxBlockSize = 16 * 1024
	// time to wait for a message. peer must send keep-alive messages to keep connection alive.
	readTimeout = 2 * time.Minute
)

var PiecePool = sync.Pool{
	New: func() interface{} {
		return make([]byte, piece.BlockSize)
	},
}

type PeerReader struct {
	conn         net.Conn
	buf          *bufio.Reader
	log          logger.Logger
	pieceTimeout time.Duration
	messages     chan interface{}
	stopC        chan struct{}
	doneC        chan struct{}
}

func New(conn net.Conn, l logger.Logger, pieceTimeout time.Duration, bufferSize int) *PeerReader {
	return &PeerReader{
		conn:         conn,
		buf:          bufio.NewReaderSize(conn, bufferSize),
		log:          l,
		pieceTimeout: pieceTimeout,
		messages:     make(chan interface{}),
		stopC:        make(chan struct{}),
		doneC:        make(chan struct{}),
	}
}

func (p *PeerReader) Messages() <-chan interface{} {
	return p.messages
}

func (p *PeerReader) Stop() {
	close(p.stopC)
}

func (p *PeerReader) Done() chan struct{} {
	return p.doneC
}

func (p *PeerReader) Run() {
	defer close(p.doneC)

	var err error
	defer func() {
		if err == nil {
			return
		} else if err == io.EOF { // peer closed the connection
			return
		} else if err == io.ErrUnexpectedEOF {
			return
		} else if _, ok := err.(*net.OpError); ok {
			return
		}
		select {
		case <-p.stopC: // don't log error if peer is stopped
		default:
			p.log.Error(err)
		}
	}()

	first := true
	for {
		err = p.conn.SetReadDeadline(time.Now().Add(readTimeout))
		if err != nil {
			return
		}

		var length uint32
		// p.log.Debug("Reading message...")
		err = binary.Read(p.buf, binary.BigEndian, &length)
		if err != nil {
			return
		}
		// p.log.Debugf("Received message of length: %d", length)

		if length == 0 { // keep-alive message
			p.log.Debug("Received message of type \"keep alive\"")
			continue
		}

		var id peerprotocol.MessageID
		err = binary.Read(p.buf, binary.BigEndian, &id)
		if err != nil {
			return
		}
		length--

		// p.log.Debugf("Received message of type: %q", id)

		var msg interface{}

		switch id {
		case peerprotocol.Choke:
			msg = peerprotocol.ChokeMessage{}
		case peerprotocol.Unchoke:
			msg = peerprotocol.UnchokeMessage{}
		case peerprotocol.Interested:
			msg = peerprotocol.InterestedMessage{}
		case peerprotocol.NotInterested:
			msg = peerprotocol.NotInterestedMessage{}
		case peerprotocol.Have:
			var hm peerprotocol.HaveMessage
			err = binary.Read(p.buf, binary.BigEndian, &hm)
			if err != nil {
				return
			}
			msg = hm
		case peerprotocol.Bitfield:
			if !first {
				err = errors.New("bitfield can only be sent after handshake")
				return
			}
			var bm peerprotocol.BitfieldMessage
			bm.Data = make([]byte, length)
			_, err = io.ReadFull(p.buf, bm.Data)
			if err != nil {
				return
			}
			msg = bm
		case peerprotocol.Request:
			var rm peerprotocol.RequestMessage
			err = binary.Read(p.buf, binary.BigEndian, &rm)
			if err != nil {
				return
			}
			// p.log.Debugf("Received Request: %+v", rm)

			if rm.Length > maxBlockSize {
				err = errors.New("received a request with block size larger than allowed")
				return
			}
			msg = rm
		case peerprotocol.Reject:
			var rm peerprotocol.RejectMessage
			err = binary.Read(p.buf, binary.BigEndian, &rm)
			if err != nil {
				return
			}
			p.log.Debugf("Received Reject: %+v", rm)
			msg = rm
		case peerprotocol.Cancel:
			var cm peerprotocol.CancelMessage
			err = binary.Read(p.buf, binary.BigEndian, &cm)
			if err != nil {
				return
			}
			if cm.Length > maxBlockSize {
				err = errors.New("received a cancel with block size larger than allowed")
				return
			}
			msg = cm
		case peerprotocol.Piece:
			var pm peerprotocol.PieceMessage
			err = binary.Read(p.buf, binary.BigEndian, &pm)
			if err != nil {
				return
			}
			var m, n int
			b := PiecePool.Get().([]byte)[:length-8]
			for {
				err = p.conn.SetReadDeadline(time.Now().Add(p.pieceTimeout))
				if err != nil {
					return
				}
				n, err = io.ReadFull(p.buf, b)
				if err != nil {
					if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
						// Peer couldn't send the block in allowed time.
						if n == 0 {
							return
						}
						m += n
						b = b[n:]
						continue
					}
					return
				}
				break
			}
			msg = Piece{PieceMessage: pm, Data: b}
		case peerprotocol.HaveAll:
			if !first {
				err = errors.New("have_all can only be sent after handshake")
				return
			}
			msg = peerprotocol.HaveAllMessage{}
		case peerprotocol.HaveNone:
			if !first {
				err = errors.New("have_none can only be sent after handshake")
				return
			}
			msg = peerprotocol.HaveNoneMessage{}
		case peerprotocol.AllowedFast:
			var am peerprotocol.AllowedFastMessage
			err = binary.Read(p.buf, binary.BigEndian, &am)
			if err != nil {
				return
			}
			msg = am
		case peerprotocol.Extension:
			buf := make([]byte, length)
			_, err = io.ReadFull(p.buf, buf)
			if err != nil {
				return
			}
			var em peerprotocol.ExtensionMessage
			err = em.UnmarshalBinary(buf)
			if err != nil {
				return
			}
			msg = em.Payload
		default:
			p.log.Debugf("unhandled message type: %s", id)
			p.log.Debugln("Discarding", length, "bytes...")
			_, err = io.CopyN(ioutil.Discard, p.buf, int64(length))
			if err != nil {
				return
			}
			continue
		}
		if msg == nil {
			panic("msg unset")
		}
		// Only message types defined in BEP 3 are counted.
		if id < 9 {
			first = false
		}
		select {
		case p.messages <- msg:
		case <-p.stopC:
			return
		}
	}
}
<commit_msg>increase max accepted block size<commit_after>package peerreader

import (
	"bufio"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/peerprotocol"
	"github.com/cenkalti/rain/internal/piece"
)

const (
	// maxBlockSize is the max size of block data that we accept from peers.
	maxBlockSize = 32 * 1024
	// time to wait for a message. peer must send keep-alive messages to keep connection alive.
	readTimeout = 2 * time.Minute
)

var PiecePool = sync.Pool{
	New: func() interface{} {
		return make([]byte, piece.BlockSize)
	},
}

type PeerReader struct {
	conn         net.Conn
	buf          *bufio.Reader
	log          logger.Logger
	pieceTimeout time.Duration
	messages     chan interface{}
	stopC        chan struct{}
	doneC        chan struct{}
}

func New(conn net.Conn, l logger.Logger, pieceTimeout time.Duration, bufferSize int) *PeerReader {
	return &PeerReader{
		conn:         conn,
		buf:          bufio.NewReaderSize(conn, bufferSize),
		log:          l,
		pieceTimeout: pieceTimeout,
		messages:     make(chan interface{}),
		stopC:        make(chan struct{}),
		doneC:        make(chan struct{}),
	}
}

func (p *PeerReader) Messages() <-chan interface{} {
	return p.messages
}

func (p *PeerReader) Stop() {
	close(p.stopC)
}

func (p *PeerReader) Done() chan struct{} {
	return p.doneC
}

func (p *PeerReader) Run() {
	defer close(p.doneC)

	var err error
	defer func() {
		if err == nil {
			return
		} else if err == io.EOF { // peer closed the connection
			return
		} else if err == io.ErrUnexpectedEOF {
			return
		} else if _, ok := err.(*net.OpError); ok {
			return
		}
		select {
		case <-p.stopC: // don't log error if peer is stopped
		default:
			p.log.Error(err)
		}
	}()

	first := true
	for {
		err = p.conn.SetReadDeadline(time.Now().Add(readTimeout))
		if err != nil {
			return
		}

		var length uint32
		// p.log.Debug("Reading message...")
		err = binary.Read(p.buf, binary.BigEndian, &length)
		if err != nil {
			return
		}
		// p.log.Debugf("Received message of length: %d", length)

		if length == 0 { // keep-alive message
			p.log.Debug("Received message of type \"keep alive\"")
			continue
		}

		var id peerprotocol.MessageID
		err = binary.Read(p.buf, binary.BigEndian, &id)
		if err != nil {
			return
		}
		length--

		// p.log.Debugf("Received message of type: %q", id)

		var msg interface{}

		switch id {
		case peerprotocol.Choke:
			msg = peerprotocol.ChokeMessage{}
		case peerprotocol.Unchoke:
			msg = peerprotocol.UnchokeMessage{}
		case peerprotocol.Interested:
			msg = peerprotocol.InterestedMessage{}
		case peerprotocol.NotInterested:
			msg = peerprotocol.NotInterestedMessage{}
		case peerprotocol.Have:
			var hm peerprotocol.HaveMessage
			err = binary.Read(p.buf, binary.BigEndian, &hm)
			if err != nil {
				return
			}
			msg = hm
		case peerprotocol.Bitfield:
			if !first {
				err = errors.New("bitfield can only be sent after handshake")
				return
			}
			var bm peerprotocol.BitfieldMessage
			bm.Data = make([]byte, length)
			_, err = io.ReadFull(p.buf, bm.Data)
			if err != nil {
				return
			}
			msg = bm
		case peerprotocol.Request:
			var rm peerprotocol.RequestMessage
			err = binary.Read(p.buf, binary.BigEndian, &rm)
			if err != nil {
				return
			}
			// p.log.Debugf("Received Request: %+v", rm)

			if rm.Length > maxBlockSize {
				err = errors.New("received a request with block size larger than allowed")
				return
			}
			msg = rm
		case peerprotocol.Reject:
			var rm peerprotocol.RejectMessage
			err = binary.Read(p.buf, binary.BigEndian, &rm)
			if err != nil {
				return
			}
			p.log.Debugf("Received Reject: %+v", rm)
			msg = rm
		case peerprotocol.Cancel:
			var cm peerprotocol.CancelMessage
			err = binary.Read(p.buf, binary.BigEndian, &cm)
			if err != nil {
				return
			}
			if cm.Length > maxBlockSize {
				err = errors.New("received a cancel with block size larger than allowed")
				return
			}
			msg = cm
		case peerprotocol.Piece:
			var pm peerprotocol.PieceMessage
			err = binary.Read(p.buf, binary.BigEndian, &pm)
			if err != nil {
				return
			}
			var m, n int
			b := PiecePool.Get().([]byte)[:length-8]
			for {
				err = p.conn.SetReadDeadline(time.Now().Add(p.pieceTimeout))
				if err != nil {
					return
				}
				n, err = io.ReadFull(p.buf, b)
				if err != nil {
					if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
						// Peer couldn't send the block in allowed time.
						if n == 0 {
							return
						}
						m += n
						b = b[n:]
						continue
					}
					return
				}
				break
			}
			msg = Piece{PieceMessage: pm, Data: b}
		case peerprotocol.HaveAll:
			if !first {
				err = errors.New("have_all can only be sent after handshake")
				return
			}
			msg = peerprotocol.HaveAllMessage{}
		case peerprotocol.HaveNone:
			if !first {
				err = errors.New("have_none can only be sent after handshake")
				return
			}
			msg = peerprotocol.HaveNoneMessage{}
		case peerprotocol.AllowedFast:
			var am peerprotocol.AllowedFastMessage
			err = binary.Read(p.buf, binary.BigEndian, &am)
			if err != nil {
				return
			}
			msg = am
		case peerprotocol.Extension:
			buf := make([]byte, length)
			_, err = io.ReadFull(p.buf, buf)
			if err != nil {
				return
			}
			var em peerprotocol.ExtensionMessage
			err = em.UnmarshalBinary(buf)
			if err != nil {
				return
			}
			msg = em.Payload
		default:
			p.log.Debugf("unhandled message type: %s", id)
			p.log.Debugln("Discarding", length, "bytes...")
			_, err = io.CopyN(ioutil.Discard, p.buf, int64(length))
			if err != nil {
				return
			}
			continue
		}
		if msg == nil {
			panic("msg unset")
		}
		// Only message types defined in BEP 3 are counted.
		if id < 9 {
			first = false
		}
		select {
		case p.messages <- msg:
		case <-p.stopC:
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package presentation

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"testing"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/loader"
	"github.com/open-policy-agent/opa/rego"
	"github.com/open-policy-agent/opa/storage"
	"github.com/open-policy-agent/opa/storage/inmem"
	"github.com/open-policy-agent/opa/util/test"
)

type testErrorWithMarshaller struct {
	msg string
}

func (t *testErrorWithMarshaller) Error() string {
	return t.msg
}

func (t *testErrorWithMarshaller) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Text string `json:"text"`
	}{
		Text: t.msg,
	})
}

type testErrorWithDetails struct{}

func (*testErrorWithDetails) Error() string   { return "something went wrong" }
func (*testErrorWithDetails) Lines() []string { return []string{"oh", "so", "wrong"} }

func validateJSONOutput(t *testing.T, testErr error, expected string) {
	t.Helper()
	output := Output{Errors: NewOutputErrors(testErr)}
	var buf bytes.Buffer
	err := JSON(&buf, output)
	if err != nil {
		t.Fatalf("Unexpected error: %s", err)
	}

	if buf.String() != expected {
		t.Fatalf("Unexpected marshalled error value.\n Expected (len=%d):\n>>>\n%s\n<<<\n\nActual (len=%d):\n>>>>\n%s\n<<<<\n",
			len(expected), expected, len(buf.String()), buf.String())
	}
}

func TestOutputJSONErrorUnstructured(t *testing.T) {
	err := errors.New("some text")
	expected := `{
  "errors": [
    {
      "message": "some text"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorCustomMarshaller(t *testing.T) {
	err := &testErrorWithMarshaller{
		msg: "custom message",
	}
	expected := `{
  "errors": [
    {
      "message": "custom message"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorWithDetails(t *testing.T) {
	err := &testErrorWithDetails{}
	expected := `{
  "errors": [
    {
      "message": "something went wrong",
      "details": "oh\nso\nwrong"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredASTErr(t *testing.T) {
	err := &ast.Error{
		Code:    "1",
		Message: "error message",
	}
	expected := `{
  "errors": [
    {
      "message": "error message",
      "code": "1"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredStorageErr(t *testing.T) {
	store := inmem.New()
	txn := storage.NewTransactionOrDie(context.Background(), store)
	err := store.Write(context.Background(), txn, storage.AddOp, storage.Path{}, map[string]interface{}{"foo": 1})
	expected := `{
  "errors": [
    {
      "message": "data write during read transaction",
      "code": "storage_invalid_txn_error"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredTopdownErr(t *testing.T) {
	mod := `
		package test

		p(x) = y {
			y = x[_]
		}

		z := p([1, 2, 3])
		`

	_, err := rego.New(
		rego.Module("test.rego", mod),
		rego.Query("data.test.z"),
	).Eval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "functions must not produce multiple outputs for same inputs",
      "code": "eval_conflict_error",
      "location": {
        "file": "test.rego",
        "row": 4,
        "col": 3
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredAstErr(t *testing.T) {
	_, err := rego.New(rego.Query("count(0)")).Eval(context.Background())
	expected := `{
  "errors": [
    {
      "message": "count: invalid argument(s)",
      "code": "rego_type_error",
      "location": {
        "file": "",
        "row": 1,
        "col": 1
      },
      "details": {
        "have": [
          {
            "type": "number"
          },
          null
        ],
        "want": [
          {
            "of": [
              {
                "type": "string"
              },
              {
                "dynamic": {
                  "type": "any"
                },
                "type": "array"
              },
              {
                "dynamic": {
                  "key": {
                    "type": "any"
                  },
                  "value": {
                    "type": "any"
                  }
                },
                "type": "object"
              },
              {
                "of": {
                  "type": "any"
                },
                "type": "set"
              }
            ],
            "type": "any"
          },
          {
            "type": "number"
          }
        ]
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredAstParseErr(t *testing.T) {
	_, err := rego.New(
		rego.Module("parse-err.rego", "!!!"),
		rego.Query("!!!"),
	).Eval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "illegal ! character",
      "code": "rego_parse_error",
      "location": {
        "file": "parse-err.rego",
        "row": 1,
        "col": 1
      },
      "details": {
        "line": "!!!",
        "idx": 0
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredASTErrList(t *testing.T) {
	c := ast.NewCompiler()
	c.Compile(map[string]*ast.Module{
		"error.rego": ast.MustParseModule(`
package test

q {
	bad[reference]
}
`)})
	c.Errors.Sort()
	err := c.Errors

	expected := `{
  "errors": [
    {
      "message": "var bad is unsafe",
      "code": "rego_unsafe_var_error",
      "location": {
        "file": "",
        "row": 5,
        "col": 2
      }
    },
    {
      "message": "var reference is unsafe",
      "code": "rego_unsafe_var_error",
      "location": {
        "file": "",
        "row": 5,
        "col": 2
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredLoaderErrList(t *testing.T) {
	files := map[string]string{
		// bundle a
		"a/data.json": "{{{",
		"b/data.json": "...",
	}

	var err error
	var tmpPath string
	test.WithTempFS(files, func(path string) {
		tmpPath = path
		_, err = loader.NewFileLoader().All([]string{path})
	})

	expected := fmt.Sprintf(`{
  "errors": [
    {
      "message": "%s/a/data.json: invalid character '{' looking for beginning of object key string"
    },
    {
      "message": "%s/b/data.json: invalid character '.' looking for beginning of value"
    }
  ]
}
`, tmpPath, tmpPath)

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredRegoErrList(t *testing.T) {
	mod := `
package test

p {
	bad_func1()
}

q {
	bad_func2()
}
`
	_, err := rego.New(
		rego.Module("error.rego", mod),
		rego.Query("data"),
	).PrepareForEval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "undefined function bad_func1",
      "code": "rego_type_error",
      "location": {
        "file": "error.rego",
        "row": 5,
        "col": 2
      }
    },
    {
      "message": "undefined function bad_func2",
      "code": "rego_type_error",
      "location": {
        "file": "error.rego",
        "row": 9,
        "col": 2
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestSource(t *testing.T) {

	buf := new(bytes.Buffer)

	err := Source(buf, Output{
		Partial: &rego.PartialQueries{
			Queries: []ast.Body{
				ast.MustParseBody("a = 1; b = 2"),
			},
			Support: []*ast.Module{
				ast.MustParseModule(`
            package test
            p = 1
        `),
			},
		},
	})
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}

	exp := `# Query 1
a = 1
b = 2

# Module 1
package test

p = 1
`

	if buf.String() != exp {
		t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String())
	}

}

func TestRaw(t *testing.T) {
	tests := []struct {
		note   string
		output Output
		want   string
	}{
		{
			note: "simple single string",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "Hello world"},
						},
					},
				},
			},
			want: "Hello world\n",
		},
		{
			note: "table format",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "one"},
							{Value: 1},
						},
					},
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "two"},
							{Value: 2},
						},
					},
				},
			},
			want: "one 1\ntwo 2\n",
		},
		{
			note: "compound values",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: []interface{}{"one"}},
							{Value: map[string]interface{}{
								"key": []interface{}{},
							}},
						},
					},
				},
			},
			want: "[\"one\"] {\"key\":[]}\n",
		},
		{
			note: "error",
			output: Output{
				Errors: NewOutputErrors(fmt.Errorf("boom")),
			},
			want: "1 error occurred: boom\n",
		},
		{
			// NOTE(sr): The presentation package outputs whatever Error() on
			// the errors it is given yields. So even though NewOutputErrors
			// will pick up the error details, they won't be output, as they
			// are not included in the error's Error() string.
			note: "error with details",
			output: Output{
				Errors: NewOutputErrors(&testErrorWithDetails{}),
			},
			want: "1 error occurred: something went wrong\noh\nso\nwrong\n",
		},
	}

	for _, tc := range tests {
		t.Run(tc.note, func(t *testing.T) {
			buf := new(bytes.Buffer)
			err := Raw(buf, tc.output)
			if err != nil {
				t.Fatalf("unexpected error: %s", err)
			}
			if buf.String() != tc.want {
				t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.want, buf.String())
			}
		})
	}
}
<commit_msg>internal/presentation: Update test with new ArgErrDetail structure<commit_after>// Copyright 2019 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package presentation

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"testing"

	"github.com/open-policy-agent/opa/util"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/loader"
	"github.com/open-policy-agent/opa/rego"
	"github.com/open-policy-agent/opa/storage"
	"github.com/open-policy-agent/opa/storage/inmem"
	"github.com/open-policy-agent/opa/util/test"
)

type testErrorWithMarshaller struct {
	msg string
}

func (t *testErrorWithMarshaller) Error() string {
	return t.msg
}

func (t *testErrorWithMarshaller) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Text string `json:"text"`
	}{
		Text: t.msg,
	})
}

type testErrorWithDetails struct{}

func (*testErrorWithDetails) Error() string   { return "something went wrong" }
func (*testErrorWithDetails) Lines() []string { return []string{"oh", "so", "wrong"} }

func validateJSONOutput(t *testing.T, testErr error, expected string) {
	t.Helper()
	output := Output{Errors: NewOutputErrors(testErr)}
	var buf bytes.Buffer
	err := JSON(&buf, output)
	if err != nil {
		t.Fatalf("Unexpected error: %s", err)
	}

	result := util.MustUnmarshalJSON(buf.Bytes())
	exp := util.MustUnmarshalJSON([]byte(expected))

	if !reflect.DeepEqual(result, exp) {
		t.Fatal("expected:", exp, "got:", result)
	}
}

func TestOutputJSONErrorUnstructured(t *testing.T) {
	err := errors.New("some text")
	expected := `{
  "errors": [
    {
      "message": "some text"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorCustomMarshaller(t *testing.T) {
	err := &testErrorWithMarshaller{
		msg: "custom message",
	}
	expected := `{
  "errors": [
    {
      "message": "custom message"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorWithDetails(t *testing.T) {
	err := &testErrorWithDetails{}
	expected := `{
  "errors": [
    {
      "message": "something went wrong",
      "details": "oh\nso\nwrong"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredASTErr(t *testing.T) {
	err := &ast.Error{
		Code:    "1",
		Message: "error message",
	}
	expected := `{
  "errors": [
    {
      "message": "error message",
      "code": "1"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredStorageErr(t *testing.T) {
	store := inmem.New()
	txn := storage.NewTransactionOrDie(context.Background(), store)
	err := store.Write(context.Background(), txn, storage.AddOp, storage.Path{}, map[string]interface{}{"foo": 1})
	expected := `{
  "errors": [
    {
      "message": "data write during read transaction",
      "code": "storage_invalid_txn_error"
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredTopdownErr(t *testing.T) {
	mod := `
		package test

		p(x) = y {
			y = x[_]
		}

		z := p([1, 2, 3])
		`

	_, err := rego.New(
		rego.Module("test.rego", mod),
		rego.Query("data.test.z"),
	).Eval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "functions must not produce multiple outputs for same inputs",
      "code": "eval_conflict_error",
      "location": {
        "file": "test.rego",
        "row": 4,
        "col": 3
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredAstErr(t *testing.T) {
	_, err := rego.New(rego.Query("count(0)")).Eval(context.Background())
	expected := `{
		"errors": [
		  {
			"message": "count: invalid argument(s)",
			"code": "rego_type_error",
			"location": {
			  "file": "",
			  "row": 1,
			  "col": 1
			},
			"details": {
			  "have": [
				{
				  "type": "number"
				},
				null
			  ],
			  "want": {
				"args": [
				  {
					"of": [
					  {
						"type": "string"
					  },
					  {
						"dynamic": {
						  "type": "any"
						},
						"type": "array"
					  },
					  {
						"dynamic": {
						  "key": {
							"type": "any"
						  },
						  "value": {
							"type": "any"
						  }
						},
						"type": "object"
					  },
					  {
						"of": {
						  "type": "any"
						},
						"type": "set"
					  }
					],
					"type": "any"
				  },
				  {
					"type": "number"
				  }
				]
			  }
			}
		  }
		]
	  }`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredAstParseErr(t *testing.T) {
	_, err := rego.New(
		rego.Module("parse-err.rego", "!!!"),
		rego.Query("!!!"),
	).Eval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "illegal ! character",
      "code": "rego_parse_error",
      "location": {
        "file": "parse-err.rego",
        "row": 1,
        "col": 1
      },
      "details": {
        "line": "!!!",
        "idx": 0
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredASTErrList(t *testing.T) {
	c := ast.NewCompiler()
	c.Compile(map[string]*ast.Module{
		"error.rego": ast.MustParseModule(`
package test

q {
	bad[reference]
}
`)})
	c.Errors.Sort()
	err := c.Errors

	expected := `{
  "errors": [
    {
      "message": "var bad is unsafe",
      "code": "rego_unsafe_var_error",
      "location": {
        "file": "",
        "row": 5,
        "col": 2
      }
    },
    {
      "message": "var reference is unsafe",
      "code": "rego_unsafe_var_error",
      "location": {
        "file": "",
        "row": 5,
        "col": 2
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredLoaderErrList(t *testing.T) {
	files := map[string]string{
		// bundle a
		"a/data.json": "{{{",
		"b/data.json": "...",
	}

	var err error
	var tmpPath string
	test.WithTempFS(files, func(path string) {
		tmpPath = path
		_, err = loader.NewFileLoader().All([]string{path})
	})

	expected := fmt.Sprintf(`{
  "errors": [
    {
      "message": "%s/a/data.json: invalid character '{' looking for beginning of object key string"
    },
    {
      "message": "%s/b/data.json: invalid character '.' looking for beginning of value"
    }
  ]
}
`, tmpPath, tmpPath)

	validateJSONOutput(t, err, expected)
}

func TestOutputJSONErrorStructuredRegoErrList(t *testing.T) {
	mod := `
package test

p {
	bad_func1()
}

q {
	bad_func2()
}
`
	_, err := rego.New(
		rego.Module("error.rego", mod),
		rego.Query("data"),
	).PrepareForEval(context.Background())

	expected := `{
  "errors": [
    {
      "message": "undefined function bad_func1",
      "code": "rego_type_error",
      "location": {
        "file": "error.rego",
        "row": 5,
        "col": 2
      }
    },
    {
      "message": "undefined function bad_func2",
      "code": "rego_type_error",
      "location": {
        "file": "error.rego",
        "row": 9,
        "col": 2
      }
    }
  ]
}
`

	validateJSONOutput(t, err, expected)
}

func TestSource(t *testing.T) {

	buf := new(bytes.Buffer)

	err := Source(buf, Output{
		Partial: &rego.PartialQueries{
			Queries: []ast.Body{
				ast.MustParseBody("a = 1; b = 2"),
			},
			Support: []*ast.Module{
				ast.MustParseModule(`
            package test
            p = 1
        `),
			},
		},
	})
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}

	exp := `# Query 1
a = 1
b = 2

# Module 1
package test

p = 1
`

	if buf.String() != exp {
		t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String())
	}

}

func TestRaw(t *testing.T) {
	tests := []struct {
		note   string
		output Output
		want   string
	}{
		{
			note: "simple single string",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "Hello world"},
						},
					},
				},
			},
			want: "Hello world\n",
		},
		{
			note: "table format",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "one"},
							{Value: 1},
						},
					},
					{
						Expressions: []*rego.ExpressionValue{
							{Value: "two"},
							{Value: 2},
						},
					},
				},
			},
			want: "one 1\ntwo 2\n",
		},
		{
			note: "compound values",
			output: Output{
				Result: []rego.Result{
					{
						Expressions: []*rego.ExpressionValue{
							{Value: []interface{}{"one"}},
							{Value: map[string]interface{}{
								"key": []interface{}{},
							}},
						},
					},
				},
			},
			want: "[\"one\"] {\"key\":[]}\n",
		},
		{
			note: "error",
			output: Output{
				Errors: NewOutputErrors(fmt.Errorf("boom")),
			},
			want: "1 error occurred: boom\n",
		},
		{
			// NOTE(sr): The presentation package outputs whatever Error() on
			// the errors it is given yields. So even though NewOutputErrors
			// will pick up the error details, they won't be output, as they
			// are not included in the error's Error() string.
			note: "error with details",
			output: Output{
				Errors: NewOutputErrors(&testErrorWithDetails{}),
			},
			want: "1 error occurred: something went wrong\noh\nso\nwrong\n",
		},
	}

	for _, tc := range tests {
		t.Run(tc.note, func(t *testing.T) {
			buf := new(bytes.Buffer)
			err := Raw(buf, tc.output)
			if err != nil {
				t.Fatalf("unexpected error: %s", err)
			}
			if buf.String() != tc.want {
				t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.want, buf.String())
			}
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2015, zheng-ji.info
*/

package monitor

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

func TestMonitor(t *testing.T) {
	fmt.Println("start")
	InitMonitor([]string{"WRITE", "READ"}, 1)
	StartMonitorServer("0.0.0.0:7070")
	timeStart := time.Now()
	time.Sleep(300 * time.Millisecond)
	StatByAction("READ", timeStart)
	fmt.Println("end")
}
<commit_msg>modify test monitor<commit_after>/*
Copyright (c) 2015, zheng-ji.info
*/

package monitor

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

func TestMonitor(t *testing.T) {
	InitMonitor([]string{"WRITE", "READ"}, 1)
	StartMonitorServer("0.0.0.0:7070")
	timeStart := time.Now()
	time.Sleep(300 * time.Millisecond)
	StatByAction("READ", timeStart)
}
<|endoftext|>


<commit_before>package movingmedian

import (
	"container/heap"
	"math"
)

type Heap []float64

func (h Heap) Len() int { return len(h) }
func (h Heap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

func (h *Heap) Push(x interface{}) {
	*h = append(*h, x.(float64))
}

func (h *Heap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

type MinHeap struct {
	Heap
}

func (h MinHeap) Less(i, j int) bool { return h.Heap[i] < h.Heap[j] }

func (h *MinHeap) RemoveValue(x float64) bool {
	for i, v := range h.Heap {
		if v == x {
			heap.Remove(h, i)
			return true
		}
	}

	return false
}

type MaxHeap struct {
	Heap
}

func (h MaxHeap) Less(i, j int) bool { return h.Heap[i] > h.Heap[j] }

func (h *MaxHeap) RemoveValue(x float64) bool {
	for i, v := range h.Heap {
		if v == x {
			heap.Remove(h, i)
			return true
		}
	}

	return false
}

type MovingMedian struct {
	size    int
	queue   []float64
	maxHeap MaxHeap
	minHeap MinHeap
}

func NewMovingMedian(size int) MovingMedian {
	m := MovingMedian{
		size:    size,
		queue:   make([]float64, 0),
		maxHeap: MaxHeap{},
		minHeap: MinHeap{},
	}

	heap.Init(&m.maxHeap)
	heap.Init(&m.minHeap)
	return m
}

func (m *MovingMedian) Push(v float64) {
	m.queue = append(m.queue, v)

	if m.minHeap.Len() == 0 ||
		v > m.minHeap.Heap[0] {
		heap.Push(&m.minHeap, v)
	} else {
		heap.Push(&m.maxHeap, v)
	}

	if len(m.queue) > m.size {
		outItem := m.queue[0]
		m.queue = m.queue[1:len(m.queue)]
		if !m.minHeap.RemoveValue(outItem) {
			m.maxHeap.RemoveValue(outItem)
		}
	}

	if m.maxHeap.Len() > (m.minHeap.Len() + 1) {
		moveItem := heap.Pop(&m.maxHeap).(float64)
		heap.Push(&m.minHeap, moveItem)
	} else if m.minHeap.Len() > (m.maxHeap.Len() + 1) {
		moveItem := heap.Pop(&m.minHeap).(float64)
		heap.Push(&m.maxHeap, moveItem)
	}
}

func (m MovingMedian) Median() float64 {
	if len(m.queue) == 0 {
		return math.NaN()
	}

	if (len(m.queue) % 2) == 0 {
		return (m.maxHeap.Heap[0] + m.minHeap.Heap[0]) / 2
	}

	if m.maxHeap.Len() > m.minHeap.Len() {
		return m.maxHeap.Heap[0]
	}

	return m.minHeap.Heap[0]
}
<commit_msg>Refactor the 2 RemoveValue funcs into a common RemoveFromHeap func<commit_after>package movingmedian

import (
	"container/heap"
	"math"
)

type HeapInterface interface {
	heap.Interface
	Data() []float64
}

type Heap []float64

func (h Heap) Len() int { return len(h) }
func (h Heap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

func (h *Heap) Push(x interface{}) {
	*h = append(*h, x.(float64))
}

func (h *Heap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

func (h Heap) Data() []float64 {
	return []float64(h)
}

func RemoveFromHeap(h HeapInterface, x float64) bool {
	for i, v := range h.Data() {
		if v == x {
			heap.Remove(h, i)
			return true
		}
	}

	return false
}

type MinHeap struct {
	Heap
}

func (h MinHeap) Less(i, j int) bool { return h.Heap[i] < h.Heap[j] }

type MaxHeap struct {
	Heap
}

func (h MaxHeap) Less(i, j int) bool { return h.Heap[i] > h.Heap[j] }

type MovingMedian struct {
	size    int
	queue   []float64
	maxHeap MaxHeap
	minHeap MinHeap
}

func NewMovingMedian(size int) MovingMedian {
	m := MovingMedian{
		size:    size,
		queue:   make([]float64, 0),
		maxHeap: MaxHeap{},
		minHeap: MinHeap{},
	}

	heap.Init(&m.maxHeap)
	heap.Init(&m.minHeap)
	return m
}

func (m *MovingMedian) Push(v float64) {
	m.queue = append(m.queue, v)

	if m.minHeap.Len() == 0 ||
		v > m.minHeap.Heap[0] {
		heap.Push(&m.minHeap, v)
	} else {
		heap.Push(&m.maxHeap, v)
	}

	if len(m.queue) > m.size {
		outItem := m.queue[0]
		m.queue = m.queue[1:len(m.queue)]
		if !RemoveFromHeap(&m.minHeap, outItem) {
			RemoveFromHeap(&m.maxHeap, outItem)
		}
	}

	if m.maxHeap.Len() > (m.minHeap.Len() + 1) {
		moveItem := heap.Pop(&m.maxHeap).(float64)
		heap.Push(&m.minHeap, moveItem)
	} else if m.minHeap.Len() > (m.maxHeap.Len() + 1) {
		moveItem := heap.Pop(&m.minHeap).(float64)
		heap.Push(&m.maxHeap, moveItem)
	}
}

func (m MovingMedian) Median() float64 {
	if len(m.queue) == 0 {
		return math.NaN()
	}

	if (len(m.queue) % 2) == 0 {
		return (m.maxHeap.Heap[0] + m.minHeap.Heap[0]) / 2
	}

	if m.maxHeap.Len() > m.minHeap.Len() {
		return m.maxHeap.Heap[0]
	}

	return m.minHeap.Heap[0]
}
<|endoftext|>


<commit_before>//
package api

import (
	"net/http"

	"github.com/gorilla/pat"

	"github.com/nanopack/sherpa/config"
)

// Start creates a new http server listner
func Start() error {

	//
	routes, err := registerRoutes()
	if err != nil {
		return err
	}

	//
	config.Log.Info("Starting sherpa server (listening on port %v)...\n", config.Options.Port)

	// blocking...
	if err := http.ListenAndServe(config.Options.Port, routes); err != nil {
		return err
	}

	return nil
}

// registerRoutes registers all api routes with the router
func registerRoutes() (*pat.Router, error) {
	config.Log.Debug("[sherpa/api] Registering routes...\n")

	//
	router := pat.New()

	//
	router.Get("/ping", func(rw http.ResponseWriter, req *http.Request) {
		rw.Write([]byte("pong"))
	})

	// templates
	router.Delete("/templates/{id}", handleRequest(deleteTemplate))
	router.Get("/templates/{id}", handleRequest(getTemplate))
	router.Post("/templates", handleRequest(postTemplate))
	router.Get("/templates", handleRequest(getTemplates))

	// builds
	router.Delete("/builds/{id}", handleRequest(deleteBuild))
	router.Get("/builds/{id}", handleRequest(getBuild))
	router.Post("/builds", handleRequest(postBuild))
	router.Get("/builds", handleRequest(getBuilds))

	return router, nil
}

// handleRequest is a wrapper for the actual route handler, simply to provide some
// debug output
func handleRequest(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {

		config.Log.Debug(`
Request:
--------------------------------------------------------------------------------
%+v

`, req)

		//
		fn(rw, req)

		config.Log.Debug(`
Response:
--------------------------------------------------------------------------------
%+v

`, rw)
	}
}

// parseBody
// func parseBody(req *http.Request, v interface{}) error {
//
// 	//
// 	b, err := ioutil.ReadAll(req.Body)
// 	if err != nil {
// 		return err
// 	}
//
// 	defer req.Body.Close()
//
// 	//
// 	if err := json.Unmarshal(b, v); err != nil {
// 		return err
// 	}
//
// 	return nil
// }

// writeBody
// func writeBody(v interface{}, rw http.ResponseWriter, status int) error {
// 	b, err := json.Marshal(v)
// 	if err != nil {
// 		return err
// 	}
//
// 	rw.Header().Set("Content-Type", "application/json")
// 	rw.WriteHeader(status)
// 	rw.Write(b)
//
// 	return nil
// }
<commit_msg>Update api.go<commit_after>//
package api

import (
	"net/http"

	"github.com/gorilla/pat"

	"github.com/nanopack/sherpa/config"
)

// Start creates a new http server listner
func Start() error {

	//
	routes, err := registerRoutes()
	if err != nil {
		return err
	}

	//
	config.Log.Info("Starting sherpa server (listening on port %v)...\n", config.Options.Port)

	// blocking...
	return http.ListenAndServe(config.Options.Port, routes)
}

// registerRoutes registers all api routes with the router
func registerRoutes() (*pat.Router, error) {
	config.Log.Debug("[sherpa/api] Registering routes...\n")

	//
	router := pat.New()

	//
	router.Get("/ping", func(rw http.ResponseWriter, req *http.Request) {
		rw.Write([]byte("pong"))
	})

	// templates
	router.Delete("/templates/{id}", handleRequest(deleteTemplate))
	router.Get("/templates/{id}", handleRequest(getTemplate))
	router.Post("/templates", handleRequest(postTemplate))
	router.Get("/templates", handleRequest(getTemplates))

	// builds
	router.Delete("/builds/{id}", handleRequest(deleteBuild))
	router.Get("/builds/{id}", handleRequest(getBuild))
	router.Post("/builds", handleRequest(postBuild))
	router.Get("/builds", handleRequest(getBuilds))

	return router, nil
}

// handleRequest is a wrapper for the actual route handler, simply to provide some
// debug output
func handleRequest(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {

		config.Log.Debug(`
Request:
--------------------------------------------------------------------------------
%+v

`, req)

		//
		fn(rw, req)

		config.Log.Debug(`
Response:
--------------------------------------------------------------------------------
%+v

`, rw)
	}
}

// parseBody
// func parseBody(req *http.Request, v interface{}) error {
//
// 	//
// 	b, err := ioutil.ReadAll(req.Body)
// 	if err != nil {
// 		return err
// 	}
//
// 	defer req.Body.Close()
//
// 	//
// 	if err := json.Unmarshal(b, v); err != nil {
// 		return err
// 	}
//
// 	return nil
// }

// writeBody
// func writeBody(v interface{}, rw http.ResponseWriter, status int) error {
// 	b, err := json.Marshal(v)
// 	if err != nil {
// 		return err
// 	}
//
// 	rw.Header().Set("Content-Type", "application/json")
// 	rw.WriteHeader(status)
// 	rw.Write(b)
//
// 	return nil
// }
<|endoftext|>