<commit_before>package mango

import (
	"bytes"
	"hash"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"encoding/gob"
	"net/http"
	"strings"
)

func hashCookie(data, secret string) (sum string) {
	var h hash.Hash = hmac.New(sha1.New, []byte(secret))
	h.Write([]byte(data))
	return string(h.Sum(nil))
}

func verifyCookie(data, secret, sum string) bool {
	return hashCookie(data, secret) == sum
}

func decodeGob(value string) (result map[string]interface{}) {
	buffer := bytes.NewBufferString(value)
	decoder := gob.NewDecoder(buffer)
	result = make(map[string]interface{})
	decoder.Decode(&result)
	return result
}

// Due to a bug in golang where when using
// base64.URLEncoding padding is still added
// (it shouldn't be), we have to strip and add
// it ourselves.
func pad64(value string) (result string) {
	padding := strings.Repeat("=", len(value)%4)
	return strings.Join([]string{value, padding}, "")
}

func decode64(value string) (result string) {
	buffer := bytes.NewBufferString(pad64(value))
	encoder := base64.NewDecoder(base64.URLEncoding, buffer)
	decoded, _ := ioutil.ReadAll(encoder)
	return string(decoded)
}

func decodeCookie(value, secret string) (cookie map[string]interface{}) {
	cookie = make(map[string]interface{})

	split := strings.Split(string(value), "--")

	if len(split) < 2 {
		return cookie
	}

	data := decode64(split[0])
	sum := decode64(split[1])
	if verifyCookie(data, secret, sum) {
		cookie = decodeGob(data)
	}

	return cookie
}

func encodeGob(value interface{}) (result string) {
	buffer := new(bytes.Buffer)
	encoder := gob.NewEncoder(buffer)
	encoder.Encode(value)
	return buffer.String()
}

// Due to a bug in golang where when using
// base64.URLEncoding padding is still added
// (it shouldn't be), we have to strip and add
// it ourselves.
func dePad64(value string) (result string) {
	return strings.TrimRight(value, "=")
}

func encode64(value string) (result string) {
	buffer := new(bytes.Buffer)
	encoder := base64.NewEncoder(base64.URLEncoding, buffer)
	encoder.Write([]byte(value))
	encoder.Close()
	return dePad64(buffer.String())
}

func encodeCookie(value map[string]interface{}, secret string) (cookie string) {
	data := encodeGob(value)

	return fmt.Sprintf("%s--%s", encode64(data), encode64(hashCookie(data, secret)))
}

func prepareSession(env Env, key, secret string) {
	for _, cookie := range env.Request().Cookies() {
		if cookie.Name == key {
			env["mango.session"] = decodeCookie(cookie.Value, secret)
			return
		}
	}

	// Didn't find a session to decode
	env["mango.session"] = make(map[string]interface{})
}

func commitSession(headers Headers, env Env, key, secret, domain string) {
	cookie := new(http.Cookie)
	cookie.Name = key
	cookie.Value = encodeCookie(env["mango.session"].(map[string]interface{}), secret)
	cookie.Domain = domain
	headers.Add("Set-Cookie", cookie.String())
}

func Sessions(secret, key, domain string) Middleware {
	return func(env Env, app App) (status Status, headers Headers, body Body) {
		prepareSession(env, key, secret)
		status, headers, body = app(env)
		commitSession(headers, env, key, secret, domain)
		return status, headers, body
	}
}
<commit_msg>Base64 URLEncoding could generate -, so it's not safe to use -- as a sign seperator http://tip.golang.org/src/pkg/encoding/base64/base64.go?s=783:870#L12<commit_after>package mango

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/gob"
	"fmt"
	"hash"
	"io/ioutil"
	"net/http"
	"strings"
)

func hashCookie(data, secret string) (sum string) {
	var h hash.Hash = hmac.New(sha1.New, []byte(secret))
	h.Write([]byte(data))
	return string(h.Sum(nil))
}

func verifyCookie(data, secret, sum string) bool {
	return hashCookie(data, secret) == sum
}

func decodeGob(value string) (result map[string]interface{}) {
	buffer := bytes.NewBufferString(value)
	decoder := gob.NewDecoder(buffer)
	result = make(map[string]interface{})
	decoder.Decode(&result)
	return result
}

// Due to a bug in golang where when using
// base64.URLEncoding padding is still added
// (it shouldn't be), we have to strip and add
// it ourselves.
func pad64(value string) (result string) {
	padding := strings.Repeat("=", len(value)%4)
	return strings.Join([]string{value, padding}, "")
}

func decode64(value string) (result string) {
	buffer := bytes.NewBufferString(pad64(value))
	encoder := base64.NewDecoder(base64.URLEncoding, buffer)
	decoded, _ := ioutil.ReadAll(encoder)
	return string(decoded)
}

func decodeCookie(value, secret string) (cookie map[string]interface{}) {
	cookie = make(map[string]interface{})

	split := strings.Split(string(value), "/")

	if len(split) < 2 {
		return cookie
	}

	data := decode64(split[0])
	sum := decode64(split[1])
	if verifyCookie(data, secret, sum) {
		cookie = decodeGob(data)
	}

	return cookie
}

func encodeGob(value interface{}) (result string) {
	buffer := new(bytes.Buffer)
	encoder := gob.NewEncoder(buffer)
	encoder.Encode(value)
	return buffer.String()
}

// Due to a bug in golang where when using
// base64.URLEncoding padding is still added
// (it shouldn't be), we have to strip and add
// it ourselves.
func dePad64(value string) (result string) {
	return strings.TrimRight(value, "=")
}

func encode64(value string) (result string) {
	buffer := new(bytes.Buffer)
	encoder := base64.NewEncoder(base64.URLEncoding, buffer)
	encoder.Write([]byte(value))
	encoder.Close()
	return dePad64(buffer.String())
}

func encodeCookie(value map[string]interface{}, secret string) (cookie string) {
	data := encodeGob(value)

	return fmt.Sprintf("%s/%s", encode64(data), encode64(hashCookie(data, secret)))
}

func prepareSession(env Env, key, secret string) {
	for _, cookie := range env.Request().Cookies() {
		if cookie.Name == key {
			env["mango.session"] = decodeCookie(cookie.Value, secret)
			return
		}
	}

	// Didn't find a session to decode
	env["mango.session"] = make(map[string]interface{})
}

func commitSession(headers Headers, env Env, key, secret, domain string) {
	cookie := new(http.Cookie)
	cookie.Name = key
	cookie.Value = encodeCookie(env["mango.session"].(map[string]interface{}), secret)
	cookie.Domain = domain
	headers.Add("Set-Cookie", cookie.String())
}

func Sessions(secret, key, domain string) Middleware {
	return func(env Env, app App) (status Status, headers Headers, body Body) {
		prepareSession(env, key, secret)
		status, headers, body = app(env)
		commitSession(headers, env, key, secret, domain)
		return status, headers, body
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	flag "github.com/jessevdk/go-flags"
)

const version = "0.11.0"

//Config is concrete and stored in configuration file
type Config struct {
	ImageDir   string `long:"dir" description:"Target Directory" default:"img" ini-name:"downdir"`
	QDepth     int    `short:"q" long:"queue" description:"Length of the queue buffer" default:"50" ini-name:"queue_depth"`
	Key        string `short:"k" long:"key" description:"Derpibooru API key" ini-name:"key"`
	LogFilters Bool   `long:"logfilter" optional:" " optional-value:"true" description:"Enable logging of filtered images" ini-name:"logfilter"`
}

//FlagOpts are runtime boolean flags
type FlagOpts struct {
	UnsafeHTTPS bool `long:"unsafe-https" description:"Disable HTTPS security verification"`
}

//FiltOpts are filtration parameters
type FiltOpts struct {
	Score  int  `long:"score" description:"Filter option, minimal score of image for it to be downloaded"`
	Faves  int  `long:"faves" description:"Filter option, minimal amount of people who favored image for it to be downloaded"`
	ScoreF bool `no-flag:" "`
	FavesF bool `no-flag:" "`
}

//TagOpts are options relevant to searching by tags
type TagOpts struct {
	Tag       string `short:"t" long:"tag" description:"Tag to download"`
	StartPage int    `short:"p" long:"startpage" description:"Starting page for search" default:"1"`
	StopPage  int    `short:"n" long:"stoppage" description:"Stopping page for search, default - parse all search pages"`
}

//Options provide program-wide options. At maximum, we got one persistent global and one short-living copy for writing in config file
type Options struct {
	*Config
	*FlagOpts
	*FiltOpts
	*TagOpts
	Args struct {
		IDs []int `description:"Image IDs to download" optional:"yes"`
	} `positional-args:"yes"`
}

func getOptions() (opts *Options, args []string) {
	opts = new(Options)
	args, inisets := opts.Setup()
	opts.Config.checkedWriteIni(inisets)
	return
}

//checkedWriteIni writes configuration into file without overwriting old one if unchanged
//As i totally forgot how it does so if configuration file is empty:
//nil and zero values and empty strings get overwritten by defaults when reading command line
//as old values are now different, defaults got written into the file
func (sets *Config) checkedWriteIni(oldsets *Config) {
	if sets.isEqual(oldsets) { //If nothing to write, no double-writing files
		return
	}

	inifile, err := os.OpenFile("config.ini", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)

	if err != nil {
		lFatal("Could  not create configuration file")
	}

	defer func() {
		err = inifile.Close()
		if err != nil {
			lFatal("Could  not close configuration file")
		}
	}()

	err = sets.prettyWriteIni(inifile)

	if err != nil {
		lFatal("Could  not write in configuration file")
	}
}

//prettyWriteIni Uses tabwriter to make pretty ini file with
func (sets *Config) prettyWriteIni(inifile io.Writer) error {
	tb := tabwriter.NewWriter(inifile, 10, 8, 0, ' ', 0) //Tabs! Elastic! Pretty!

	fmt.Fprintf(tb, "key \t= %s\n", sets.Key)
	fmt.Fprintf(tb, "queue_depth \t= %s\n", strconv.Itoa(sets.QDepth))
	fmt.Fprintf(tb, "downdir \t= %s\n", sets.ImageDir)
	fmt.Fprintf(tb, "logfilter \t= %t\n", sets.LogFilters)

	return tb.Flush() //Returns and passes error upstairs
}

//isEqual compares only options I want to preserve across launches.
func (sets *Config) isEqual(b *Config) bool {
	if b == nil {
		return false
	}
	if sets.ImageDir == b.ImageDir &&
		sets.QDepth == b.QDepth &&
		sets.Key == b.Key &&
		sets.LogFilters == b.LogFilters {
		return true
	}
	return false
}

//Setup reads static config from file and runtime options from commandline
//It also preserves static config for later comparsion with runtime to prevent
//rewriting it when no changes are made
func (opts *Options) Setup() ([]string, *Config) {
	err := flag.IniParse("config.ini", opts)
	if err != nil {
		switch err.(type) {
		default:
			lFatal(err)
		case *os.PathError:
			lWarn("config.ini not found, using defaults")
		}
	}
	inisets := *opts.Config //copy value instead of reference - or we will get no results later

	args, err := flag.Parse(opts)
	checkFlagError(err) //Here we scream if something goes wrong and provide help if something goes meh.

	for _, arg := range os.Args {
		if strings.Contains(arg, "--score") {
			opts.ScoreF = true
		}
		if strings.Contains(arg, "--faves") {
			opts.FavesF = true
		}
	}
	return args, &inisets
}

func checkFlagError(err error) {
	if err == nil {
		return
	}

	flagError := err.(*flag.Error)

	switch flagError.Type {
	case flag.ErrHelp:
		os.Exit(0) //Why fall through when asked for help? Just exit with suggestion
	case flag.ErrUnknownFlag:
		fmt.Println("Use --help to view all available options")
		os.Exit(0)
	default:
		lFatal("Can't parse flags: ", err)
	}
}
<commit_msg>Inline functions to ease future bugfix<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	flag "github.com/jessevdk/go-flags"
)

const version = "0.11.0"

//Config is concrete and stored in configuration file
type Config struct {
	ImageDir   string `long:"dir" description:"Target Directory" default:"img" ini-name:"downdir"`
	QDepth     int    `short:"q" long:"queue" description:"Length of the queue buffer" default:"50" ini-name:"queue_depth"`
	Key        string `short:"k" long:"key" description:"Derpibooru API key" ini-name:"key"`
	LogFilters Bool   `long:"logfilter" optional:" " optional-value:"true" description:"Enable logging of filtered images" ini-name:"logfilter"`
}

//FlagOpts are runtime boolean flags
type FlagOpts struct {
	UnsafeHTTPS bool `long:"unsafe-https" description:"Disable HTTPS security verification"`
}

//FiltOpts are filtration parameters
type FiltOpts struct {
	Score  int  `long:"score" description:"Filter option, minimal score of image for it to be downloaded"`
	Faves  int  `long:"faves" description:"Filter option, minimal amount of people who favored image for it to be downloaded"`
	ScoreF bool `no-flag:" "`
	FavesF bool `no-flag:" "`
}

//TagOpts are options relevant to searching by tags
type TagOpts struct {
	Tag       string `short:"t" long:"tag" description:"Tag to download"`
	StartPage int    `short:"p" long:"startpage" description:"Starting page for search" default:"1"`
	StopPage  int    `short:"n" long:"stoppage" description:"Stopping page for search, default - parse all search pages"`
}

//Options provide program-wide options. At maximum, we got one persistent global and one short-living copy for writing in config file
type Options struct {
	*Config
	*FlagOpts
	*FiltOpts
	*TagOpts
	Args struct {
		IDs []int `description:"Image IDs to download" optional:"yes"`
	} `positional-args:"yes"`
}

func getOptions() (opts *Options, args []string) {
	opts = new(Options)
	err := flag.IniParse("config.ini", opts)
	if err != nil {
		switch err.(type) {
		default:
			lFatal(err)
		case *os.PathError:
			lWarn("config.ini not found, using defaults")
		}
	}
	inisets := *opts.Config //copy value instead of reference - or we will get no results later

	args, err = flag.Parse(opts)
	checkFlagError(err) //Here we scream if something goes wrong and provide help if something goes meh.

	for _, arg := range os.Args {
		if strings.Contains(arg, "--score") {
			opts.ScoreF = true
		}
		if strings.Contains(arg, "--faves") {
			opts.FavesF = true
		}
	}
	if opts.Config.isEqual(&inisets) { //If nothing to write, no double-writing files
		return
	}

	inifile, err := os.OpenFile("config.ini", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)

	if err != nil {
		lFatal("Could  not create configuration file")
	}

	defer func() {
		err = inifile.Close()
		if err != nil {
			lFatal("Could  not close configuration file")
		}
	}()

	err = opts.Config.prettyWriteIni(inifile)

	if err != nil {
		lFatal("Could  not write in configuration file")
	}
	return
}

//prettyWriteIni Uses tabwriter to make pretty ini file with
func (sets *Config) prettyWriteIni(inifile io.Writer) error {
	tb := tabwriter.NewWriter(inifile, 10, 8, 0, ' ', 0) //Tabs! Elastic! Pretty!

	fmt.Fprintf(tb, "key \t= %s\n", sets.Key)
	fmt.Fprintf(tb, "queue_depth \t= %s\n", strconv.Itoa(sets.QDepth))
	fmt.Fprintf(tb, "downdir \t= %s\n", sets.ImageDir)
	fmt.Fprintf(tb, "logfilter \t= %t\n", sets.LogFilters)

	return tb.Flush() //Returns and passes error upstairs
}

//isEqual compares only options I want to preserve across launches.
func (sets *Config) isEqual(b *Config) bool {
	if b == nil {
		return false
	}
	if sets.ImageDir == b.ImageDir &&
		sets.QDepth == b.QDepth &&
		sets.Key == b.Key &&
		sets.LogFilters == b.LogFilters {
		return true
	}
	return false
}

func checkFlagError(err error) {
	if err == nil {
		return
	}

	flagError := err.(*flag.Error)

	switch flagError.Type {
	case flag.ErrHelp:
		os.Exit(0) //Why fall through when asked for help? Just exit with suggestion
	case flag.ErrUnknownFlag:
		fmt.Println("Use --help to view all available options")
		os.Exit(0)
	default:
		lFatal("Can't parse flags: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"syscall"

	"github.com/coreos/go-etcd/etcd"
	"github.com/lytics/metafora"
)

type shellHandler struct {
	etcdc *etcd.Client
	id    string
	m     sync.Mutex
	p     *os.Process
	ps    *os.ProcessState
	stop  bool
}

// Run retrieves task information from etcd and executes it.
func (h *shellHandler) Run(taskID string) (done bool) {
	h.id = taskID

	const sort, recurs = false, false
	resp, err := h.etcdc.Get("/koalemos-tasks/"+taskID, sort, recurs)
	if err != nil {
		h.log("Fatal error: Failed retrieving task from etcd: %v", err)
		return true
	}

	task := struct{ Args []string }{}
	if err := json.Unmarshal([]byte(resp.Node.Value), &task); err != nil {
		h.log("Failed to unmarshal command body: %v", err)
		return true
	}
	if len(task.Args) == 0 {
		h.log("No Args in task: %s", resp.Node.Value)
		return true
	}

	cmd := exec.Command(task.Args[0], task.Args[1:]...)

	// Set stdout and stderr to temporary files
	stdout, stderr, err := outFiles(taskID)
	if err != nil {
		h.log("Could not create log files: %v", err)
		return false
	}
	defer stdout.Close()
	defer stderr.Close()

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// Entering critical section where we have to lock handler fields to avoid
	// race conditions with Stop() getting called.
	h.m.Lock()
	if h.stop {
		h.log("Task stopped before it even started.")
		h.m.Unlock()
		return false
	}

	h.log("Running task: %s", strings.Join(task.Args, " "))
	if err := cmd.Start(); err != nil {
		h.m.Unlock()
		h.log("Error starting task: %v", err)
		return true
	}
	h.p = cmd.Process
	h.ps = cmd.ProcessState

	// Leaving critical section. Now if Stop() is called, cmd.Wait() will return.
	h.m.Unlock()

	h.log("running")

	if err := cmd.Wait(); err != nil {
		if err.(*exec.ExitError).Sys().(syscall.WaitStatus).Signal() == os.Interrupt {
			h.log("Stopping")
		} else {
			h.log("Exited with error: %v", err)
			done = true // don't retry commands that error'd
		}
	}

	// Only delete task if command is done
	if done {
		//FIXME Use CompareAndDelete
		if _, err := h.etcdc.Delete("/koalemos-tasks/"+taskID, recurs); err != nil {
			h.log("Error deleting task body: %v", err)
		}
	}
	h.log("done? %t", done)
	return done
}

// Stop sends the Interrupt signal to the running process.
func (h *shellHandler) Stop() {
	h.m.Lock()
	defer h.m.Unlock()

	h.log("Setting as stopped")
	h.stop = true

	if h.p != nil && h.ps != nil && !h.ps.Exited() {
		h.log("Process has not started.")
		return
	}

	if err := h.p.Signal(os.Interrupt); err != nil {
		h.log("Error stopping process %d: %v", h.p.Pid, err)
	}
}

func (h *shellHandler) log(msg string, v ...interface{}) {
	log.Printf("[%s] %s", h.id, fmt.Sprintf(msg, v...))
}

func outFiles(name string) (io.WriteCloser, io.WriteCloser, error) {
	stdout, err := os.Create(filepath.Join(os.TempDir(), name+"-stdout.log"))
	if err != nil {
		return nil, nil, err
	}
	stderr, err := os.Create(filepath.Join(os.TempDir(), name+"-stderr.log"))
	return stdout, stderr, err
}

func makeHandlerFunc(c *etcd.Client) metafora.HandlerFunc {
	return func() metafora.Handler {
		return &shellHandler{etcdc: c}
	}
}
<commit_msg>Retry koalemosd tasks on etcd failures<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"syscall"

	"github.com/coreos/go-etcd/etcd"
	"github.com/lytics/metafora"
)

type shellHandler struct {
	etcdc *etcd.Client
	id    string
	m     sync.Mutex
	p     *os.Process
	ps    *os.ProcessState
	stop  bool
}

// Run retrieves task information from etcd and executes it.
func (h *shellHandler) Run(taskID string) (done bool) {
	h.id = taskID

	const sort, recurs = false, false
	resp, err := h.etcdc.Get("/koalemos-tasks/"+taskID, sort, recurs)
	if err != nil {
		h.log("Fatal error: Failed retrieving task from etcd: %v", err)
		return false
	}

	task := struct{ Args []string }{}
	if err := json.Unmarshal([]byte(resp.Node.Value), &task); err != nil {
		h.log("Failed to unmarshal command body: %v", err)
		return true
	}
	if len(task.Args) == 0 {
		h.log("No Args in task: %s", resp.Node.Value)
		return true
	}

	cmd := exec.Command(task.Args[0], task.Args[1:]...)

	// Set stdout and stderr to temporary files
	stdout, stderr, err := outFiles(taskID)
	if err != nil {
		h.log("Could not create log files: %v", err)
		return false
	}
	defer stdout.Close()
	defer stderr.Close()

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// Entering critical section where we have to lock handler fields to avoid
	// race conditions with Stop() getting called.
	h.m.Lock()
	if h.stop {
		h.log("Task stopped before it even started.")
		h.m.Unlock()
		return false
	}

	h.log("Running task: %s", strings.Join(task.Args, " "))
	if err := cmd.Start(); err != nil {
		h.m.Unlock()
		h.log("Error starting task: %v", err)
		return true
	}
	h.p = cmd.Process
	h.ps = cmd.ProcessState

	// Leaving critical section. Now if Stop() is called, cmd.Wait() will return.
	h.m.Unlock()

	h.log("running")

	if err := cmd.Wait(); err != nil {
		if err.(*exec.ExitError).Sys().(syscall.WaitStatus).Signal() == os.Interrupt {
			h.log("Stopping")
		} else {
			h.log("Exited with error: %v", err)
			done = true // don't retry commands that error'd
		}
	}

	// Only delete task if command is done
	if done {
		//FIXME Use CompareAndDelete
		if _, err := h.etcdc.Delete("/koalemos-tasks/"+taskID, recurs); err != nil {
			h.log("Error deleting task body: %v", err)
		}
	}
	h.log("done? %t", done)
	return done
}

// Stop sends the Interrupt signal to the running process.
func (h *shellHandler) Stop() {
	h.m.Lock()
	defer h.m.Unlock()

	h.log("Setting as stopped")
	h.stop = true

	if h.p != nil && h.ps != nil && !h.ps.Exited() {
		h.log("Process has not started.")
		return
	}

	if err := h.p.Signal(os.Interrupt); err != nil {
		h.log("Error stopping process %d: %v", h.p.Pid, err)
	}
}

func (h *shellHandler) log(msg string, v ...interface{}) {
	log.Printf("[%s] %s", h.id, fmt.Sprintf(msg, v...))
}

func outFiles(name string) (io.WriteCloser, io.WriteCloser, error) {
	stdout, err := os.Create(filepath.Join(os.TempDir(), name+"-stdout.log"))
	if err != nil {
		return nil, nil, err
	}
	stderr, err := os.Create(filepath.Join(os.TempDir(), name+"-stderr.log"))
	return stdout, stderr, err
}

func makeHandlerFunc(c *etcd.Client) metafora.HandlerFunc {
	return func() metafora.Handler {
		return &shellHandler{etcdc: c}
	}
}
<|endoftext|>


<commit_before>// Utilities for the Flow Parser
//
// This file contains some utilities that help building the flow parser.
// Most of them are themself simple parsers.

package parser

import (
	"strings"

	"github.com/flowdev/gparselib"
)

// NameIdentParser parses a name identifier.
// Regexp: [a-z][a-zA-Z0-9]*
// Semantic result: The parsed text.
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type NameIdentParser gparselib.ParseRegexper

// NewNameIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewNameIdentParser() (*NameIdentParser, error) {
	p, err := gparselib.NewParseRegexper(`^[a-z][a-zA-Z0-9]*`)
	return (*NameIdentParser)(p), err
}

// ParseNameIdent is the input port of the NameIdentParser operation.
func (p *NameIdentParser) ParseNameIdent(
	pd *gparselib.ParseData, ctx interface{},
) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.ParseRegexper)(p)).ParseRegexp(pd, ctx, TextSemantic)
}

// PackageIdentParser parses a package identifier.
// Regexp: [a-z][a-z0-9]*\.
// Semantic result: The parsed text (without the dot).
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type PackageIdentParser gparselib.ParseRegexper

// NewPackageIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewPackageIdentParser() (*PackageIdentParser, error) {
	p, err := gparselib.NewParseRegexper(`^[a-z][a-z0-9]*\.`)
	return (*PackageIdentParser)(p), err
}

// ParsePackageIdent is the input port of the PackageIdentParser operation.
func (p *PackageIdentParser) ParsePackageIdent(
	pd *gparselib.ParseData, ctx interface{},
) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.ParseRegexper)(p)).ParseRegexp(pd, ctx,
		func(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
			pd.Result.Value = pd.Result.Text[:len(pd.Result.Text)-1]
			return pd, ctx
		})
}

// LocalTypeIdentParser parses a local (without package) type identifier.
// Regexp: [A-Za-z][a-zA-Z0-9]*
// Semantic result: The parsed text.
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type LocalTypeIdentParser gparselib.ParseRegexper

// NewLocalTypeIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewLocalTypeIdentParser() (*LocalTypeIdentParser, error) {
	p, err := gparselib.NewParseRegexper(`^[A-Za-z][a-zA-Z0-9]*`)
	return (*LocalTypeIdentParser)(p), err
}

// ParseLocalTypeIdent is the input port of the LocalTypeIdentParser operation.
func (p *LocalTypeIdentParser) ParseLocalTypeIdent(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.ParseRegexper)(p)).ParseRegexp(pd, ctx, TextSemantic)
}

// ParseOptSpc parses optional space but no newline.
// Semantic result: The parsed text.
func ParseOptSpc(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSpc := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseSpace(pd2, ctx2, TextSemantic, false)
	}
	return gparselib.ParseOptional(pd, ctx, pSpc, TextSemantic)
}

// ParseASpc parses space but no newline.
// Semantic result: The parsed text.
func ParseASpc(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	return gparselib.ParseSpace(pd, ctx, TextSemantic, false)
}

// SpaceCommentSemValue is the semantic representation of space and comments.
// It specifically informs whether a newline has been parsed.
type SpaceCommentSemValue struct {
	Text    string
	NewLine bool
}

const newLineRune = 10

// spaceCommentSemantic returns the successfully parsed text as semantic value
// plus a signal whether a newline has been parsed.
func spaceCommentSemantic(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	semVal := SpaceCommentSemValue{Text: pd.Result.Text}
	semVal.NewLine = strings.ContainsRune(semVal.Text, newLineRune)
	pd.Result.Value = semVal
	return pd, ctx
}

// ParseSpaceComment parses any amount of space (including newline) and line
// (`//` ... <NL>) and block (`/*` ... `*/`) comments.
// Semantic result: The parsed text plus a signal whether a newline was
// parsed.
func ParseSpaceComment(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSpc := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseSpace(pd2, ctx2, TextSemantic, true)
	}
	pLnCmnt := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		var err error
		pd2, ctx2, err = gparselib.ParseLineComment(pd2, ctx2, TextSemantic, `//`)
		if err != nil {
			panic(err) // can only be a programming error!
		}
		return pd2, ctx2
	}
	pBlkCmnt := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		var err error
		pd2, ctx2, err = gparselib.ParseBlockComment(pd2, ctx2, TextSemantic, `/*`, `*/`)
		if err != nil {
			panic(err) // can only be a programming error!
		}
		return pd2, ctx2
	}
	pAny := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseAny(
			pd2, ctx2,
			[]gparselib.SubparserOp{pSpc, pLnCmnt, pBlkCmnt},
			TextSemantic,
		)
	}
	return gparselib.ParseMulti0(pd, ctx, pAny, spaceCommentSemantic)
}

// Error messages for semantic errors.
const (
	errMsgNoEnd = "A statement must be ended by a semicolon (';') or a new line"
)

// ParseStatementEnd parses optional space and comments as defined by
// `ParseSpaceComment` followed by a semicolon (`;`) and more optional space
// and comments.
// The semicolon can be omited if the space or comments contain a new line.
// Semantic result: The parsed text.
func ParseStatementEnd(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSemicolon := func(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseLiteral(pd, ctx, TextSemantic, `;`)
	}
	pOptSemi := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseOptional(pd, ctx, pSemicolon, nil)
	}
	return gparselib.ParseAll(pd, ctx,
		[]gparselib.SubparserOp{ParseSpaceComment, pOptSemi, ParseSpaceComment},
		func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
			spcCmnt1 := pd2.SubResults[0].Value.(SpaceCommentSemValue)
			semi := pd2.SubResults[1].Value
			spcCmnt2 := pd2.SubResults[2].Value.(SpaceCommentSemValue)
			if spcCmnt1.NewLine || semi != nil || spcCmnt2.NewLine {
				pd2.Result.Value = pd2.Result.Text
			} else {
				pd2.AddError(pd2.Result.Pos, errMsgNoEnd, nil)
				pd2.Result.Value = nil
			}
			return pd2, ctx2
		},
	)
}

// TextSemantic returns the successfully parsed text as semantic value.
func TextSemantic(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pd.Result.Value = pd.Result.Text
	return pd, ctx
}
<commit_msg>Adjust to gparselib changes<commit_after>// Utilities for the Flow Parser
//
// This file contains some utilities that help building the flow parser.
// Most of them are themself simple parsers.

package parser

import (
	"strings"

	"github.com/flowdev/gparselib"
)

// NameIdentParser parses a name identifier.
// Regexp: [a-z][a-zA-Z0-9]*
// Semantic result: The parsed text.
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type NameIdentParser gparselib.RegexpParser

// NewNameIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewNameIdentParser() (*NameIdentParser, error) {
	p, err := gparselib.NewRegexpParser(`^[a-z][a-zA-Z0-9]*`)
	return (*NameIdentParser)(p), err
}

// ParseNameIdent is the input port of the NameIdentParser operation.
func (p *NameIdentParser) ParseNameIdent(
	pd *gparselib.ParseData, ctx interface{},
) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.RegexpParser)(p)).ParseRegexp(pd, ctx, TextSemantic)
}

// PackageIdentParser parses a package identifier.
// Regexp: [a-z][a-z0-9]*\.
// Semantic result: The parsed text (without the dot).
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type PackageIdentParser gparselib.RegexpParser

// NewPackageIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewPackageIdentParser() (*PackageIdentParser, error) {
	p, err := gparselib.NewRegexpParser(`^[a-z][a-z0-9]*\.`)
	return (*PackageIdentParser)(p), err
}

// ParsePackageIdent is the input port of the PackageIdentParser operation.
func (p *PackageIdentParser) ParsePackageIdent(
	pd *gparselib.ParseData, ctx interface{},
) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.RegexpParser)(p)).ParseRegexp(pd, ctx,
		func(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
			pd.Result.Value = pd.Result.Text[:len(pd.Result.Text)-1]
			return pd, ctx
		})
}

// LocalTypeIdentParser parses a local (without package) type identifier.
// Regexp: [A-Za-z][a-zA-Z0-9]*
// Semantic result: The parsed text.
//
// flow:
//     in (ParseData)-> [gparselib.ParseRegexp[semantics=TextSemantic]] -> out
//
// Details:
//  - [ParseData](https://github.com/flowdev/gparselib/blob/master/base.go#L74-L79)
//  - [ParseRegexp](https://github.com/flowdev/gparselib/blob/master/simpleParser.go#L163)
//  - [TextSemantic](./parseUtils.md#textsemantic)
type LocalTypeIdentParser gparselib.RegexpParser

// NewLocalTypeIdentParser creates a new parser for the given regular expression.
// If the regular expression is invalid an error is returned.
func NewLocalTypeIdentParser() (*LocalTypeIdentParser, error) {
	p, err := gparselib.NewRegexpParser(`^[A-Za-z][a-zA-Z0-9]*`)
	return (*LocalTypeIdentParser)(p), err
}

// ParseLocalTypeIdent is the input port of the LocalTypeIdentParser operation.
func (p *LocalTypeIdentParser) ParseLocalTypeIdent(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	return ((*gparselib.RegexpParser)(p)).ParseRegexp(pd, ctx, TextSemantic)
}

// ParseOptSpc parses optional space but no newline.
// Semantic result: The parsed text.
func ParseOptSpc(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSpc := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseSpace(pd2, ctx2, TextSemantic, false)
	}
	return gparselib.ParseOptional(pd, ctx, pSpc, TextSemantic)
}

// ParseASpc parses space but no newline.
// Semantic result: The parsed text.
func ParseASpc(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	return gparselib.ParseSpace(pd, ctx, TextSemantic, false)
}

// SpaceCommentSemValue is the semantic representation of space and comments.
// It specifically informs whether a newline has been parsed.
type SpaceCommentSemValue struct {
	Text    string
	NewLine bool
}

const newLineRune = 10

// spaceCommentSemantic returns the successfully parsed text as semantic value
// plus a signal whether a newline has been parsed.
func spaceCommentSemantic(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	semVal := SpaceCommentSemValue{Text: pd.Result.Text}
	semVal.NewLine = strings.ContainsRune(semVal.Text, newLineRune)
	pd.Result.Value = semVal
	return pd, ctx
}

// ParseSpaceComment parses any amount of space (including newline) and line
// (`//` ... <NL>) and block (`/*` ... `*/`) comments.
// Semantic result: The parsed text plus a signal whether a newline was
// parsed.
func ParseSpaceComment(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSpc := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseSpace(pd2, ctx2, TextSemantic, true)
	}
	pLnCmnt := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		var err error
		pd2, ctx2, err = gparselib.ParseLineComment(pd2, ctx2, TextSemantic, `//`)
		if err != nil {
			panic(err) // can only be a programming error!
		}
		return pd2, ctx2
	}
	pBlkCmnt := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		var err error
		pd2, ctx2, err = gparselib.ParseBlockComment(pd2, ctx2, TextSemantic, `/*`, `*/`)
		if err != nil {
			panic(err) // can only be a programming error!
		}
		return pd2, ctx2
	}
	pAny := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseAny(
			pd2, ctx2,
			[]gparselib.SubparserOp{pSpc, pLnCmnt, pBlkCmnt},
			TextSemantic,
		)
	}
	return gparselib.ParseMulti0(pd, ctx, pAny, spaceCommentSemantic)
}

// Error messages for semantic errors.
const (
	errMsgNoEnd = "A statement must be ended by a semicolon (';') or a new line"
)

// ParseStatementEnd parses optional space and comments as defined by
// `ParseSpaceComment` followed by a semicolon (`;`) and more optional space
// and comments.
// The semicolon can be omited if the space or comments contain a new line.
// Semantic result: The parsed text.
func ParseStatementEnd(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pSemicolon := func(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseLiteral(pd, ctx, TextSemantic, `;`)
	}
	pOptSemi := func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
		return gparselib.ParseOptional(pd, ctx, pSemicolon, nil)
	}
	return gparselib.ParseAll(pd, ctx,
		[]gparselib.SubparserOp{ParseSpaceComment, pOptSemi, ParseSpaceComment},
		func(pd2 *gparselib.ParseData, ctx2 interface{}) (*gparselib.ParseData, interface{}) {
			spcCmnt1 := pd2.SubResults[0].Value.(SpaceCommentSemValue)
			semi := pd2.SubResults[1].Value
			spcCmnt2 := pd2.SubResults[2].Value.(SpaceCommentSemValue)
			if spcCmnt1.NewLine || semi != nil || spcCmnt2.NewLine {
				pd2.Result.Value = pd2.Result.Text
			} else {
				pd2.AddError(pd2.Result.Pos, errMsgNoEnd, nil)
				pd2.Result.Value = nil
			}
			return pd2, ctx2
		},
	)
}

// TextSemantic returns the successfully parsed text as semantic value.
func TextSemantic(pd *gparselib.ParseData, ctx interface{}) (*gparselib.ParseData, interface{}) {
	pd.Result.Value = pd.Result.Text
	return pd, ctx
}
<|endoftext|>


<commit_before>package chaosmonkey

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

const (
	StrategyShutdownInstance       = "ShutdownInstance"
	StrategyBlockAllNetworkTraffic = "BlockAllNetworkTraffic"
	StrategyDetachVolumes          = "DetachVolumes"
	StrategyBurnCPU                = "BurnCpu"
	StrategyBurnIO                 = "BurnIo"
	StrategyKillProcesses          = "KillProcesses"
	StrategyNullRoute              = "NullRoute"
	StrategyFailEC2                = "FailEc2"
	StrategyFailDNS                = "FailDns"
	StrategyFailDynamoDB           = "FailDynamoDb"
	StrategyFailS3                 = "FailS3"
	StrategyFillDisk               = "FillDisk"
	StrategyNetworkCorruption      = "NetworkCorruption"
	StrategyNetworkLatency         = "NetworkLatency"
	StrategyNetworkLoss            = "NetworkLoss"
)

type ChaosEvent struct {
	Strategy   string
	ASGName    string
	InstanceID string
	Region     string
	Time       time.Time
}

type chaosRequest struct {
	EventType string `json:"eventType"`
	GroupType string `json:"groupType"`
	GroupName string `json:"groupName"`
	ChaosType string `json:"chaosType,omitempty"`
}

type chaosResponse struct {
	*chaosRequest

	MonkeyType string `json:"monkeyType"`
	EventID    string `json:"eventId"`
	EventTime  int64  `json:"eventTime"`
	Region     string `json:"region"`
}

type Config struct {
	Endpoint string
	Username string
	Password string

	HTTPClient *http.Client
}

type Client struct {
	config *Config
}

func NewClient(c *Config) (*Client, error) {
	if c.Endpoint == "" {
		return nil, fmt.Errorf("Endpoint must not be empty")
	}
	if c.HTTPClient == nil {
		c.HTTPClient = http.DefaultClient
	}
	return &Client{config: c}, nil
}

func (c *Client) TriggerEvent(asgName, strategy string) (*ChaosEvent, error) {
	url := c.config.Endpoint + "/simianarmy/api/v1/chaos"

	body, err := json.Marshal(chaosRequest{
		EventType: "CHAOS_TERMINATION",
		GroupType: "ASG",
		GroupName: asgName,
		ChaosType: strategy,
	})
	if err != nil {
		return nil, err
	}

	var resp chaosResponse
	if err := c.sendRequest("POST", url, bytes.NewReader(body), &resp); err != nil {
		return nil, err
	}

	return makeChaosEvent(&resp), nil
}

func (c *Client) GetEvents() ([]ChaosEvent, error) {
	url := c.config.Endpoint + "/simianarmy/api/v1/chaos"

	var resp []chaosResponse
	if err := c.sendRequest("GET", url, nil, &resp); err != nil {
		return nil, err
	}

	var events []ChaosEvent
	for _, r := range resp {
		events = append(events, *makeChaosEvent(&r))
	}

	return events, nil
}

func (c *Client) sendRequest(method, url string, body io.Reader, out interface{}) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}

	if c.config.Username != "" && c.config.Password != "" {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}

	resp, err := c.config.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return decodeError(resp)
	}

	return json.NewDecoder(resp.Body).Decode(out)
}

func decodeError(resp *http.Response) error {
	var m struct {
		Message string `json:"message"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&m); err == nil && m.Message != "" {
		return fmt.Errorf("%s", m.Message)
	}
	return fmt.Errorf("%s", resp.Status)
}

func makeChaosEvent(in *chaosResponse) *ChaosEvent {
	return &ChaosEvent{
		Strategy:   in.ChaosType,
		ASGName:    in.GroupName,
		InstanceID: in.EventID,
		Region:     in.Region,
		Time:       time.Unix(in.EventTime/1000, 0),
	}
}
<commit_msg>Store event time as UTC<commit_after>package chaosmonkey

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

const (
	StrategyShutdownInstance       = "ShutdownInstance"
	StrategyBlockAllNetworkTraffic = "BlockAllNetworkTraffic"
	StrategyDetachVolumes          = "DetachVolumes"
	StrategyBurnCPU                = "BurnCpu"
	StrategyBurnIO                 = "BurnIo"
	StrategyKillProcesses          = "KillProcesses"
	StrategyNullRoute              = "NullRoute"
	StrategyFailEC2                = "FailEc2"
	StrategyFailDNS                = "FailDns"
	StrategyFailDynamoDB           = "FailDynamoDb"
	StrategyFailS3                 = "FailS3"
	StrategyFillDisk               = "FillDisk"
	StrategyNetworkCorruption      = "NetworkCorruption"
	StrategyNetworkLatency         = "NetworkLatency"
	StrategyNetworkLoss            = "NetworkLoss"
)

type ChaosEvent struct {
	Strategy   string
	ASGName    string
	InstanceID string
	Region     string
	Time       time.Time
}

type chaosRequest struct {
	EventType string `json:"eventType"`
	GroupType string `json:"groupType"`
	GroupName string `json:"groupName"`
	ChaosType string `json:"chaosType,omitempty"`
}

type chaosResponse struct {
	*chaosRequest

	MonkeyType string `json:"monkeyType"`
	EventID    string `json:"eventId"`
	EventTime  int64  `json:"eventTime"`
	Region     string `json:"region"`
}

type Config struct {
	Endpoint string
	Username string
	Password string

	HTTPClient *http.Client
}

type Client struct {
	config *Config
}

func NewClient(c *Config) (*Client, error) {
	if c.Endpoint == "" {
		return nil, fmt.Errorf("Endpoint must not be empty")
	}
	if c.HTTPClient == nil {
		c.HTTPClient = http.DefaultClient
	}
	return &Client{config: c}, nil
}

func (c *Client) TriggerEvent(asgName, strategy string) (*ChaosEvent, error) {
	url := c.config.Endpoint + "/simianarmy/api/v1/chaos"

	body, err := json.Marshal(chaosRequest{
		EventType: "CHAOS_TERMINATION",
		GroupType: "ASG",
		GroupName: asgName,
		ChaosType: strategy,
	})
	if err != nil {
		return nil, err
	}

	var resp chaosResponse
	if err := c.sendRequest("POST", url, bytes.NewReader(body), &resp); err != nil {
		return nil, err
	}

	return makeChaosEvent(&resp), nil
}

func (c *Client) GetEvents() ([]ChaosEvent, error) {
	url := c.config.Endpoint + "/simianarmy/api/v1/chaos"

	var resp []chaosResponse
	if err := c.sendRequest("GET", url, nil, &resp); err != nil {
		return nil, err
	}

	var events []ChaosEvent
	for _, r := range resp {
		events = append(events, *makeChaosEvent(&r))
	}

	return events, nil
}

func (c *Client) sendRequest(method, url string, body io.Reader, out interface{}) error {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}

	if c.config.Username != "" && c.config.Password != "" {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}

	resp, err := c.config.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return decodeError(resp)
	}

	return json.NewDecoder(resp.Body).Decode(out)
}

func decodeError(resp *http.Response) error {
	var m struct {
		Message string `json:"message"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&m); err == nil && m.Message != "" {
		return fmt.Errorf("%s", m.Message)
	}
	return fmt.Errorf("%s", resp.Status)
}

func makeChaosEvent(in *chaosResponse) *ChaosEvent {
	return &ChaosEvent{
		Strategy:   in.ChaosType,
		ASGName:    in.GroupName,
		InstanceID: in.EventID,
		Region:     in.Region,
		Time:       time.Unix(in.EventTime/1000, 0).UTC(),
	}
}
<|endoftext|>


<commit_before>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"github.com/uber/tchannel/golang/typed"
	"io"
	"time"
)

// Type of message
type messageType byte

const (
	messageTypeInitReq         messageType = 0x01
	messageTypeInitRes         messageType = 0x02
	messageTypeCallReq         messageType = 0x03
	messageTypeCallRes         messageType = 0x04
	messageTypeCallReqContinue messageType = 0x13
	messageTypeCallResContinue messageType = 0x14
	messageTypeError           messageType = 0xFF
)

var messageTypeNames = map[messageType]string{
	messageTypeInitReq:         "initReq",
	messageTypeInitRes:         "InitRes",
	messageTypeCallReq:         "CallReq",
	messageTypeCallReqContinue: "CallReqContinue",
	messageTypeCallRes:         "CallRes",
	messageTypeCallResContinue: "CallResContinue",
	messageTypeError:           "Error",
}

func (t messageType) String() string {
	return messageTypeNames[t]
}

// Base interface for messages.  Has an id and a type, and knows how to read and write onto a binary stream
type message interface {
	// The id of the message
	ID() uint32

	// The type of the message
	messageType() messageType

	read(r typed.ReadBuffer) error
	write(r typed.WriteBuffer) error
}

// Parameters to an initReq/InitRes
type initParams map[string]string

// Standard init params
const (
	InitParamHostPort    = "host_port"
	InitParamProcessName = "process_name"
)

type initMessage struct {
	id         uint32
	Version    uint16
	initParams initParams
}

func (m *initMessage) read(r typed.ReadBuffer) error {
	var err error
	m.Version, err = r.ReadUint16()
	if err != nil {
		return err
	}

	m.initParams = initParams{}
	for {
		klen, err := r.ReadUint16()
		if err == io.EOF {
			return nil
		}

		k, err := r.ReadString(int(klen))
		if err != nil {
			return err
		}

		vlen, err := r.ReadUint16()
		if err != nil {
			return err
		}

		v, err := r.ReadString(int(vlen))
		if err != nil {
			return err
		}

		m.initParams[k] = v
	}
}

func (m *initMessage) write(w typed.WriteBuffer) error {
	if err := w.WriteUint16(m.Version); err != nil {
		return err
	}

	for k, v := range m.initParams {
		if err := w.WriteUint16(uint16(len(k))); err != nil {
			return err
		}

		if err := w.WriteString(k); err != nil {
			return err
		}

		if err := w.WriteUint16(uint16(len(v))); err != nil {
			return err
		}

		if err := w.WriteString(v); err != nil {
			return err
		}
	}

	return nil
}

func (m *initMessage) ID() uint32 {
	return m.id
}

// An initReq, containing context information to exchange with peer
type initReq struct {
	initMessage
}

func (m *initReq) messageType() messageType { return messageTypeInitReq }

// An InitRes, containing context information to return to intiating peer
type initRes struct {
	initMessage
}

func (m *initRes) messageType() messageType { return messageTypeInitRes }

// Headers passed as part of a CallReq/CallRes
type callHeaders map[string]string

func (ch callHeaders) read(r typed.ReadBuffer) error {
	nh, err := r.ReadByte()
	if err != nil {
		return err
	}

	for i := 0; i < int(nh); i++ {
		klen, err := r.ReadByte()
		if err != nil {
			return err
		}

		k, err := r.ReadString(int(klen))
		if err != nil {
			return err
		}

		vlen, err := r.ReadByte()
		if err != nil {
			return err
		}

		v, err := r.ReadString(int(vlen))
		if err != nil {
			return err
		}

		ch[k] = v
	}

	return nil
}

func (ch callHeaders) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(len(ch))); err != nil {
		return err
	}

	for k, v := range ch {
		if err := w.WriteByte(byte(len(k))); err != nil {
			return err
		}

		if err := w.WriteString(k); err != nil {
			return err
		}

		if err := w.WriteByte(byte(len(v))); err != nil {
			return err
		}

		if err := w.WriteString(v); err != nil {
			return err
		}
	}

	return nil
}

// Tracing represents Zipkin-style tracing info
type Tracing struct {
	// The outer trace id.  Established at the outermost edge service and propagated through all calls
	TraceID uint64

	// The id of the parent span in this call graph
	ParentID uint64

	// The id of this specific RPC
	SpanID uint64
}

// A CallReq for service
type callReq struct {
	id         uint32
	TimeToLive time.Duration
	Tracing    Tracing
	TraceFlags byte
	Headers    callHeaders
	Service    []byte
}

func (m *callReq) ID() uint32               { return m.id }
func (m *callReq) messageType() messageType { return messageTypeCallReq }
func (m *callReq) read(r typed.ReadBuffer) error {
	var err error
	ttl, err := r.ReadUint32()
	if err != nil {
		return err
	}

	m.TimeToLive = time.Duration(ttl) * time.Millisecond
	m.Tracing.TraceID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.ParentID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.SpanID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.TraceFlags, err = r.ReadByte()
	if err != nil {
		return err
	}

	m.Headers = callHeaders{}
	if err := m.Headers.read(r); err != nil {
		return err
	}

	serviceNameLen, err := r.ReadUint16()
	if err != nil {
		return err
	}

	if m.Service, err = r.ReadBytes(int(serviceNameLen)); err != nil {
		return err
	}

	return nil
}

func (m *callReq) write(w typed.WriteBuffer) error {
	if err := w.WriteUint32(uint32(m.TimeToLive.Seconds() * 1000)); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.TraceID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.ParentID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.SpanID); err != nil {
		return err
	}

	if err := w.WriteByte(m.TraceFlags); err != nil {
		return err
	}

	if err := m.Headers.write(w); err != nil {
		return err
	}

	if err := w.WriteUint16(uint16(len(m.Service))); err != nil {
		return err
	}

	if err := w.WriteBytes(m.Service); err != nil {
		return err
	}

	return nil
}

// A continuatin of a previous CallReq
type callReqContinue struct {
	id uint32
}

func (c *callReqContinue) ID() uint32                      { return c.id }
func (c *callReqContinue) messageType() messageType        { return messageTypeCallReqContinue }
func (c *callReqContinue) read(r typed.ReadBuffer) error   { return nil }
func (c *callReqContinue) write(w typed.WriteBuffer) error { return nil }

// ResponseCode to a CallReq
type ResponseCode byte

const (
	responseOK               ResponseCode = 0x00
	responseApplicationError ResponseCode = 0x01
)

// A response to a CallReq
type callRes struct {
	id           uint32
	ResponseCode ResponseCode
	Tracing      Tracing
	TraceFlags   byte
	Headers      callHeaders
}

func (m *callRes) ID() uint32               { return m.id }
func (m *callRes) messageType() messageType { return messageTypeCallRes }

func (m *callRes) read(r typed.ReadBuffer) error {
	var err error
	c, err := r.ReadByte()
	if err != nil {
		return err
	}
	m.ResponseCode = ResponseCode(c)
	m.Tracing.TraceID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.ParentID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.SpanID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.TraceFlags, err = r.ReadByte()
	if err != nil {
		return err
	}

	m.Headers = callHeaders{}
	if err := m.Headers.read(r); err != nil {
		return err
	}

	return nil
}

func (m *callRes) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(m.ResponseCode)); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.TraceID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.ParentID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.SpanID); err != nil {
		return err
	}

	if err := w.WriteByte(m.TraceFlags); err != nil {
		return err
	}

	if err := m.Headers.write(w); err != nil {
		return err
	}

	return nil
}

// A continuation of a previous CallRes
type callResContinue struct {
	id uint32
}

func (c *callResContinue) ID() uint32                      { return c.id }
func (c *callResContinue) messageType() messageType        { return messageTypeCallResContinue }
func (c *callResContinue) read(r typed.ReadBuffer) error   { return nil }
func (c *callResContinue) write(w typed.WriteBuffer) error { return nil }

// An Error message, a system-level error response to a request or a protocol level error
type errorMessage struct {
	id                uint32
	errorCode         SystemErrorCode
	originalMessageID uint32
	message           string
}

func (m *errorMessage) ID() uint32               { return m.id }
func (m *errorMessage) messageType() messageType { return messageTypeError }
func (m *errorMessage) read(r typed.ReadBuffer) error {
	errCode, err := r.ReadByte()
	if err != nil {
		return err
	}

	m.errorCode = SystemErrorCode(errCode)

	if m.originalMessageID, err = r.ReadUint32(); err != nil {
		return err
	}

	msgSize, err := r.ReadUint16()
	if err != nil {
		return err
	}

	if m.message, err = r.ReadString(int(msgSize)); err != nil {
		return err
	}

	return nil
}

func (m *errorMessage) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(m.errorCode)); err != nil {
		return err
	}

	if err := w.WriteUint32(m.originalMessageID); err != nil {
		return err
	}

	if err := w.WriteUint16(uint16(len(m.message))); err != nil {
		return err
	}

	if err := w.WriteString(m.message); err != nil {
		return err
	}

	return nil
}

func (m errorMessage) AsSystemError() error {
	// TODO(mmihic): Might be nice to return one of the well defined error types
	return NewSystemError(m.errorCode, m.message)
}
<commit_msg>Switch to service~1<commit_after>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"github.com/uber/tchannel/golang/typed"
	"io"
	"time"
)

// Type of message
type messageType byte

const (
	messageTypeInitReq         messageType = 0x01
	messageTypeInitRes         messageType = 0x02
	messageTypeCallReq         messageType = 0x03
	messageTypeCallRes         messageType = 0x04
	messageTypeCallReqContinue messageType = 0x13
	messageTypeCallResContinue messageType = 0x14
	messageTypeError           messageType = 0xFF
)

var messageTypeNames = map[messageType]string{
	messageTypeInitReq:         "initReq",
	messageTypeInitRes:         "InitRes",
	messageTypeCallReq:         "CallReq",
	messageTypeCallReqContinue: "CallReqContinue",
	messageTypeCallRes:         "CallRes",
	messageTypeCallResContinue: "CallResContinue",
	messageTypeError:           "Error",
}

func (t messageType) String() string {
	return messageTypeNames[t]
}

// Base interface for messages.  Has an id and a type, and knows how to read and write onto a binary stream
type message interface {
	// The id of the message
	ID() uint32

	// The type of the message
	messageType() messageType

	read(r typed.ReadBuffer) error
	write(r typed.WriteBuffer) error
}

// Parameters to an initReq/InitRes
type initParams map[string]string

// Standard init params
const (
	InitParamHostPort    = "host_port"
	InitParamProcessName = "process_name"
)

type initMessage struct {
	id         uint32
	Version    uint16
	initParams initParams
}

func (m *initMessage) read(r typed.ReadBuffer) error {
	var err error
	m.Version, err = r.ReadUint16()
	if err != nil {
		return err
	}

	m.initParams = initParams{}
	for {
		klen, err := r.ReadUint16()
		if err == io.EOF {
			return nil
		}

		k, err := r.ReadString(int(klen))
		if err != nil {
			return err
		}

		vlen, err := r.ReadUint16()
		if err != nil {
			return err
		}

		v, err := r.ReadString(int(vlen))
		if err != nil {
			return err
		}

		m.initParams[k] = v
	}
}

func (m *initMessage) write(w typed.WriteBuffer) error {
	if err := w.WriteUint16(m.Version); err != nil {
		return err
	}

	for k, v := range m.initParams {
		if err := w.WriteUint16(uint16(len(k))); err != nil {
			return err
		}

		if err := w.WriteString(k); err != nil {
			return err
		}

		if err := w.WriteUint16(uint16(len(v))); err != nil {
			return err
		}

		if err := w.WriteString(v); err != nil {
			return err
		}
	}

	return nil
}

func (m *initMessage) ID() uint32 {
	return m.id
}

// An initReq, containing context information to exchange with peer
type initReq struct {
	initMessage
}

func (m *initReq) messageType() messageType { return messageTypeInitReq }

// An InitRes, containing context information to return to intiating peer
type initRes struct {
	initMessage
}

func (m *initRes) messageType() messageType { return messageTypeInitRes }

// Headers passed as part of a CallReq/CallRes
type callHeaders map[string]string

func (ch callHeaders) read(r typed.ReadBuffer) error {
	nh, err := r.ReadByte()
	if err != nil {
		return err
	}

	for i := 0; i < int(nh); i++ {
		klen, err := r.ReadByte()
		if err != nil {
			return err
		}

		k, err := r.ReadString(int(klen))
		if err != nil {
			return err
		}

		vlen, err := r.ReadByte()
		if err != nil {
			return err
		}

		v, err := r.ReadString(int(vlen))
		if err != nil {
			return err
		}

		ch[k] = v
	}

	return nil
}

func (ch callHeaders) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(len(ch))); err != nil {
		return err
	}

	for k, v := range ch {
		if err := w.WriteByte(byte(len(k))); err != nil {
			return err
		}

		if err := w.WriteString(k); err != nil {
			return err
		}

		if err := w.WriteByte(byte(len(v))); err != nil {
			return err
		}

		if err := w.WriteString(v); err != nil {
			return err
		}
	}

	return nil
}

// Tracing represents Zipkin-style tracing info
type Tracing struct {
	// The outer trace id.  Established at the outermost edge service and propagated through all calls
	TraceID uint64

	// The id of the parent span in this call graph
	ParentID uint64

	// The id of this specific RPC
	SpanID uint64
}

// A CallReq for service
type callReq struct {
	id         uint32
	TimeToLive time.Duration
	Tracing    Tracing
	TraceFlags byte
	Headers    callHeaders
	Service    []byte
}

func (m *callReq) ID() uint32               { return m.id }
func (m *callReq) messageType() messageType { return messageTypeCallReq }
func (m *callReq) read(r typed.ReadBuffer) error {
	var err error
	ttl, err := r.ReadUint32()
	if err != nil {
		return err
	}

	m.TimeToLive = time.Duration(ttl) * time.Millisecond
	m.Tracing.TraceID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.ParentID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.SpanID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.TraceFlags, err = r.ReadByte()
	if err != nil {
		return err
	}

	m.Headers = callHeaders{}
	if err := m.Headers.read(r); err != nil {
		return err
	}

	serviceNameLen, err := r.ReadByte()
	if err != nil {
		return err
	}

	if m.Service, err = r.ReadBytes(int(serviceNameLen)); err != nil {
		return err
	}

	return nil
}

func (m *callReq) write(w typed.WriteBuffer) error {
	if err := w.WriteUint32(uint32(m.TimeToLive.Seconds() * 1000)); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.TraceID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.ParentID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.SpanID); err != nil {
		return err
	}

	if err := w.WriteByte(m.TraceFlags); err != nil {
		return err
	}

	if err := m.Headers.write(w); err != nil {
		return err
	}

	if err := w.WriteByte(byte(len(m.Service))); err != nil {
		return err
	}

	if err := w.WriteBytes(m.Service); err != nil {
		return err
	}

	return nil
}

// A continuatin of a previous CallReq
type callReqContinue struct {
	id uint32
}

func (c *callReqContinue) ID() uint32                      { return c.id }
func (c *callReqContinue) messageType() messageType        { return messageTypeCallReqContinue }
func (c *callReqContinue) read(r typed.ReadBuffer) error   { return nil }
func (c *callReqContinue) write(w typed.WriteBuffer) error { return nil }

// ResponseCode to a CallReq
type ResponseCode byte

const (
	responseOK               ResponseCode = 0x00
	responseApplicationError ResponseCode = 0x01
)

// A response to a CallReq
type callRes struct {
	id           uint32
	ResponseCode ResponseCode
	Tracing      Tracing
	TraceFlags   byte
	Headers      callHeaders
}

func (m *callRes) ID() uint32               { return m.id }
func (m *callRes) messageType() messageType { return messageTypeCallRes }

func (m *callRes) read(r typed.ReadBuffer) error {
	var err error
	c, err := r.ReadByte()
	if err != nil {
		return err
	}
	m.ResponseCode = ResponseCode(c)
	m.Tracing.TraceID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.ParentID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.Tracing.SpanID, err = r.ReadUint64()
	if err != nil {
		return err
	}

	m.TraceFlags, err = r.ReadByte()
	if err != nil {
		return err
	}

	m.Headers = callHeaders{}
	if err := m.Headers.read(r); err != nil {
		return err
	}

	return nil
}

func (m *callRes) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(m.ResponseCode)); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.TraceID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.ParentID); err != nil {
		return err
	}

	if err := w.WriteUint64(m.Tracing.SpanID); err != nil {
		return err
	}

	if err := w.WriteByte(m.TraceFlags); err != nil {
		return err
	}

	if err := m.Headers.write(w); err != nil {
		return err
	}

	return nil
}

// A continuation of a previous CallRes
type callResContinue struct {
	id uint32
}

func (c *callResContinue) ID() uint32                      { return c.id }
func (c *callResContinue) messageType() messageType        { return messageTypeCallResContinue }
func (c *callResContinue) read(r typed.ReadBuffer) error   { return nil }
func (c *callResContinue) write(w typed.WriteBuffer) error { return nil }

// An Error message, a system-level error response to a request or a protocol level error
type errorMessage struct {
	id                uint32
	errorCode         SystemErrorCode
	originalMessageID uint32
	message           string
}

func (m *errorMessage) ID() uint32               { return m.id }
func (m *errorMessage) messageType() messageType { return messageTypeError }
func (m *errorMessage) read(r typed.ReadBuffer) error {
	errCode, err := r.ReadByte()
	if err != nil {
		return err
	}

	m.errorCode = SystemErrorCode(errCode)

	if m.originalMessageID, err = r.ReadUint32(); err != nil {
		return err
	}

	msgSize, err := r.ReadUint16()
	if err != nil {
		return err
	}

	if m.message, err = r.ReadString(int(msgSize)); err != nil {
		return err
	}

	return nil
}

func (m *errorMessage) write(w typed.WriteBuffer) error {
	if err := w.WriteByte(byte(m.errorCode)); err != nil {
		return err
	}

	if err := w.WriteUint32(m.originalMessageID); err != nil {
		return err
	}

	if err := w.WriteUint16(uint16(len(m.message))); err != nil {
		return err
	}

	if err := w.WriteString(m.message); err != nil {
		return err
	}

	return nil
}

func (m errorMessage) AsSystemError() error {
	// TODO(mmihic): Might be nice to return one of the well defined error types
	return NewSystemError(m.errorCode, m.message)
}
<|endoftext|>


<commit_before>package ops

import (
	"bsearch/index"
	"sort"
)

type attr struct {
	docs    []index.IbDoc
	currptr int
}

// QueryOp that is the set of all documents for one attribute.
func NewAttr(in *index.Index, key string) QueryOp {
	a := in.Attrs[key]
	if a == nil {
		return nil
	}
	return &attr{in.Attrs[key], 0}
}

func (ba *attr) CurrentDoc() *index.IbDoc {
	if ba.currptr == -1 {
		return nil
	}
	return &ba.docs[ba.currptr]
}

func (ba *attr) NextDoc(search *index.IbDoc) *index.IbDoc {
	if search == nil {
		r := &ba.docs[ba.currptr]
		ba.currptr++
		return r
	}

	from := ba.currptr
	l := len(ba.docs) - from
	i := sort.Search(l, func (i int) bool {
		d := ba.docs[from + i]
		if search.Order > d.Order {
			return true
		} else if search.Order == d.Order {
			return search.Id >= d.Id
		}
		return false
	});
	if i == l {
		ba.currptr = -1
		return nil
	}
	ba.currptr = from + i
	return &ba.docs[from + i]
}
<commit_msg>No need to keep currptr, just shrink the slice.<commit_after>package ops

import (
	"bsearch/index"
	"sort"
)

type attr struct {
	docs    []index.IbDoc
}

// QueryOp that is the set of all documents for one attribute.
func NewAttr(in *index.Index, key string) QueryOp {
	a := in.Attrs[key]
	if a == nil {
		return nil
	}
	return &attr{in.Attrs[key]}
}

func (ba *attr) CurrentDoc() *index.IbDoc {
	if ba.docs == nil {
		return nil
	}
	return &ba.docs[0]
}

func (ba *attr) NextDoc(search *index.IbDoc) *index.IbDoc {
	if search == nil {
		return ba.CurrentDoc()
	}

	l := len(ba.docs)
	i := sort.Search(l, func (i int) bool {
		d := ba.docs[i]
		if search.Order > d.Order {
			return true
		} else if search.Order == d.Order {
			return search.Id >= d.Id
		}
		return false
	});
	if i == l {
		ba.docs = nil
		return nil
	}
	ba.docs = ba.docs[i:]
	return &ba.docs[0]
}
<|endoftext|>


<commit_before>// ATM - Automatic TempUrl Maker
// A builder of Swift TempURLs
// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
package main

import (
	"fmt"
	"log"
	"os"
	"os/user"

	"github.com/codegangsta/cli"
	"github.com/glennsb/atm"
	"github.com/howeyc/gopass"
)

func main() {
	app := cli.NewApp()
	app.Name = "atm"
	app.Usage = "Automated TempURL Maker"
	app.Version = "0.0.1 - 20151025"
	app.Author = "Stuart Glenn"
	app.Email = "Stuart-Glenn@omrf.org"
	app.Copyright = "2015 Stuart Glenn, All rights reserved"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "V, verbose",
			Usage: "show more output",
		},
	}

	app.Commands = clientCommands()
	app.Commands = append(app.Commands, serverCommand())
	app.RunAndExitOnError()
}

func clientCommands() []cli.Command {
	return []cli.Command{
		cli.Command{
			Name:        "url",
			Usage:       "Request a temp url to Account/Container/Object",
			ArgsUsage:   "<Account> <Container> <Object> ",
			Description: "Send a request to the ATM service for a tempurl",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "api-key, k",
					Usage:  "account/user atm api-key",
					EnvVar: "ATM_API_KEY",
				},
				cli.StringFlag{
					Name:   "api-secret, s",
					Usage:  "account/user atm api-secret",
					EnvVar: "ATM_API_SECRET",
				},
				cli.StringFlag{
					Name:   "atm-host, a",
					Usage:  "atm server endpoint",
					EnvVar: "ATM_HOST",
				},
				cli.StringFlag{
					Name:  "method, m",
					Usage: "HTTP method requested for temp url",
					Value: "GET",
				},
			},
			Action: func(c *cli.Context) {
				method := c.String("method")
				if "" == method {
					fmt.Fprintf(os.Stderr, "Missing HTTP method option\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				account := c.Args().Get(0)
				if "" == account {
					fmt.Fprintf(os.Stderr, "Missing Account argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				container := c.Args().Get(1)
				if "" == container {
					fmt.Fprintf(os.Stderr, "Missing Container argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				object := c.Args().Get(2)
				if "" == object {
					fmt.Fprintf(os.Stderr, "Missing Object argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				atm := &atm.AtmClient{
					ApiKey:    c.String("api-key"),
					ApiSecret: c.String("api-secret"),
					AtmHost:   c.String("atm-host"),
				}
				url, err := atm.RequestTempUrl(method, account, container, object)
				if nil != err {
					log.Fatal(err)
					return
				}
				fmt.Println(url)
			},
		},

		cli.Command{
			Name:  "key",
			Usage: "Add/Remove signing key",
			Action: func(c *cli.Context) {
				log.Fatal("Not implemented yet")
				return
			},
		},
	}
}

func serverFlags() []cli.Flag {
	current_user, err := user.Current()
	default_username := ""
	if nil == err {
		default_username = current_user.Username
	}
	return []cli.Flag{
		cli.StringFlag{
			Name:  "database",
			Usage: "name of database",
			Value: "atm",
		},
		cli.StringFlag{
			Name:  "database-host",
			Usage: "hostname of database server",
			Value: "localhost",
		},
		cli.StringFlag{
			Name:  "database-user",
			Usage: "username for database connection",
			Value: default_username,
		},
		cli.IntFlag{
			Name:  "database-port",
			Usage: "port number of database server",
			Value: 3306,
		},
		cli.DurationFlag{
			Name:  "duration",
			Usage: "Default lifetime for generated tempurl",
			Value: atm.DURATION,
		},
		cli.StringFlag{
			Name:  "object-host, host",
			Usage: "Swift service host prefix",
			Value: atm.HOST,
		},
	}
}

func serverCommand() cli.Command {
	return cli.Command{
		Name:  "server",
		Usage: "Run webservice",
		Flags: serverFlags(),
		Action: func(c *cli.Context) {
			db_user := c.String("database-user")
			db_host := c.String("database-host")
			db := c.String("database")

			fmt.Printf("%s@%s/%s password: ", db_user, db_host, db)
			db_pass := string(gopass.GetPasswd())

			ds, err := atm.NewDatastore("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
				db_user, db_pass, db_host, c.Int("database-port"), db))
			if nil != err {
				log.Fatal(err)
				return
			}
			db_pass = ""
			defer ds.Close()

			service := &atm.Server{
				Ds:               ds,
				Object_host:      c.String("object-host"),
				Default_duration: int64(c.Duration("duration").Seconds()),
			}
			service.Run()
		},
	}
}
<commit_msg>Bumb version<commit_after>// ATM - Automatic TempUrl Maker
// A builder of Swift TempURLs
// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
package main

import (
	"fmt"
	"log"
	"os"
	"os/user"

	"github.com/codegangsta/cli"
	"github.com/glennsb/atm"
	"github.com/howeyc/gopass"
)

func main() {
	app := cli.NewApp()
	app.Name = "atm"
	app.Usage = "Automated TempURL Maker"
	app.Version = "0.0.2 - 20151026"
	app.Author = "Stuart Glenn"
	app.Email = "Stuart-Glenn@omrf.org"
	app.Copyright = "2015 Stuart Glenn, All rights reserved"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "V, verbose",
			Usage: "show more output",
		},
	}

	app.Commands = clientCommands()
	app.Commands = append(app.Commands, serverCommand())
	app.RunAndExitOnError()
}

func clientCommands() []cli.Command {
	return []cli.Command{
		cli.Command{
			Name:        "url",
			Usage:       "Request a temp url to Account/Container/Object",
			ArgsUsage:   "<Account> <Container> <Object> ",
			Description: "Send a request to the ATM service for a tempurl",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "api-key, k",
					Usage:  "account/user atm api-key",
					EnvVar: "ATM_API_KEY",
				},
				cli.StringFlag{
					Name:   "api-secret, s",
					Usage:  "account/user atm api-secret",
					EnvVar: "ATM_API_SECRET",
				},
				cli.StringFlag{
					Name:   "atm-host, a",
					Usage:  "atm server endpoint",
					EnvVar: "ATM_HOST",
				},
				cli.StringFlag{
					Name:  "method, m",
					Usage: "HTTP method requested for temp url",
					Value: "GET",
				},
			},
			Action: func(c *cli.Context) {
				method := c.String("method")
				if "" == method {
					fmt.Fprintf(os.Stderr, "Missing HTTP method option\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				account := c.Args().Get(0)
				if "" == account {
					fmt.Fprintf(os.Stderr, "Missing Account argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				container := c.Args().Get(1)
				if "" == container {
					fmt.Fprintf(os.Stderr, "Missing Container argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				object := c.Args().Get(2)
				if "" == object {
					fmt.Fprintf(os.Stderr, "Missing Object argument\n")
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				atm := &atm.AtmClient{
					ApiKey:    c.String("api-key"),
					ApiSecret: c.String("api-secret"),
					AtmHost:   c.String("atm-host"),
				}
				url, err := atm.RequestTempUrl(method, account, container, object)
				if nil != err {
					log.Fatal(err)
					return
				}
				fmt.Println(url)
			},
		},

		cli.Command{
			Name:  "key",
			Usage: "Add/Remove signing key",
			Action: func(c *cli.Context) {
				log.Fatal("Not implemented yet")
				return
			},
		},
	}
}

func serverFlags() []cli.Flag {
	current_user, err := user.Current()
	default_username := ""
	if nil == err {
		default_username = current_user.Username
	}
	return []cli.Flag{
		cli.StringFlag{
			Name:  "database",
			Usage: "name of database",
			Value: "atm",
		},
		cli.StringFlag{
			Name:  "database-host",
			Usage: "hostname of database server",
			Value: "localhost",
		},
		cli.StringFlag{
			Name:  "database-user",
			Usage: "username for database connection",
			Value: default_username,
		},
		cli.IntFlag{
			Name:  "database-port",
			Usage: "port number of database server",
			Value: 3306,
		},
		cli.DurationFlag{
			Name:  "duration",
			Usage: "Default lifetime for generated tempurl",
			Value: atm.DURATION,
		},
		cli.StringFlag{
			Name:  "object-host, host",
			Usage: "Swift service host prefix",
			Value: atm.HOST,
		},
	}
}

func serverCommand() cli.Command {
	return cli.Command{
		Name:  "server",
		Usage: "Run webservice",
		Flags: serverFlags(),
		Action: func(c *cli.Context) {
			db_user := c.String("database-user")
			db_host := c.String("database-host")
			db := c.String("database")

			fmt.Printf("%s@%s/%s password: ", db_user, db_host, db)
			db_pass := string(gopass.GetPasswd())

			ds, err := atm.NewDatastore("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
				db_user, db_pass, db_host, c.Int("database-port"), db))
			if nil != err {
				log.Fatal(err)
				return
			}
			db_pass = ""
			defer ds.Close()

			service := &atm.Server{
				Ds:               ds,
				Object_host:      c.String("object-host"),
				Default_duration: int64(c.Duration("duration").Seconds()),
			}
			service.Run()
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"../godump978"
	rtl "github.com/jpoirier/gortlsdr"
)

type UAT struct {
	dev     *rtl.Context
	indexID int
}

type ES struct {
	dev     *rtl.Context
	indexID int
}

var UATDev *UAT
var ESDev *ES

var uat_shutdown chan int
var uat_wg *sync.WaitGroup = &sync.WaitGroup{}

var es_shutdown chan int
var es_wg *sync.WaitGroup = &sync.WaitGroup{}

var maxSignalStrength int

func (e *ES) read() {
	defer es_wg.Done()
	log.Println("Entered ES read() ...")
	cmd := exec.Command("/usr/bin/dump1090", "--net", "--device-index", strconv.Itoa(e.indexID))
	err := cmd.Start()
	if err != nil {
		log.Printf("Error executing /usr/bin/dump1090: %s\n", err.Error())
		return
	}
	log.Println("Executed /usr/bin/dump1090 successfully...")
	for {
		select {
		default:
			time.Sleep(1 * time.Second)
		case _ = <-es_shutdown:
			log.Println("ES read(): shutdown msg received, calling cmd.Process.Kill() ...")
			cmd.Process.Kill()
			log.Println("\t kill successful...")
			return
		}
	}
}

func (u *UAT) read() {
	defer uat_wg.Done()
	// defer u.dev.Close()
	log.Println("Entered UAT read() ...")
	var buffer = make([]uint8, rtl.DefaultBufLength)
	for {
		select {
		default:
			nRead, err := u.dev.ReadSync(buffer, rtl.DefaultBufLength)
			if err != nil {
				log.Printf("\tReadSync Failed - error: %s\n", err)
				break
			}
			// log.Printf("\tReadSync %d\n", nRead)
			if nRead > 0 {
				buf := buffer[:nRead]
				godump978.InChan <- buf
			}
		case _ = <-uat_shutdown:
			log.Println("UAT read(): shutdown msg received...")
			return
		}
	}
}

func (e *ES) sdrConfig() (err error) {
	return
}

// Read 978MHz from SDR.
func (u *UAT) sdrConfig() (err error) {
	log.Printf("===== UAT Device name: %s =====\n", rtl.GetDeviceName(u.indexID))
	if u.dev, err = rtl.Open(u.indexID); err != nil {
		log.Printf("\tUAT Open Failed...\n")
		return
	}
	log.Printf("\tGetTunerType: %s\n", u.dev.GetTunerType())

	//---------- Set Tuner Gain ----------
	err = u.dev.SetTunerGainMode(true)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerGainMode Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetTunerGainMode Successful\n")
	}

	tgain := 480
	err = u.dev.SetTunerGain(tgain)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerGain Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetTunerGain Successful\n")
	}

	//---------- Get/Set Sample Rate ----------
	samplerate := 2083334
	err = u.dev.SetSampleRate(samplerate)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetSampleRate Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetSampleRate - rate: %d\n", samplerate)
	}
	log.Printf("\tGetSampleRate: %d\n", u.dev.GetSampleRate())

	//---------- Get/Set Xtal Freq ----------
	rtlFreq, tunerFreq, err := u.dev.GetXtalFreq()
	if err != nil {
		u.dev.Close()
		log.Printf("\tGetXtalFreq Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tGetXtalFreq - Rtl: %d, Tuner: %d\n", rtlFreq, tunerFreq)
	}

	newRTLFreq := 28800000
	newTunerFreq := 28800000
	err = u.dev.SetXtalFreq(newRTLFreq, newTunerFreq)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetXtalFreq Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetXtalFreq - Center freq: %d, Tuner freq: %d\n",
			newRTLFreq, newTunerFreq)
	}

	//---------- Get/Set Center Freq ----------
	err = u.dev.SetCenterFreq(978000000)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetCenterFreq 978MHz Failed, error: %s\n", err)
		return
	} else {
		log.Printf("\tSetCenterFreq 978MHz Successful\n")
	}

	log.Printf("\tGetCenterFreq: %d\n", u.dev.GetCenterFreq())

	//---------- Set Bandwidth ----------
	bw := 1000000
	log.Printf("\tSetting Bandwidth: %d\n", bw)
	if err = u.dev.SetTunerBw(bw); err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerBw %d Failed, error: %s\n", bw, err)
		return
	} else {
		log.Printf("\tSetTunerBw %d Successful\n", bw)
	}

	if err = u.dev.ResetBuffer(); err != nil {
		u.dev.Close()
		log.Printf("\tResetBuffer Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tResetBuffer Successful\n")
	}
	//---------- Get/Set Freq Correction ----------
	freqCorr := u.dev.GetFreqCorrection()
	log.Printf("\tGetFreqCorrection: %d\n", freqCorr)
	err = u.dev.SetFreqCorrection(globalSettings.PPM)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetFreqCorrection %d Failed, error: %s\n", globalSettings.PPM, err)
		return
	} else {
		log.Printf("\tSetFreqCorrection %d Successful\n", globalSettings.PPM)
	}
	return
}

// Read from the godump978 channel - on or off.
func uatReader() {
	log.Println("Entered uatReader() ...")
	for {
		uat := <-godump978.OutChan
		o, msgtype := parseInput(uat)
		if o != nil && msgtype != 0 {
			relayMessage(msgtype, o)
		}
	}
}

func (u *UAT) shutdown() {
	log.Println("Entered UAT shutdown() ...")
	close(uat_shutdown) // signal to shutdown
	log.Println("UAT shutdown(): closing device ...")
	// XXX: how to preempt ReadSync?
	// the assumption is that calling Close()
	// causes ReadSync to return immediately
	u.dev.Close()
	log.Println("UAT shutdown(): calling uat_wg.Wait() ...")
	uat_wg.Wait() // Wait for the goroutine to shutdown
	log.Println("UAT shutdown(): uat_wg.Wait() returned...")
}

func (e *ES) shutdown() {
	log.Println("Entered ES shutdown() ...")
	close(es_shutdown) // signal to shutdown
	log.Println("ES shutdown(): calling es_wg.Wait() ...")
	es_wg.Wait() // Wait for the goroutine to shutdown
	log.Println("ES shutdown(): es_wg.Wait() returned...")
}

var devMap = map[int]string{0: "", 1: ""}

// Watch for config/device changes.
func sdrWatcher() {
	for {
		time.Sleep(1 * time.Second)
		count := rtl.GetDeviceCount()
		log.Println("DeviceCount...", count)

		// support two and only two dongles
		if count > 2 {
			count = 2
		}

		// cleanup if necessary
		if count < 1 || (!globalSettings.UAT_Enabled && !globalSettings.ES_Enabled) {
			log.Println("doing cleanup...")
			if UATDev != nil {
				UATDev.shutdown()
				UATDev = nil
			}
			if ESDev != nil {
				ESDev.shutdown()
				ESDev = nil
			}
			continue
		}

		if count == 1 {
			if UATDev != nil && ESDev == nil {
				UATDev.indexID = 0
			} else if UATDev == nil && ESDev != nil {
				ESDev.indexID = 0
			}
		}

		ids := []string{"", ""}
		for i := 0; i < count; i++ {
			// manufact, product, serial, err
			_, _, s, _ := rtl.GetDeviceUsbStrings(i)
			ids[i] = s
		}

		// UAT specific handling
		// When count is one, favor UAT in the case where the user
		// has enabled both UAT and ES via the web interface.
		id := 0
		if globalSettings.UAT_Enabled {
			if count == 1 {
				if ESDev != nil {
					ESDev.shutdown()
					ESDev = nil
				}
			} else { // count == 2
				if UATDev == nil && ESDev != nil {
					if ESDev.indexID == 0 {
						id = 1
					}
				}
			}

			if UATDev == nil {
				// preference check based on stratux
				// hardware serial when it exists
				serial := ids[id]
				if strings.HasPrefix(serial, "stratux:1090") {
					log.Println("Settings conflict: 978UAT set via WebUI but hardware serial says stratux:1090")
				} else {
					UATDev = &UAT{indexID: id}
					if err := UATDev.sdrConfig(); err != nil {
						UATDev = nil
					} else {
						uat_shutdown = make(chan int)
						uat_wg.Add(1)
						go UATDev.read()
					}
				}
			}
		} else if UATDev != nil {
			UATDev.shutdown()
			UATDev = nil
			if count == 1 && ESDev != nil {
				ESDev.indexID = 0
			}
		}

		// ES specific handling
		id = 0
		if globalSettings.ES_Enabled {
			if count == 1 {
				if globalSettings.UAT_Enabled {
					// defer to the UAT handler
					goto End
				}
			} else { // count == 2
				if ESDev == nil && UATDev != nil {
					if UATDev.indexID == 0 {
						id = 1
					}
				}
			}

			if ESDev == nil {
				// preference check based on stratux
				// hardware serial when it exists
				serial := ids[id]
				if strings.HasPrefix(serial, "stratux:978") {
					log.Println("Settings conflict: 1090ES set via WebUI but hardware serial says stratux:978")
				} else {
					ESDev = &ES{indexID: id}
					if err := ESDev.sdrConfig(); err != nil {
						ESDev = nil
					} else {
						es_shutdown = make(chan int)
						es_wg.Add(1)
						go ESDev.read()
					}
				}
			}
		} else if ESDev != nil {
			ESDev.shutdown()
			ESDev = nil
			if count == 1 && UATDev != nil {
				UATDev.indexID = 0
			}
		}
	End:
	}
}

func sdrInit() {
	go sdrWatcher()
	go uatReader()
	godump978.Dump978Init()
	go godump978.ProcessDataFromChannel()
}
<commit_msg>minorcode changes<commit_after>package main

import (
	"log"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"../godump978"
	rtl "github.com/jpoirier/gortlsdr"
)

type UAT struct {
	dev     *rtl.Context
	indexID int
}

type ES struct {
	dev     *rtl.Context
	indexID int
}

var UATDev *UAT
var ESDev *ES

var uat_shutdown chan int
var uat_wg *sync.WaitGroup = &sync.WaitGroup{}

var es_shutdown chan int
var es_wg *sync.WaitGroup = &sync.WaitGroup{}

var maxSignalStrength int

func (e *ES) read() {
	defer es_wg.Done()
	log.Println("Entered ES read() ...")
	cmd := exec.Command("/usr/bin/dump1090", "--net", "--device-index", strconv.Itoa(e.indexID))
	err := cmd.Start()
	if err != nil {
		log.Printf("Error executing /usr/bin/dump1090: %s\n", err.Error())
		return
	}
	log.Println("Executed /usr/bin/dump1090 successfully...")
	for {
		select {
		default:
			time.Sleep(1 * time.Second)
		case <-es_shutdown:
			log.Println("ES read(): shutdown msg received, calling cmd.Process.Kill() ...")
			cmd.Process.Kill()
			log.Println("\t kill successful...")
			return
		}
	}
}

func (u *UAT) read() {
	defer uat_wg.Done()
	log.Println("Entered UAT read() ...")
	var buffer = make([]uint8, rtl.DefaultBufLength)
	for {
		select {
		default:
			nRead, err := u.dev.ReadSync(buffer, rtl.DefaultBufLength)
			if err != nil {
				log.Printf("\tReadSync Failed - error: %s\n", err)
				break
			}
			// log.Printf("\tReadSync %d\n", nRead)
			if nRead > 0 {
				buf := buffer[:nRead]
				godump978.InChan <- buf
			}
		case <-uat_shutdown:
			log.Println("UAT read(): shutdown msg received...")
			return
		}
	}
}

func (e *ES) sdrConfig() (err error) {
	return
}

// Read 978MHz from SDR.
func (u *UAT) sdrConfig() (err error) {
	log.Printf("===== UAT Device name: %s =====\n", rtl.GetDeviceName(u.indexID))
	if u.dev, err = rtl.Open(u.indexID); err != nil {
		log.Printf("\tUAT Open Failed...\n")
		return
	}
	log.Printf("\tGetTunerType: %s\n", u.dev.GetTunerType())

	//---------- Set Tuner Gain ----------
	err = u.dev.SetTunerGainMode(true)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerGainMode Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetTunerGainMode Successful\n")
	}

	tgain := 480
	err = u.dev.SetTunerGain(tgain)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerGain Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetTunerGain Successful\n")
	}

	//---------- Get/Set Sample Rate ----------
	samplerate := 2083334
	err = u.dev.SetSampleRate(samplerate)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetSampleRate Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetSampleRate - rate: %d\n", samplerate)
	}
	log.Printf("\tGetSampleRate: %d\n", u.dev.GetSampleRate())

	//---------- Get/Set Xtal Freq ----------
	rtlFreq, tunerFreq, err := u.dev.GetXtalFreq()
	if err != nil {
		u.dev.Close()
		log.Printf("\tGetXtalFreq Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tGetXtalFreq - Rtl: %d, Tuner: %d\n", rtlFreq, tunerFreq)
	}

	newRTLFreq := 28800000
	newTunerFreq := 28800000
	err = u.dev.SetXtalFreq(newRTLFreq, newTunerFreq)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetXtalFreq Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tSetXtalFreq - Center freq: %d, Tuner freq: %d\n",
			newRTLFreq, newTunerFreq)
	}

	//---------- Get/Set Center Freq ----------
	err = u.dev.SetCenterFreq(978000000)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetCenterFreq 978MHz Failed, error: %s\n", err)
		return
	} else {
		log.Printf("\tSetCenterFreq 978MHz Successful\n")
	}

	log.Printf("\tGetCenterFreq: %d\n", u.dev.GetCenterFreq())

	//---------- Set Bandwidth ----------
	bw := 1000000
	log.Printf("\tSetting Bandwidth: %d\n", bw)
	if err = u.dev.SetTunerBw(bw); err != nil {
		u.dev.Close()
		log.Printf("\tSetTunerBw %d Failed, error: %s\n", bw, err)
		return
	} else {
		log.Printf("\tSetTunerBw %d Successful\n", bw)
	}

	if err = u.dev.ResetBuffer(); err != nil {
		u.dev.Close()
		log.Printf("\tResetBuffer Failed - error: %s\n", err)
		return
	} else {
		log.Printf("\tResetBuffer Successful\n")
	}
	//---------- Get/Set Freq Correction ----------
	freqCorr := u.dev.GetFreqCorrection()
	log.Printf("\tGetFreqCorrection: %d\n", freqCorr)
	err = u.dev.SetFreqCorrection(globalSettings.PPM)
	if err != nil {
		u.dev.Close()
		log.Printf("\tSetFreqCorrection %d Failed, error: %s\n", globalSettings.PPM, err)
		return
	} else {
		log.Printf("\tSetFreqCorrection %d Successful\n", globalSettings.PPM)
	}
	return
}

// Read from the godump978 channel - on or off.
func uatReader() {
	log.Println("Entered uatReader() ...")
	for {
		uat := <-godump978.OutChan
		o, msgtype := parseInput(uat)
		if o != nil && msgtype != 0 {
			relayMessage(msgtype, o)
		}
	}
}

func (u *UAT) shutdown() {
	log.Println("Entered UAT shutdown() ...")
	close(uat_shutdown) // signal to shutdown
	log.Println("UAT shutdown(): closing device ...")
	u.dev.Close() // preempt the blocking ReadSync call
	log.Println("UAT shutdown(): calling uat_wg.Wait() ...")
	uat_wg.Wait() // Wait for the goroutine to shutdown
	log.Println("UAT shutdown(): uat_wg.Wait() returned...")
}

func (e *ES) shutdown() {
	log.Println("Entered ES shutdown() ...")
	close(es_shutdown) // signal to shutdown
	log.Println("ES shutdown(): calling es_wg.Wait() ...")
	es_wg.Wait() // Wait for the goroutine to shutdown
	log.Println("ES shutdown(): es_wg.Wait() returned...")
}

var devMap = map[int]string{0: "", 1: ""}

// Watch for config/device changes.
func sdrWatcher() {
	for {
		time.Sleep(1 * time.Second)
		count := rtl.GetDeviceCount()
		log.Println("DeviceCount...", count)

		// support two and only two dongles
		if count > 2 {
			count = 2
		}

		// cleanup if necessary
		if count < 1 || (!globalSettings.UAT_Enabled && !globalSettings.ES_Enabled) {
			log.Println("doing cleanup...")
			if UATDev != nil {
				UATDev.shutdown()
				UATDev = nil
			}
			if ESDev != nil {
				ESDev.shutdown()
				ESDev = nil
			}
			continue
		}

		if count == 1 {
			if UATDev != nil && ESDev == nil {
				UATDev.indexID = 0
			} else if UATDev == nil && ESDev != nil {
				ESDev.indexID = 0
			}
		}

		ids := []string{"", ""}
		for i := 0; i < count; i++ {
			// manufact, product, serial, err
			_, _, s, _ := rtl.GetDeviceUsbStrings(i)
			ids[i] = s
		}

		// UAT specific handling
		// When count is one, favor UAT in the case where the user
		// has enabled both UAT and ES via the web interface.
		id := 0
		if globalSettings.UAT_Enabled {
			if count == 1 {
				if ESDev != nil {
					ESDev.shutdown()
					ESDev = nil
				}
			} else { // count == 2
				if UATDev == nil && ESDev != nil {
					if ESDev.indexID == 0 {
						id = 1
					}
				}
			}

			if UATDev == nil {
				// preference check based on stratux
				// hardware serial when it exists
				serial := ids[id]
				if strings.HasPrefix(serial, "stratux:1090") {
					log.Println("Settings conflict: 978UAT set via WebUI but hardware serial says stratux:1090")
				} else {
					UATDev = &UAT{indexID: id}
					if err := UATDev.sdrConfig(); err != nil {
						UATDev = nil
					} else {
						uat_shutdown = make(chan int)
						uat_wg.Add(1)
						go UATDev.read()
					}
				}
			}
		} else if UATDev != nil {
			UATDev.shutdown()
			UATDev = nil
			if count == 1 && ESDev != nil {
				ESDev.indexID = 0
			}
		}

		// ES specific handling
		id = 0
		if globalSettings.ES_Enabled {
			if count == 1 {
				if globalSettings.UAT_Enabled {
					// defer to the UAT handler
					goto End
				}
			} else { // count == 2
				if ESDev == nil && UATDev != nil {
					if UATDev.indexID == 0 {
						id = 1
					}
				}
			}

			if ESDev == nil {
				// preference check based on stratux
				// hardware serial when it exists
				serial := ids[id]
				if strings.HasPrefix(serial, "stratux:978") {
					log.Println("Settings conflict: 1090ES set via WebUI but hardware serial says stratux:978")
				} else {
					ESDev = &ES{indexID: id}
					if err := ESDev.sdrConfig(); err != nil {
						ESDev = nil
					} else {
						es_shutdown = make(chan int)
						es_wg.Add(1)
						go ESDev.read()
					}
				}
			}
		} else if ESDev != nil {
			ESDev.shutdown()
			ESDev = nil
			if count == 1 && UATDev != nil {
				UATDev.indexID = 0
			}
		}
	End:
	}
}

func sdrInit() {
	go sdrWatcher()
	go uatReader()
	godump978.Dump978Init()
	go godump978.ProcessDataFromChannel()
}
<|endoftext|>


<commit_before>// Copyright 2017 The LUCI 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 buildbot

import (
	"bytes"
	"encoding/json"
	"fmt"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/milo/common/model"
)

type Result int

const (
	NoResult Result = iota - 1
	Success
	Warning
	Failure
	Skipped
	Exception
	Retry
	resultEnd
)

// Status converts r into a model.Status.
func (r Result) Status() model.Status {
	switch r {
	case NoResult:
		return model.Running
	case Success:
		return model.Success
	case Warning:
		return model.Warning
	case Failure:
		return model.Failure
	case Skipped:
		return model.NotRun
	case Exception:
		return model.Exception
	default:
		panic(fmt.Errorf("unknown status %d", r))
	}
}

func (r *Result) MarshalJSON() ([]byte, error) {
	var buildbotFormat *int
	if *r != NoResult {
		v := int(*r)
		buildbotFormat = &v
	}
	return json.Marshal(buildbotFormat)
}

func (r *Result) UnmarshalJSON(data []byte) error {
	var buildbotFormat *int
	if err := json.Unmarshal(data, &buildbotFormat); err != nil {
		return err
	}

	if buildbotFormat == nil {
		*r = NoResult
	} else {
		*r = Result(*buildbotFormat)
	}
	return nil
}

type StepResults struct {
	Result
	rest []interface{} // part of JSON we don't understand
}

func (r *StepResults) MarshalJSON() ([]byte, error) {
	buildbotFormat := append([]interface{}{nil}, r.rest...)
	if r.Result != NoResult {
		buildbotFormat[0] = r.Result
	}
	return json.Marshal(buildbotFormat)
}

func (r *StepResults) UnmarshalJSON(data []byte) error {
	var buildbotFormat []interface{}
	m := json.NewDecoder(bytes.NewReader(data))
	m.UseNumber()
	if err := m.Decode(&buildbotFormat); err != nil {
		return err
	}

	buf := StepResults{Result: NoResult}
	if len(buildbotFormat) > 0 {
		if buildbotFormat[0] != nil {
			n, ok := buildbotFormat[0].(json.Number)
			if !ok {
				return errors.Reason("expected a number, received %T", buildbotFormat[0]).Err()
			}
			v, err := n.Int64()
			if err != nil {
				return err
			}
			buf.Result = Result(int(v))
			if buf.Result < 0 || buf.Result >= resultEnd {
				return errors.Reason("invalid result %d", v).Err()
			}
		}
		if len(buildbotFormat) > 1 {
			buf.rest = buildbotFormat[1:]
			// otherwise keep it nil, so we can compare StepResults structs
			// in tests
		}
	}
	*r = buf
	return nil
}
<commit_msg>[milo] treat buildbot.Retry as exception<commit_after>// Copyright 2017 The LUCI 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 buildbot

import (
	"bytes"
	"encoding/json"
	"fmt"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/milo/common/model"
)

type Result int

const (
	NoResult Result = iota - 1
	Success
	Warning
	Failure
	Skipped
	Exception
	Retry
	resultEnd
)

// Status converts r into a model.Status.
func (r Result) Status() model.Status {
	switch r {
	case NoResult:
		return model.Running
	case Success:
		return model.Success
	case Warning:
		return model.Warning
	case Failure:
		return model.Failure
	case Skipped:
		return model.NotRun
	case Exception, Retry:
		return model.Exception
	default:
		panic(fmt.Errorf("unknown status %d", r))
	}
}

func (r *Result) MarshalJSON() ([]byte, error) {
	var buildbotFormat *int
	if *r != NoResult {
		v := int(*r)
		buildbotFormat = &v
	}
	return json.Marshal(buildbotFormat)
}

func (r *Result) UnmarshalJSON(data []byte) error {
	var buildbotFormat *int
	if err := json.Unmarshal(data, &buildbotFormat); err != nil {
		return err
	}

	if buildbotFormat == nil {
		*r = NoResult
	} else {
		*r = Result(*buildbotFormat)
	}
	return nil
}

type StepResults struct {
	Result
	rest []interface{} // part of JSON we don't understand
}

func (r *StepResults) MarshalJSON() ([]byte, error) {
	buildbotFormat := append([]interface{}{nil}, r.rest...)
	if r.Result != NoResult {
		buildbotFormat[0] = r.Result
	}
	return json.Marshal(buildbotFormat)
}

func (r *StepResults) UnmarshalJSON(data []byte) error {
	var buildbotFormat []interface{}
	m := json.NewDecoder(bytes.NewReader(data))
	m.UseNumber()
	if err := m.Decode(&buildbotFormat); err != nil {
		return err
	}

	buf := StepResults{Result: NoResult}
	if len(buildbotFormat) > 0 {
		if buildbotFormat[0] != nil {
			n, ok := buildbotFormat[0].(json.Number)
			if !ok {
				return errors.Reason("expected a number, received %T", buildbotFormat[0]).Err()
			}
			v, err := n.Int64()
			if err != nil {
				return err
			}
			buf.Result = Result(int(v))
			if buf.Result < 0 || buf.Result >= resultEnd {
				return errors.Reason("invalid result %d", v).Err()
			}
		}
		if len(buildbotFormat) > 1 {
			buf.rest = buildbotFormat[1:]
			// otherwise keep it nil, so we can compare StepResults structs
			// in tests
		}
	}
	*r = buf
	return nil
}
<|endoftext|>