<commit_before>package main

import (
	"flag"
	"github.com/Symantec/Dominator/lib/flagutil"
	"github.com/Symantec/Dominator/lib/fsutil"
	"github.com/Symantec/Dominator/lib/logbuf"
	"github.com/Symantec/proxima/cmd/proxima/splash"
	"github.com/Symantec/scotty/lib/apiutil"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/uuid"
	"log"
	"net/http"
	"net/rpc"
	"net/url"
	"time"
)

var (
	fConfigFile = flag.String("config", "./proxima.yaml", "config file")
	fPorts      = flagutil.StringList{"8086"}
)

func init() {
	flag.Var(&fPorts, "ports", "Comma separated list of ports")
}

func setHeader(w http.ResponseWriter, r *http.Request, key, value string) {
	r.Header.Set(key, value)
	w.Header().Set(key, value)
}

func uuidHandler(inner http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		uid := uuid.TimeUUID()
		setHeader(w, r, "Request-Id", uid.String())
		setHeader(w, r, "X-Influxdb-Version", "0.13.0")
		inner.ServeHTTP(w, r)
	})
}

type seriesListType struct {
	Series []models.Row `json:"series"`
}

type resultListType struct {
	Results []seriesListType `json:"results"`
}

func performQuery(
	executer *executerType,
	query, db, epoch string) (*resultListType, error) {
	resp, err := executer.Query(query, db, epoch)
	if err == nil {
		err = resp.Error()
	}
	if err != nil {
		return nil, err
	}
	results := &resultListType{
		Results: make([]seriesListType, len(resp.Results)),
	}
	for i := range results.Results {
		theSeries := resp.Results[i].Series
		if theSeries == nil {
			theSeries = []models.Row{}
		}
		results.Results[i] = seriesListType{
			Series: theSeries,
		}
	}
	return results, nil
}

func main() {
	tricorder.RegisterFlags()
	flag.Parse()
	rpc.HandleHTTP()
	circularBuffer := logbuf.New()
	logger := log.New(circularBuffer, "", log.LstdFlags)
	executer := newExecuter()
	changeCh := fsutil.WatchFile(*fConfigFile, logger)
	// We want to be sure we have something valid in the config file
	// initially.
	select {
	case readCloser := <-changeCh:
		if err := executer.SetupWithStream(readCloser); err != nil {
			logger.Fatal(err)
		}
		readCloser.Close()
	case <-time.After(time.Second):
		logger.Fatal("No config file")
	}
	go func() {
		for readCloser := range changeCh {
			if err := executer.SetupWithStream(readCloser); err != nil {
				logger.Println(err)
			}
			readCloser.Close()
		}
	}()
	http.Handle("/",
		&splash.Handler{
			Log: circularBuffer,
		})
	http.Handle(
		"/query",
		uuidHandler(
			apiutil.NewHandler(
				func(req url.Values) (interface{}, error) {
					resp, err := performQuery(
						executer, req.Get("q"), req.Get("db"), req.Get("epoch"))
					if err != nil {
						return nil, err
					}
					return resp, err
				},
				nil,
			),
		),
	)
	if len(fPorts) == 0 {
		log.Fatal("At least one port required.")
	}
	for _, port := range fPorts[1:] {
		go func(port string) {
			if err := http.ListenAndServe(":"+port, nil); err != nil {
				log.Fatal(err)
			}
		}(port)
	}
	if err := http.ListenAndServe(":"+fPorts[0], nil); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>config flag defaults to /etc/proxima/proxima.yaml<commit_after>package main

import (
	"flag"
	"github.com/Symantec/Dominator/lib/flagutil"
	"github.com/Symantec/Dominator/lib/fsutil"
	"github.com/Symantec/Dominator/lib/logbuf"
	"github.com/Symantec/proxima/cmd/proxima/splash"
	"github.com/Symantec/scotty/lib/apiutil"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/uuid"
	"log"
	"net/http"
	"net/rpc"
	"net/url"
	"time"
)

var (
	fConfigFile = flag.String(
		"config", "/etc/proxima/proxima.yaml", "config file")
	fPorts = flagutil.StringList{"8086"}
)

func init() {
	flag.Var(&fPorts, "ports", "Comma separated list of ports")
}

func setHeader(w http.ResponseWriter, r *http.Request, key, value string) {
	r.Header.Set(key, value)
	w.Header().Set(key, value)
}

func uuidHandler(inner http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		uid := uuid.TimeUUID()
		setHeader(w, r, "Request-Id", uid.String())
		setHeader(w, r, "X-Influxdb-Version", "0.13.0")
		inner.ServeHTTP(w, r)
	})
}

type seriesListType struct {
	Series []models.Row `json:"series"`
}

type resultListType struct {
	Results []seriesListType `json:"results"`
}

func performQuery(
	executer *executerType,
	query, db, epoch string) (*resultListType, error) {
	resp, err := executer.Query(query, db, epoch)
	if err == nil {
		err = resp.Error()
	}
	if err != nil {
		return nil, err
	}
	results := &resultListType{
		Results: make([]seriesListType, len(resp.Results)),
	}
	for i := range results.Results {
		theSeries := resp.Results[i].Series
		if theSeries == nil {
			theSeries = []models.Row{}
		}
		results.Results[i] = seriesListType{
			Series: theSeries,
		}
	}
	return results, nil
}

func main() {
	tricorder.RegisterFlags()
	flag.Parse()
	rpc.HandleHTTP()
	circularBuffer := logbuf.New()
	logger := log.New(circularBuffer, "", log.LstdFlags)
	executer := newExecuter()
	changeCh := fsutil.WatchFile(*fConfigFile, logger)
	// We want to be sure we have something valid in the config file
	// initially.
	select {
	case readCloser := <-changeCh:
		if err := executer.SetupWithStream(readCloser); err != nil {
			logger.Fatal(err)
		}
		readCloser.Close()
	case <-time.After(time.Second):
		logger.Fatal("No config file")
	}
	go func() {
		for readCloser := range changeCh {
			if err := executer.SetupWithStream(readCloser); err != nil {
				logger.Println(err)
			}
			readCloser.Close()
		}
	}()
	http.Handle("/",
		&splash.Handler{
			Log: circularBuffer,
		})
	http.Handle(
		"/query",
		uuidHandler(
			apiutil.NewHandler(
				func(req url.Values) (interface{}, error) {
					resp, err := performQuery(
						executer, req.Get("q"), req.Get("db"), req.Get("epoch"))
					if err != nil {
						return nil, err
					}
					return resp, err
				},
				nil,
			),
		),
	)
	if len(fPorts) == 0 {
		log.Fatal("At least one port required.")
	}
	for _, port := range fPorts[1:] {
		go func(port string) {
			if err := http.ListenAndServe(":"+port, nil); err != nil {
				log.Fatal(err)
			}
		}(port)
	}
	if err := http.ListenAndServe(":"+fPorts[0], nil); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/vmihailenco/msgpack.v2"
	"h12.me/errors"
	"h12.me/sej"
	"h12.me/uuid/hexid"
)

type TimestampCommand struct {
	JournalDirConfig `positional-args:"yes"  required:"yes"`
	Offset           string `
		long:"offset"
		description:"the offset"`
}

func (d *TimestampCommand) Execute(args []string) error {
	ofsFilename := path.Join(sej.OffsetDirPath(d.Dir), d.Offset) + ".ofs"
	ofsFile, err := os.Open(ofsFilename)
	if err != nil {
		return err
	}
	defer ofsFile.Close()
	offset, err := sej.ReadOffset(ofsFile)
	if err != nil {
		return err
	}

	s, err := sej.NewScanner(d.Dir, offset)
	if err != nil {
		return err
	}
	if s.Offset() != offset {
		return fmt.Errorf("fail to scan to offset %d in %s", offset, d.Dir)
	}
	fmt.Println("offset:", s.Offset())
	fmt.Println("timestamp:", s.Message().Timestamp)
	return nil
}

type DumpCommand struct {
	JournalFileConfig `positional-args:"yes"  required:"yes"`
}

type JournalFileConfig struct {
	JournalFile string
}

func (d *DumpCommand) Execute(args []string) error {
	var msg sej.Message
	for {
		file, err := os.Open(d.JournalFile)
		if err != nil {
			return errors.Wrap(err)
		}
		defer file.Close()
		if _, err := msg.ReadFrom(file); err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		fmt.Println("offset:", msg.Offset)
		fmt.Printf("message: %x (%s)\n", msg.Value, string(msg.Value))
	}
	return nil
}

type LastOffsetCommand struct {
	JournalDirConfig `positional-args:"yes"  required:"yes"`
	Offset           string `
		long:"offset"
		description:"the offset"`
}

func (c *LastOffsetCommand) Execute(args []string) error {
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return err
	}
	offset, err := dir.Last().LastReadableOffset()
	if err != nil {
		return err
	}
	fmt.Println(offset)
	return nil
}

type JournalDirConfig struct {
	Dir string
}

type TailCommand struct {
	Count int `
		long:"count"
		description:"the number of tailing messages to print"
		default:"10"`
	Format string `
		long:"format"
		default:"msgpack"
		description:"encoding format of the message"`
	JournalDirConfig `positional-args:"yes"  required:"yes"`
}

func (c *TailCommand) Execute(args []string) error {
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return err
	}
	earlist := dir.First().FirstOffset
	latest, err := dir.Last().LastOffset()
	if err != nil {
		return err
	}
	offset := int(latest) - c.Count
	if offset < int(earlist) {
		offset = int(earlist)
	}
	scanner, err := sej.NewScanner(c.Dir, uint64(offset))
	if err != nil {
		return err
	}
	cnt := 0
	for scanner.Scan() {
		switch c.Format {
		case "json", "msgpack", "bson":
			line, _ := Format(c.Format).Sprint(scanner.Message())
			fmt.Println(line)
		}
		cnt++
		if cnt >= int(c.Count) {
			break
		}
	}
	return nil
}

type Format string

func (format Format) Sprint(msg *sej.Message) (string, error) {
	value := msg.Value
	m := make(map[string]interface{})
	switch format {
	case "msgpack":
		if err := msgpack.Unmarshal(value, &m); err != nil {
			return "", err
		}
	case "bson":
		if err := bson.Unmarshal(value, &m); err != nil {
			return "", err
		}
	default:
		return string(value), nil
	}
	hexid.Restore(m)
	m = map[string]interface{}{
		"key":       string(msg.Key),
		"timestamp": msg.Timestamp,
		"type":      msg.Type,
		"value":     m,
	}
	buf, err := json.Marshal(m)
	if err != nil {
		return "", fmt.Errorf("fail to marshal %#v: %s", m, err.Error())
	}
	return string(buf), nil
}

type CleanCommand struct {
	Max int `
		long:"max"
		default:"2"
		description:"max number of journal files kept after cleanning"`
	JournalDirConfig `positional-args:"yes"  required:"yes"`
}

func (c *CleanCommand) Execute(args []string) error {
	if c.Max < 1 {
		return errors.New("max must be at least 1")
	}
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return errors.Wrap(err)
	}
	// make sure len(dir.Files) > c.Max
	if len(dir.Files) <= c.Max {
		return nil
	}

	latest, err := dir.Last().LastOffset()
	if err != nil {
		return errors.Wrap(err)
	}
	slowestReader := ""
	slowestOffset := latest
	ofsFiles, err := filepath.Glob(path.Join(sej.OffsetDirPath(c.Dir), "*.ofs"))
	if err != nil {
		return errors.Wrap(err)
	}
	for _, ofsFile := range ofsFiles {
		f, err := os.Open(ofsFile)
		if err != nil {
			return errors.Wrap(err)
		}
		offset, err := sej.ReadOffset(f)
		if err != nil {
			f.Close()
			return errors.Wrap(err)
		}
		f.Close()
		if offset < slowestOffset {
			slowestReader = ofsFile
			slowestOffset = offset
		}
	}

	for _, journalFile := range dir.Files[:len(dir.Files)-c.Max] {
		lastOffset, err := journalFile.LastOffset()
		if err != nil {
			return errors.Wrap(err)
		}
		if slowestOffset <= lastOffset {
			log.Printf("stop cleaning %s (%d-%d) because of slow reader %s\n", journalFile.FileName, journalFile.FirstOffset, lastOffset, slowestReader)
			break
		}
		if err := os.Remove(journalFile.FileName); err != nil {
			return errors.Wrap(err)
		}
		log.Printf("%s removed", journalFile.FileName)
	}
	return nil
}
<commit_msg>remove unused parameter<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/vmihailenco/msgpack.v2"
	"h12.me/errors"
	"h12.me/sej"
	"h12.me/uuid/hexid"
)

type TimestampCommand struct {
	JournalDirConfig `positional-args:"yes"  required:"yes"`
	Offset           string `
		long:"offset"
		description:"the offset"`
}

func (d *TimestampCommand) Execute(args []string) error {
	ofsFilename := path.Join(sej.OffsetDirPath(d.Dir), d.Offset) + ".ofs"
	ofsFile, err := os.Open(ofsFilename)
	if err != nil {
		return err
	}
	defer ofsFile.Close()
	offset, err := sej.ReadOffset(ofsFile)
	if err != nil {
		return err
	}

	s, err := sej.NewScanner(d.Dir, offset)
	if err != nil {
		return err
	}
	if s.Offset() != offset {
		return fmt.Errorf("fail to scan to offset %d in %s", offset, d.Dir)
	}
	fmt.Println("offset:", s.Offset())
	fmt.Println("timestamp:", s.Message().Timestamp)
	return nil
}

type DumpCommand struct {
	JournalFileConfig `positional-args:"yes"  required:"yes"`
}

type JournalFileConfig struct {
	JournalFile string
}

func (d *DumpCommand) Execute(args []string) error {
	var msg sej.Message
	for {
		file, err := os.Open(d.JournalFile)
		if err != nil {
			return errors.Wrap(err)
		}
		defer file.Close()
		if _, err := msg.ReadFrom(file); err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		fmt.Println("offset:", msg.Offset)
		fmt.Printf("message: %x (%s)\n", msg.Value, string(msg.Value))
	}
	return nil
}

type LastOffsetCommand struct {
	JournalDirConfig `positional-args:"yes"  required:"yes"`
}

func (c *LastOffsetCommand) Execute(args []string) error {
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return err
	}
	offset, err := dir.Last().LastReadableOffset()
	if err != nil {
		return err
	}
	fmt.Println(offset)
	return nil
}

type JournalDirConfig struct {
	Dir string
}

type TailCommand struct {
	Count int `
		long:"count"
		description:"the number of tailing messages to print"
		default:"10"`
	Format string `
		long:"format"
		default:"msgpack"
		description:"encoding format of the message"`
	JournalDirConfig `positional-args:"yes"  required:"yes"`
}

func (c *TailCommand) Execute(args []string) error {
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return err
	}
	earlist := dir.First().FirstOffset
	latest, err := dir.Last().LastOffset()
	if err != nil {
		return err
	}
	offset := int(latest) - c.Count
	if offset < int(earlist) {
		offset = int(earlist)
	}
	scanner, err := sej.NewScanner(c.Dir, uint64(offset))
	if err != nil {
		return err
	}
	cnt := 0
	for scanner.Scan() {
		switch c.Format {
		case "json", "msgpack", "bson":
			line, _ := Format(c.Format).Sprint(scanner.Message())
			fmt.Println(line)
		}
		cnt++
		if cnt >= int(c.Count) {
			break
		}
	}
	return nil
}

type Format string

func (format Format) Sprint(msg *sej.Message) (string, error) {
	value := msg.Value
	m := make(map[string]interface{})
	switch format {
	case "msgpack":
		if err := msgpack.Unmarshal(value, &m); err != nil {
			return "", err
		}
	case "bson":
		if err := bson.Unmarshal(value, &m); err != nil {
			return "", err
		}
	default:
		return string(value), nil
	}
	hexid.Restore(m)
	m = map[string]interface{}{
		"key":       string(msg.Key),
		"timestamp": msg.Timestamp,
		"type":      msg.Type,
		"value":     m,
	}
	buf, err := json.Marshal(m)
	if err != nil {
		return "", fmt.Errorf("fail to marshal %#v: %s", m, err.Error())
	}
	return string(buf), nil
}

type CleanCommand struct {
	Max int `
		long:"max"
		default:"2"
		description:"max number of journal files kept after cleanning"`
	JournalDirConfig `positional-args:"yes"  required:"yes"`
}

func (c *CleanCommand) Execute(args []string) error {
	if c.Max < 1 {
		return errors.New("max must be at least 1")
	}
	dir, err := sej.OpenJournalDir(sej.JournalDirPath(c.Dir))
	if err != nil {
		return errors.Wrap(err)
	}
	// make sure len(dir.Files) > c.Max
	if len(dir.Files) <= c.Max {
		return nil
	}

	latest, err := dir.Last().LastOffset()
	if err != nil {
		return errors.Wrap(err)
	}
	slowestReader := ""
	slowestOffset := latest
	ofsFiles, err := filepath.Glob(path.Join(sej.OffsetDirPath(c.Dir), "*.ofs"))
	if err != nil {
		return errors.Wrap(err)
	}
	for _, ofsFile := range ofsFiles {
		f, err := os.Open(ofsFile)
		if err != nil {
			return errors.Wrap(err)
		}
		offset, err := sej.ReadOffset(f)
		if err != nil {
			f.Close()
			return errors.Wrap(err)
		}
		f.Close()
		if offset < slowestOffset {
			slowestReader = ofsFile
			slowestOffset = offset
		}
	}

	for _, journalFile := range dir.Files[:len(dir.Files)-c.Max] {
		lastOffset, err := journalFile.LastOffset()
		if err != nil {
			return errors.Wrap(err)
		}
		if slowestOffset <= lastOffset {
			log.Printf("stop cleaning %s (%d-%d) because of slow reader %s\n", journalFile.FileName, journalFile.FirstOffset, lastOffset, slowestReader)
			break
		}
		if err := os.Remove(journalFile.FileName); err != nil {
			return errors.Wrap(err)
		}
		log.Printf("%s removed", journalFile.FileName)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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

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

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

package main

import (
	"flag"
	"os"
	"sort"
	"time"

	"github.com/codegangsta/cli"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
)

var (
	gitversion string
	pClient    *client.Client
	timeFormat = time.RFC1123
)

func main() {
	app := cli.NewApp()
	app.Name = "snapctl"
	app.Version = gitversion
	app.Usage = "A powerful telemetry framework"
	app.Flags = []cli.Flag{flURL, flSecure, flAPIVer}
	app.Commands = commands
	sort.Sort(ByCommand(app.Commands))
	app.Run(os.Args)
}

func init() {
	f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
	prtURL := f1.String("url", flURL.Value, flURL.Usage)
	prtU := f1.String("u", flURL.Value, flURL.Usage)
	prtAv := f1.String("api-version", flAPIVer.Value, flAPIVer.Usage)
	prtA := f1.String("a", flAPIVer.Value, flAPIVer.Usage)
	prti := f1.Bool("insecure", false, flSecure.Usage)

	url := flURL.Value
	ver := flAPIVer.Value
	secure := false

	for idx, a := range os.Args {
		switch a {
		case "--url":
			if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
				url = *prtURL
			}
		case "-u":
			if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
				url = *prtU
			}
		case "--api-version":
			if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
				ver = *prtAv
			}
		case "-a":
			if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
				ver = *prtA
			}
		case "--insecure":
			if err := f1.Parse(os.Args[idx:]); err == nil {
				secure = *prti
			}
		}
	}
	pClient = client.New(url, ver, secure)
	resp := pClient.ListAgreements()
	if resp.Err == nil {
		commands = append(commands, tribeCommands...)
	}

}
<commit_msg>Added checking of arg length to avoid panics when parsing in snapctl.<commit_after>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

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

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

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

package main

import (
	"flag"
	"os"
	"sort"
	"time"

	"github.com/codegangsta/cli"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
)

var (
	gitversion string
	pClient    *client.Client
	timeFormat = time.RFC1123
)

func main() {
	app := cli.NewApp()
	app.Name = "snapctl"
	app.Version = gitversion
	app.Usage = "A powerful telemetry framework"
	app.Flags = []cli.Flag{flURL, flSecure, flAPIVer}
	app.Commands = commands
	sort.Sort(ByCommand(app.Commands))
	app.Run(os.Args)
}

func init() {
	f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
	prtURL := f1.String("url", flURL.Value, flURL.Usage)
	prtU := f1.String("u", flURL.Value, flURL.Usage)
	prtAv := f1.String("api-version", flAPIVer.Value, flAPIVer.Usage)
	prtA := f1.String("a", flAPIVer.Value, flAPIVer.Usage)
	prti := f1.Bool("insecure", false, flSecure.Usage)

	url := flURL.Value
	ver := flAPIVer.Value
	secure := false

	for idx, a := range os.Args {
		switch a {
		case "--url":
			if len(os.Args) >= idx+2 {
				if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
					url = *prtURL
				}
			}
		case "-u":
			if len(os.Args) >= idx+2 {
				if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
					url = *prtU
				}
			}
		case "--api-version":
			if len(os.Args) >= idx+2 {
				if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
					ver = *prtAv
				}
			}
		case "-a":
			if len(os.Args) >= idx+2 {
				if err := f1.Parse(os.Args[idx : idx+2]); err == nil {
					ver = *prtA
				}
			}
		case "--insecure":
			if err := f1.Parse([]string{os.Args[idx]}); err == nil {
				secure = *prti
			}
		}
	}
	pClient = client.New(url, ver, secure)
	resp := pClient.ListAgreements()
	if resp.Err == nil {
		commands = append(commands, tribeCommands...)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	fglog "github.com/forestgiant/log"
	"github.com/forestgiant/semver"

	"golang.org/x/net/context"

	"github.com/forestgiant/netutil"
	"gitlab.fg/go/stela"
	"gitlab.fg/go/stela/api"
)

func main() {
	logger := fglog.Logger{}.With("time", fglog.DefaultTimestamp, "caller", fglog.DefaultCaller, "cli", "stelawd")
	err := semver.SetVersion(stela.Version)
	if err != nil {
		logger.Error("error", err.Error())
		os.Exit(1)
	}

	// Check for command line configuration flags
	var (
		watcherListUsage  = "Path to the list of services to watch."
		watcherListPtr    = flag.String("watchlist", "watch.list", watcherListUsage)
		caFileUsage       = "Path to the ca file for gRPC client to connect with."
		caFilePtr         = flag.String("cafile", "../certs/ca.pem", caFileUsage)
		stelaAddressUsage = "Address of stela instance to connect to."
		stelaAddress      = flag.String("stelaAddr", stela.DefaultStelaAddress, stelaAddressUsage)
	)
	flag.Parse()

	ctx, cancelFunc := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancelFunc()
	stelaClient, err := api.NewClient(ctx, *stelaAddress, *caFilePtr)
	if err != nil {
		logger.Error("Failed to create stela client. Make sure there is a stela instance running", "error", err.Error())
		os.Exit(1)
	}
	defer stelaClient.Close()

	// Read service names and ip address/ports from config
	config, err := openConfig(*watcherListPtr)
	if err != nil {
		logger.Error("Failed to open config:", "error", err.Error())
		os.Exit(1)
	}
	watchers, err := createWatchers(stelaClient, config)
	if err != nil {
		logger.Error("Failed to create watchers:", "error", err.Error())
		os.Exit(1)
	}

	// Print status for each service being watched
	for _, w := range watchers {
		if err := w.watch(); err != nil {
			logger.Error("Failed to watch:", "error", err.Error(), "watcher:", w)
			os.Exit(1)
		}
		fmt.Printf("Registering service: %s \n", w)
	}

	// Listen for shutdown signal
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	select {
	case <-sigs:
		// stop all watchers
		for _, w := range watchers {
			w.stop()
		}
		fmt.Println("Stopping watch dog")
		return
	}

}

func openConfig(filePath string) (io.Reader, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}

	return bufio.NewReader(f), nil
}

func createWatchers(stelaClient *api.Client, config io.Reader) ([]*watcher, error) {
	r := csv.NewReader(config)
	r.Comment = '#'
	var watchers []*watcher

	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}

		if len(record) < 4 {
			return nil, fmt.Errorf("Could not process record: %v. Not enough fields.", record)
		}

		// Verify serviceName
		if strings.TrimSpace(record[0]) == "" {
			return nil, fmt.Errorf("Could not process service name for: %v", record)
		}
		serviceName := strings.TrimSpace(record[0])

		// Create and verify address/port
		ip, p, err := net.SplitHostPort(strings.TrimSpace(record[1]))
		if err != nil {
			return nil, fmt.Errorf("Could not process address %v for: %v", record[1], record)
		}

		// If the ip is blank use the localhost ipv4 address
		if ip == "" {
			ip = netutil.LocalIPv4().String()
		}

		port, err := strconv.Atoi(p)
		if err != nil {
			return nil, fmt.Errorf("Could not process port: %v for: %v", p, record)
		}

		// Create and verify value
		// Value can only be a string for a stelawd config
		if strings.TrimSpace(record[2]) == "" {
			return nil, fmt.Errorf("Could not process value for: %v", record)
		}
		value := strings.TrimSpace(record[2])

		// Create and verify interval
		interval, err := strconv.Atoi(strings.TrimSpace(record[3]))
		if err != nil {
			return nil, fmt.Errorf("Could not process interval %v for: %v", record[3], record)
		}

		w := &watcher{
			service: &stela.Service{
				Name:  serviceName,
				IPv4:  ip,
				Port:  int32(port),
				Value: stela.EncodeValue(value),
			},
			interval:    time.Millisecond * time.Duration(interval),
			stelaClient: stelaClient,
		}

		if !w.valid() {
			return nil, fmt.Errorf("Invalid inputs. Could not process record: %v", record)
		}
		watchers = append(watchers, w)
	}

	return watchers, nil
}
<commit_msg>Updating stelawd fglog usage<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	fglog "github.com/forestgiant/log"
	"github.com/forestgiant/semver"

	"golang.org/x/net/context"

	"github.com/forestgiant/netutil"
	"gitlab.fg/go/stela"
	"gitlab.fg/go/stela/api"
)

func main() {
	logger := fglog.Logger{}.With("time", fglog.DefaultTimestamp, "caller", fglog.DefaultCaller, "cli", "stelawd")
	err := semver.SetVersion(stela.Version)
	if err != nil {
		logger.Error("Unable to set semantic version.", "error", err.Error())
		os.Exit(1)
	}

	// Check for command line configuration flags
	var (
		watcherListUsage  = "Path to the list of services to watch."
		watcherListPtr    = flag.String("watchlist", "watch.list", watcherListUsage)
		caFileUsage       = "Path to the ca file for gRPC client to connect with."
		caFilePtr         = flag.String("cafile", "../certs/ca.pem", caFileUsage)
		stelaAddressUsage = "Address of stela instance to connect to."
		stelaAddress      = flag.String("stelaAddr", stela.DefaultStelaAddress, stelaAddressUsage)
	)
	flag.Parse()

	ctx, cancelFunc := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancelFunc()
	stelaClient, err := api.NewClient(ctx, *stelaAddress, *caFilePtr)
	if err != nil {
		logger.Error("Failed to create stela client. Make sure there is a stela instance running", "error", err.Error())
		os.Exit(1)
	}
	defer stelaClient.Close()

	// Read service names and ip address/ports from config
	config, err := openConfig(*watcherListPtr)
	if err != nil {
		logger.Error("Failed to open config:", "error", err.Error())
		os.Exit(1)
	}
	watchers, err := createWatchers(stelaClient, config)
	if err != nil {
		logger.Error("Failed to create watchers:", "error", err.Error())
		os.Exit(1)
	}

	// Print status for each service being watched
	for _, w := range watchers {
		if err := w.watch(); err != nil {
			logger.Error("Failed to watch:", "error", err.Error(), "watcher:", w)
			os.Exit(1)
		}
		fmt.Printf("Registering service: %s \n", w)
	}

	// Listen for shutdown signal
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	select {
	case <-sigs:
		// stop all watchers
		for _, w := range watchers {
			w.stop()
		}
		fmt.Println("Stopping watch dog")
		return
	}

}

func openConfig(filePath string) (io.Reader, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}

	return bufio.NewReader(f), nil
}

func createWatchers(stelaClient *api.Client, config io.Reader) ([]*watcher, error) {
	r := csv.NewReader(config)
	r.Comment = '#'
	var watchers []*watcher

	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}

		if len(record) < 4 {
			return nil, fmt.Errorf("Could not process record: %v. Not enough fields.", record)
		}

		// Verify serviceName
		if strings.TrimSpace(record[0]) == "" {
			return nil, fmt.Errorf("Could not process service name for: %v", record)
		}
		serviceName := strings.TrimSpace(record[0])

		// Create and verify address/port
		ip, p, err := net.SplitHostPort(strings.TrimSpace(record[1]))
		if err != nil {
			return nil, fmt.Errorf("Could not process address %v for: %v", record[1], record)
		}

		// If the ip is blank use the localhost ipv4 address
		if ip == "" {
			ip = netutil.LocalIPv4().String()
		}

		port, err := strconv.Atoi(p)
		if err != nil {
			return nil, fmt.Errorf("Could not process port: %v for: %v", p, record)
		}

		// Create and verify value
		// Value can only be a string for a stelawd config
		if strings.TrimSpace(record[2]) == "" {
			return nil, fmt.Errorf("Could not process value for: %v", record)
		}
		value := strings.TrimSpace(record[2])

		// Create and verify interval
		interval, err := strconv.Atoi(strings.TrimSpace(record[3]))
		if err != nil {
			return nil, fmt.Errorf("Could not process interval %v for: %v", record[3], record)
		}

		w := &watcher{
			service: &stela.Service{
				Name:  serviceName,
				IPv4:  ip,
				Port:  int32(port),
				Value: stela.EncodeValue(value),
			},
			interval:    time.Millisecond * time.Duration(interval),
			stelaClient: stelaClient,
		}

		if !w.valid() {
			return nil, fmt.Errorf("Invalid inputs. Could not process record: %v", record)
		}
		watchers = append(watchers, w)
	}

	return watchers, nil
}
<|endoftext|>


<commit_before>package beegopro

import (
	"fmt"
	"github.com/beego/bee/internal/pkg/git"
	"github.com/beego/bee/internal/pkg/system"
	beeLogger "github.com/beego/bee/logger"
	"github.com/beego/bee/utils"
	"github.com/davecgh/go-spew/spew"
	"github.com/pelletier/go-toml"
	"github.com/spf13/viper"
	"io/ioutil"
	"sync"
	"time"
)

const MDateFormat = "20060102_150405"

var DefaultBeegoPro = &Container{
	BeegoProFile:  system.CurrentDir + "/beegopro.toml",
	TimestampFile: system.CurrentDir + "/.beegopro.timestamp",
	GoModFile:     system.CurrentDir + "/go.mod",
	UserOption: UserOption{
		Debug:          false,
		ContextDebug:   false,
		Dsn:            "",
		Driver:         "mysql",
		ProType:        "default",
		ApiPrefix:      "/api",
		EnableModule:   nil,
		Models:         make(map[string]TextModel, 0),
		GitRemotePath:  "https://github.com/beego-dev/beego-pro.git",
		Branch:         "master",
		GitLocalPath:   system.BeegoHome + "/beego-pro",
		EnableFormat:   true,
		SourceGen:      "text",
		EnableGitPull:  true,
		RefreshGitTime: 24 * 3600,
		Path: map[string]string{
			"beego": ".",
		},
		EnableGomod: true,
	},
	GenerateTime:     time.Now().Format(MDateFormat),
	GenerateTimeUnix: time.Now().Unix(),
	TmplOption:       TmplOption{},
	CurPath:          system.CurrentDir,
	EnableModules:    make(map[string]interface{}, 0), // get the user configuration, get the enable module result
	FunctionOnce:     make(map[string]sync.Once, 0),   // get the tmpl configuration, get the function once result
}

func (c *Container) Run() {
	// init git refresh cache time
	c.initTimestamp()
	c.initUserOption()
	c.initTemplateOption()
	c.initParser()
	c.initRender()
	c.flushTimestamp()
}

func (c *Container) initUserOption() {
	if !utils.IsExist(c.BeegoProFile) {
		beeLogger.Log.Fatalf("beego pro config is not exist, beego json path: %s", c.BeegoProFile)
		return
	}
	viper.SetConfigFile(c.BeegoProFile)
	err := viper.ReadInConfig()
	if err != nil {
		beeLogger.Log.Fatalf("read beego pro config content, err: %s", err.Error())
		return
	}

	err = viper.Unmarshal(&c.UserOption)
	if err != nil {
		beeLogger.Log.Fatalf("beego pro config unmarshal error, err: %s", err.Error())
		return
	}
	if c.UserOption.Debug {
		viper.Debug()
	}

	if c.UserOption.EnableGomod {
		if !utils.IsExist(c.GoModFile) {
			beeLogger.Log.Fatalf("go mod not exist, please create go mod file")
			return
		}
	}

	for _, value := range c.UserOption.EnableModule {
		c.EnableModules[value] = struct{}{}
	}

	if len(c.EnableModules) == 0 {
		c.EnableModules["*"] = struct{}{}
	}

	if c.UserOption.Debug {
		fmt.Println("c.modules", c.EnableModules)
	}

}

func (c *Container) initTemplateOption() {
	if c.UserOption.EnableGitPull && (c.GenerateTimeUnix-c.Timestamp.GitCacheLastRefresh > c.UserOption.RefreshGitTime) {
		err := git.CloneORPullRepo(c.UserOption.GitRemotePath, c.UserOption.GitLocalPath)
		if err != nil {
			beeLogger.Log.Fatalf("beego pro git clone or pull repo error, err: %s", err)
			return
		}
		c.Timestamp.GitCacheLastRefresh = c.GenerateTimeUnix
	}

	tree, err := toml.LoadFile(c.UserOption.GitLocalPath + "/" + c.UserOption.ProType + "/bee.toml")

	if err != nil {
		beeLogger.Log.Fatalf("beego tmpl exec error, err: %s", err)
		return
	}
	err = tree.Unmarshal(&c.TmplOption)
	if err != nil {
		beeLogger.Log.Fatalf("beego tmpl parse error, err: %s", err)
		return
	}

	if c.UserOption.Debug {
		spew.Dump("tmpl", c.TmplOption)
	}

	for _, value := range c.TmplOption.Descriptor {
		if value.Once == true {
			c.FunctionOnce[value.SrcName] = sync.Once{}
		}
	}
}

func (c *Container) initParser() {
	driver, flag := ParserDriver[c.UserOption.SourceGen]
	if !flag {
		beeLogger.Log.Fatalf("parse driver not exit, source gen %s", c.UserOption.SourceGen)
	}
	driver.RegisterOption(c.UserOption, c.TmplOption)
	c.Parser = driver
}

func (c *Container) initRender() {
	for _, desc := range c.TmplOption.Descriptor {
		_, allFlag := c.EnableModules["*"]
		_, moduleFlag := c.EnableModules[desc.Module]
		if !allFlag && !moduleFlag {
			continue
		}

		models := c.Parser.GetRenderInfos(desc)
		// model table name, model table schema
		for _, m := range models {
			// some render exec once
			syncOnce, flag := c.FunctionOnce[desc.SrcName]
			if flag {
				syncOnce.Do(func() {
					c.renderModel(m)
				})
				continue
			}
			c.renderModel(m)
		}
	}
}

func (c *Container) renderModel(m RenderInfo) {
	// todo optimize
	m.GenerateTime = c.GenerateTime
	render := NewRender(m)
	render.Exec(m.Descriptor.SrcName)
	if render.Descriptor.IsExistScript() {
		err := render.Descriptor.ExecScript(c.CurPath)
		if err != nil {
			beeLogger.Log.Fatalf("beego exec shell error, err: %s", err)
		}
	}
}

func (c *Container) initTimestamp() {
	if utils.IsExist(c.TimestampFile) {
		tree, err := toml.LoadFile(c.TimestampFile)
		if err != nil {
			beeLogger.Log.Fatalf("beego timestamp tmpl exec error, err: %s", err)
			return
		}
		err = tree.Unmarshal(&c.Timestamp)
		if err != nil {
			beeLogger.Log.Fatalf("beego timestamp tmpl parse error, err: %s", err)
			return
		}
	}
	c.Timestamp.Generate = c.GenerateTimeUnix
}

func (c *Container) flushTimestamp() {
	tomlByte, err := toml.Marshal(c.Timestamp)
	if err != nil {
		beeLogger.Log.Fatalf("marshal timestamp tmpl parse error, err: %s", err)
	}
	err = ioutil.WriteFile(c.TimestampFile, tomlByte, 0644)
	if err != nil {
		beeLogger.Log.Fatalf("flush timestamp tmpl parse error, err: %s", err)
	}
}
<commit_msg>beego pro move to beego group<commit_after>package beegopro

import (
	"fmt"
	"github.com/beego/bee/internal/pkg/git"
	"github.com/beego/bee/internal/pkg/system"
	beeLogger "github.com/beego/bee/logger"
	"github.com/beego/bee/utils"
	"github.com/davecgh/go-spew/spew"
	"github.com/pelletier/go-toml"
	"github.com/spf13/viper"
	"io/ioutil"
	"sync"
	"time"
)

const MDateFormat = "20060102_150405"

var DefaultBeegoPro = &Container{
	BeegoProFile:  system.CurrentDir + "/beegopro.toml",
	TimestampFile: system.CurrentDir + "/.beegopro.timestamp",
	GoModFile:     system.CurrentDir + "/go.mod",
	UserOption: UserOption{
		Debug:          false,
		ContextDebug:   false,
		Dsn:            "",
		Driver:         "mysql",
		ProType:        "default",
		ApiPrefix:      "/api",
		EnableModule:   nil,
		Models:         make(map[string]TextModel, 0),
		GitRemotePath:  "https://github.com/beego/beego-pro.git",
		Branch:         "master",
		GitLocalPath:   system.BeegoHome + "/beego-pro",
		EnableFormat:   true,
		SourceGen:      "text",
		EnableGitPull:  true,
		RefreshGitTime: 24 * 3600,
		Path: map[string]string{
			"beego": ".",
		},
		EnableGomod: true,
	},
	GenerateTime:     time.Now().Format(MDateFormat),
	GenerateTimeUnix: time.Now().Unix(),
	TmplOption:       TmplOption{},
	CurPath:          system.CurrentDir,
	EnableModules:    make(map[string]interface{}, 0), // get the user configuration, get the enable module result
	FunctionOnce:     make(map[string]sync.Once, 0),   // get the tmpl configuration, get the function once result
}

func (c *Container) Run() {
	// init git refresh cache time
	c.initTimestamp()
	c.initUserOption()
	c.initTemplateOption()
	c.initParser()
	c.initRender()
	c.flushTimestamp()
}

func (c *Container) initUserOption() {
	if !utils.IsExist(c.BeegoProFile) {
		beeLogger.Log.Fatalf("beego pro config is not exist, beego json path: %s", c.BeegoProFile)
		return
	}
	viper.SetConfigFile(c.BeegoProFile)
	err := viper.ReadInConfig()
	if err != nil {
		beeLogger.Log.Fatalf("read beego pro config content, err: %s", err.Error())
		return
	}

	err = viper.Unmarshal(&c.UserOption)
	if err != nil {
		beeLogger.Log.Fatalf("beego pro config unmarshal error, err: %s", err.Error())
		return
	}
	if c.UserOption.Debug {
		viper.Debug()
	}

	if c.UserOption.EnableGomod {
		if !utils.IsExist(c.GoModFile) {
			beeLogger.Log.Fatalf("go mod not exist, please create go mod file")
			return
		}
	}

	for _, value := range c.UserOption.EnableModule {
		c.EnableModules[value] = struct{}{}
	}

	if len(c.EnableModules) == 0 {
		c.EnableModules["*"] = struct{}{}
	}

	if c.UserOption.Debug {
		fmt.Println("c.modules", c.EnableModules)
	}

}

func (c *Container) initTemplateOption() {
	if c.UserOption.EnableGitPull && (c.GenerateTimeUnix-c.Timestamp.GitCacheLastRefresh > c.UserOption.RefreshGitTime) {
		err := git.CloneORPullRepo(c.UserOption.GitRemotePath, c.UserOption.GitLocalPath)
		if err != nil {
			beeLogger.Log.Fatalf("beego pro git clone or pull repo error, err: %s", err)
			return
		}
		c.Timestamp.GitCacheLastRefresh = c.GenerateTimeUnix
	}

	tree, err := toml.LoadFile(c.UserOption.GitLocalPath + "/" + c.UserOption.ProType + "/bee.toml")

	if err != nil {
		beeLogger.Log.Fatalf("beego tmpl exec error, err: %s", err)
		return
	}
	err = tree.Unmarshal(&c.TmplOption)
	if err != nil {
		beeLogger.Log.Fatalf("beego tmpl parse error, err: %s", err)
		return
	}

	if c.UserOption.Debug {
		spew.Dump("tmpl", c.TmplOption)
	}

	for _, value := range c.TmplOption.Descriptor {
		if value.Once == true {
			c.FunctionOnce[value.SrcName] = sync.Once{}
		}
	}
}

func (c *Container) initParser() {
	driver, flag := ParserDriver[c.UserOption.SourceGen]
	if !flag {
		beeLogger.Log.Fatalf("parse driver not exit, source gen %s", c.UserOption.SourceGen)
	}
	driver.RegisterOption(c.UserOption, c.TmplOption)
	c.Parser = driver
}

func (c *Container) initRender() {
	for _, desc := range c.TmplOption.Descriptor {
		_, allFlag := c.EnableModules["*"]
		_, moduleFlag := c.EnableModules[desc.Module]
		if !allFlag && !moduleFlag {
			continue
		}

		models := c.Parser.GetRenderInfos(desc)
		// model table name, model table schema
		for _, m := range models {
			// some render exec once
			syncOnce, flag := c.FunctionOnce[desc.SrcName]
			if flag {
				syncOnce.Do(func() {
					c.renderModel(m)
				})
				continue
			}
			c.renderModel(m)
		}
	}
}

func (c *Container) renderModel(m RenderInfo) {
	// todo optimize
	m.GenerateTime = c.GenerateTime
	render := NewRender(m)
	render.Exec(m.Descriptor.SrcName)
	if render.Descriptor.IsExistScript() {
		err := render.Descriptor.ExecScript(c.CurPath)
		if err != nil {
			beeLogger.Log.Fatalf("beego exec shell error, err: %s", err)
		}
	}
}

func (c *Container) initTimestamp() {
	if utils.IsExist(c.TimestampFile) {
		tree, err := toml.LoadFile(c.TimestampFile)
		if err != nil {
			beeLogger.Log.Fatalf("beego timestamp tmpl exec error, err: %s", err)
			return
		}
		err = tree.Unmarshal(&c.Timestamp)
		if err != nil {
			beeLogger.Log.Fatalf("beego timestamp tmpl parse error, err: %s", err)
			return
		}
	}
	c.Timestamp.Generate = c.GenerateTimeUnix
}

func (c *Container) flushTimestamp() {
	tomlByte, err := toml.Marshal(c.Timestamp)
	if err != nil {
		beeLogger.Log.Fatalf("marshal timestamp tmpl parse error, err: %s", err)
	}
	err = ioutil.WriteFile(c.TimestampFile, tomlByte, 0644)
	if err != nil {
		beeLogger.Log.Fatalf("flush timestamp tmpl parse error, err: %s", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 opengl

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/web"
)

// arrayBufferLayoutPart is a part of an array buffer layout.
type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name string
	num  int
}

// arrayBufferLayout is an array buffer layout.
//
// An array buffer in OpenGL is a buffer representing vertices and
// is passed to a vertex shader.
type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) names() []string {
	ns := make([]string, len(a.parts))
	for i, p := range a.parts {
		ns[i] = p.name
	}
	return ns
}

// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += float.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

// newArrayBuffer creates OpenGL's buffer object for the array buffer.
func (a *arrayBufferLayout) newArrayBuffer(context *context) buffer {
	return context.newArrayBuffer(a.totalBytes() * graphics.IndicesNum)
}

// enable binds the array buffer the given program to use the array buffer.
func (a *arrayBufferLayout) enable(context *context, program program) {
	for i := range a.parts {
		context.enableVertexAttribArray(program, i)
	}
	total := a.totalBytes()
	offset := 0
	for i, p := range a.parts {
		context.vertexAttribPointer(program, i, p.num, float, total, offset)
		offset += float.SizeInBytes() * p.num
	}
}

// disable stops using the array buffer.
func (a *arrayBufferLayout) disable(context *context, program program) {
	// TODO: Disabling should be done in reversed order?
	for i := range a.parts {
		context.disableVertexAttribArray(program, i)
	}
}

// theArrayBufferLayout is the array buffer layout for Ebiten.
var theArrayBufferLayout = arrayBufferLayout{
	// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
	parts: []arrayBufferLayoutPart{
		{
			name: "vertex",
			num:  2,
		},
		{
			name: "tex",
			num:  2,
		},
		{
			name: "tex_region",
			num:  4,
		},
		{
			name: "color_scale",
			num:  4,
		},
	},
}

func init() {
	vertexFloatNum := theArrayBufferLayout.totalBytes() / float.SizeInBytes()
	if graphics.VertexFloatNum != vertexFloatNum {
		panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatNum, vertexFloatNum))
	}
}

type programKey struct {
	useColorM bool
	filter    driver.Filter
	address   driver.Address
}

// openGLState is a state for
type openGLState struct {
	// arrayBuffer is OpenGL's array buffer (vertices data).
	arrayBuffer buffer

	// elementArrayBuffer is OpenGL's element array buffer (indices data).
	elementArrayBuffer buffer

	// programs is OpenGL's program for rendering a texture.
	programs map[programKey]program

	lastProgram                program
	lastViewportWidth          int
	lastViewportHeight         int
	lastColorMatrix            []float32
	lastColorMatrixTranslation []float32
	lastSourceWidth            int
	lastSourceHeight           int

	source      *Image
	destination *Image
}

var (
	zeroBuffer  buffer
	zeroProgram program
)

// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
	if err := context.reset(); err != nil {
		return err
	}

	s.lastProgram = zeroProgram
	s.lastViewportWidth = 0
	s.lastViewportHeight = 0
	s.lastColorMatrix = nil
	s.lastColorMatrixTranslation = nil
	s.lastSourceWidth = 0
	s.lastSourceHeight = 0

	// When context lost happens, deleting programs or buffers is not necessary.
	// However, it is not assumed that reset is called only when context lost happens.
	// Let's delete them explicitly.
	if s.programs == nil {
		s.programs = map[programKey]program{}
	} else {
		for k, p := range s.programs {
			context.deleteProgram(p)
			delete(s.programs, k)
		}
	}

	// On browsers (at least Chrome), buffers are already detached from the context
	// and must not be deleted by DeleteBuffer.
	if !web.IsBrowser() {
		if !s.arrayBuffer.equal(zeroBuffer) {
			context.deleteBuffer(s.arrayBuffer)
		}
		if !s.elementArrayBuffer.equal(zeroBuffer) {
			context.deleteBuffer(s.elementArrayBuffer)
		}
	}

	shaderVertexModelviewNative, err := context.newShader(vertexShader, vertexShaderStr())
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.deleteShader(shaderVertexModelviewNative)

	for _, c := range []bool{false, true} {
		for _, a := range []driver.Address{
			driver.AddressClampToZero,
			driver.AddressRepeat,
		} {
			for _, f := range []driver.Filter{
				driver.FilterNearest,
				driver.FilterLinear,
				driver.FilterScreen,
			} {
				shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, fragmentShaderStr(c, f, a))
				if err != nil {
					panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
				}
				defer context.deleteShader(shaderFragmentColorMatrixNative)

				program, err := context.newProgram([]shader{
					shaderVertexModelviewNative,
					shaderFragmentColorMatrixNative,
				}, theArrayBufferLayout.names())

				if err != nil {
					return err
				}

				s.programs[programKey{
					useColorM: c,
					filter:    f,
					address:   a,
				}] = program
			}
		}
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	// Note that the indices passed to NewElementArrayBuffer is not under GC management
	// in opengl package due to unsafe-way.
	// See NewElementArrayBuffer in context_mobile.go.
	s.elementArrayBuffer = context.newElementArrayBuffer(graphics.IndicesNum * 2)

	return nil
}

// areSameFloat32Array returns a boolean indicating if a and b are deeply equal.
func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// useProgram uses the program (programTexture).
func (g *Graphics) useProgram(mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
	destination := g.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := g.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	g.context.blendFunc(mode)

	program := g.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if !g.state.lastProgram.equal(program) {
		g.context.useProgram(program)
		if g.state.lastProgram.equal(zeroProgram) {
			theArrayBufferLayout.enable(&g.context, program)
			g.context.bindBuffer(arrayBuffer, g.state.arrayBuffer)
			g.context.bindBuffer(elementArrayBuffer, g.state.elementArrayBuffer)
			g.context.uniformInt(program, "texture", 0)
		}

		g.state.lastProgram = program
		g.state.lastViewportWidth = 0
		g.state.lastViewportHeight = 0
		g.state.lastColorMatrix = nil
		g.state.lastColorMatrixTranslation = nil
		g.state.lastSourceWidth = 0
		g.state.lastSourceHeight = 0
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	if g.state.lastViewportWidth != vw || g.state.lastViewportHeight != vh {
		g.context.uniformFloats(program, "viewport_size", []float32{float32(vw), float32(vh)})
		g.state.lastViewportWidth = vw
		g.state.lastViewportHeight = vh
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(g.state.lastColorMatrix, esBody) {
			g.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			g.state.lastColorMatrix = esBody
		}
		if !areSameFloat32Array(g.state.lastColorMatrixTranslation, esTranslate) {
			g.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			g.state.lastColorMatrixTranslation = esTranslate
		}
	}

	if filter != driver.FilterNearest {
		sw := graphics.InternalImageSize(srcW)
		sh := graphics.InternalImageSize(srcH)
		if g.state.lastSourceWidth != sw || g.state.lastSourceHeight != sh {
			g.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
			g.state.lastSourceWidth = sw
			g.state.lastSourceHeight = sh
		}
	}

	if filter == driver.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		g.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	g.context.bindTexture(source.textureNative)

	g.state.source = nil
	g.state.destination = nil
	return nil
}
<commit_msg>graphicsdriver/opengl: Integrate last uniform values<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 opengl

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/web"
)

// arrayBufferLayoutPart is a part of an array buffer layout.
type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name string
	num  int
}

// arrayBufferLayout is an array buffer layout.
//
// An array buffer in OpenGL is a buffer representing vertices and
// is passed to a vertex shader.
type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) names() []string {
	ns := make([]string, len(a.parts))
	for i, p := range a.parts {
		ns[i] = p.name
	}
	return ns
}

// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += float.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

// newArrayBuffer creates OpenGL's buffer object for the array buffer.
func (a *arrayBufferLayout) newArrayBuffer(context *context) buffer {
	return context.newArrayBuffer(a.totalBytes() * graphics.IndicesNum)
}

// enable binds the array buffer the given program to use the array buffer.
func (a *arrayBufferLayout) enable(context *context, program program) {
	for i := range a.parts {
		context.enableVertexAttribArray(program, i)
	}
	total := a.totalBytes()
	offset := 0
	for i, p := range a.parts {
		context.vertexAttribPointer(program, i, p.num, float, total, offset)
		offset += float.SizeInBytes() * p.num
	}
}

// disable stops using the array buffer.
func (a *arrayBufferLayout) disable(context *context, program program) {
	// TODO: Disabling should be done in reversed order?
	for i := range a.parts {
		context.disableVertexAttribArray(program, i)
	}
}

// theArrayBufferLayout is the array buffer layout for Ebiten.
var theArrayBufferLayout = arrayBufferLayout{
	// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
	parts: []arrayBufferLayoutPart{
		{
			name: "vertex",
			num:  2,
		},
		{
			name: "tex",
			num:  2,
		},
		{
			name: "tex_region",
			num:  4,
		},
		{
			name: "color_scale",
			num:  4,
		},
	},
}

func init() {
	vertexFloatNum := theArrayBufferLayout.totalBytes() / float.SizeInBytes()
	if graphics.VertexFloatNum != vertexFloatNum {
		panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatNum, vertexFloatNum))
	}
}

type programKey struct {
	useColorM bool
	filter    driver.Filter
	address   driver.Address
}

// openGLState is a state for
type openGLState struct {
	// arrayBuffer is OpenGL's array buffer (vertices data).
	arrayBuffer buffer

	// elementArrayBuffer is OpenGL's element array buffer (indices data).
	elementArrayBuffer buffer

	// programs is OpenGL's program for rendering a texture.
	programs map[programKey]program

	lastProgram  program
	lastUniforms map[string][]float32

	source      *Image
	destination *Image
}

var (
	zeroBuffer  buffer
	zeroProgram program
)

// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
	if err := context.reset(); err != nil {
		return err
	}

	s.lastProgram = zeroProgram
	s.lastUniforms = map[string][]float32{}

	// When context lost happens, deleting programs or buffers is not necessary.
	// However, it is not assumed that reset is called only when context lost happens.
	// Let's delete them explicitly.
	if s.programs == nil {
		s.programs = map[programKey]program{}
	} else {
		for k, p := range s.programs {
			context.deleteProgram(p)
			delete(s.programs, k)
		}
	}

	// On browsers (at least Chrome), buffers are already detached from the context
	// and must not be deleted by DeleteBuffer.
	if !web.IsBrowser() {
		if !s.arrayBuffer.equal(zeroBuffer) {
			context.deleteBuffer(s.arrayBuffer)
		}
		if !s.elementArrayBuffer.equal(zeroBuffer) {
			context.deleteBuffer(s.elementArrayBuffer)
		}
	}

	shaderVertexModelviewNative, err := context.newShader(vertexShader, vertexShaderStr())
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.deleteShader(shaderVertexModelviewNative)

	for _, c := range []bool{false, true} {
		for _, a := range []driver.Address{
			driver.AddressClampToZero,
			driver.AddressRepeat,
		} {
			for _, f := range []driver.Filter{
				driver.FilterNearest,
				driver.FilterLinear,
				driver.FilterScreen,
			} {
				shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, fragmentShaderStr(c, f, a))
				if err != nil {
					panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
				}
				defer context.deleteShader(shaderFragmentColorMatrixNative)

				program, err := context.newProgram([]shader{
					shaderVertexModelviewNative,
					shaderFragmentColorMatrixNative,
				}, theArrayBufferLayout.names())

				if err != nil {
					return err
				}

				s.programs[programKey{
					useColorM: c,
					filter:    f,
					address:   a,
				}] = program
			}
		}
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	// Note that the indices passed to NewElementArrayBuffer is not under GC management
	// in opengl package due to unsafe-way.
	// See NewElementArrayBuffer in context_mobile.go.
	s.elementArrayBuffer = context.newElementArrayBuffer(graphics.IndicesNum * 2)

	return nil
}

// areSameFloat32Array returns a boolean indicating if a and b are deeply equal.
func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// useProgram uses the program (programTexture).
func (g *Graphics) useProgram(mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
	destination := g.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := g.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	g.context.blendFunc(mode)

	program := g.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if !g.state.lastProgram.equal(program) {
		g.context.useProgram(program)
		if g.state.lastProgram.equal(zeroProgram) {
			theArrayBufferLayout.enable(&g.context, program)
			g.context.bindBuffer(arrayBuffer, g.state.arrayBuffer)
			g.context.bindBuffer(elementArrayBuffer, g.state.elementArrayBuffer)
			g.context.uniformInt(program, "texture", 0)
		}

		g.state.lastProgram = program
		g.state.lastUniforms = map[string][]float32{}
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	vs := []float32{float32(vw), float32(vh)}
	if !areSameFloat32Array(g.state.lastUniforms["viewport_size"], vs) {
		g.context.uniformFloats(program, "viewport_size", vs)
		g.state.lastUniforms["viewport_size"] = vs
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(g.state.lastUniforms["color_matrix_body"], esBody) {
			g.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			g.state.lastUniforms["color_matrix_body"] = esBody
		}
		if !areSameFloat32Array(g.state.lastUniforms["color_matrix_translation"], esTranslate) {
			g.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			g.state.lastUniforms["color_matrix_translation"] = esTranslate
		}
	}

	if filter != driver.FilterNearest {
		sw := graphics.InternalImageSize(srcW)
		sh := graphics.InternalImageSize(srcH)
		s := []float32{float32(sw), float32(sh)}
		if !areSameFloat32Array(g.state.lastUniforms["source_size"], s) {
			g.context.uniformFloats(program, "source_size", s)
			g.state.lastUniforms["source_size"] = s
		}
	}

	if filter == driver.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		g.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	g.context.bindTexture(source.textureNative)

	g.state.source = nil
	g.state.destination = nil
	return nil
}
<|endoftext|>


<commit_before>package test/postgres

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/pinzolo/dbmodel"

	_ "github.com/lib/pq"
)

func TestMain(m *testing.M) {
	err := createTestResources()
	if err != nil {
		panic(err)
	}
	code := m.Run()
	defer os.Exit(code)
	err = dropTestResources()
	if err != nil {
		fmt.Println(err)
		code = 2
	}
}

func TestAllTableNames(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.AllTableNames("sales")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 2 {
		t.Errorf("AllTableNames should return 2 table names. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region_currency" {
		t.Errorf("AllTableNames returns invalid table name. expected 'country_region_currency', but actual '%v'", ts[0].Name())
	}
	if ts[0].Comment() != "" {
		t.Errorf("Table comment is null, Comment() should return empty")
	}
	if ts[1].Name() != "currency" {
		t.Errorf("AllTableNames returns invalid table name. expected 'currency', but actual '%v'", ts[1].Name())
	}
	if ts[1].Comment() != "Lookup table containing standard ISO currencies." {
		t.Errorf("AllTableNames should pick up table comment. %+v", ts[1])
	}
}

func TestAllTableNamesOtherSchema(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.AllTableNames("person")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 1 {
		t.Errorf("AllTableNames should return 1 table name. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region" {
		t.Errorf("AllTableNames returns invalid table name. expected 'country_region', but actual '%v'", ts[0].Name())
	}
}

func TestTableNames(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.TableNames("sales", "region")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 1 {
		t.Errorf("TableNames with region should return 1 table names. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region_currency" {
		t.Errorf("TableNames returns invalid table name. expected 'country_region_currency', but actual '%v'", ts[0].Name())
	}
	if ts[0].Comment() != "" {
		t.Errorf("Table comment is null, Comment() should return empty")
	}
}

func TestTableNamesNoResult(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.TableNames("sales", "sample")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 0 {
		t.Errorf("TableNames should return 0 table name. but actual %v", len(ts))
		return
	}
}

func createPgTestDB() error {
	db, err := sql.Open("postgres", "host=localhost user=postgres sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	_, err = db.Exec("CREATE DATABASE dbmodel_test")
	if err != nil {
		return err
	}

	return nil
}

func createTestResources() error {
	err := createPgTestDB()
	if err != nil {
		return err
	}
	db, err := sql.Open("postgres", "host=localhost user=postgres dbname=dbmodel_test sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	bytes, err := createResourceSQL()
	_, err = db.Exec(string(bytes))
	if err != nil {
		return err
	}

	return nil
}

func dropTestResources() error {
	db, err := sql.Open("postgres", "host=localhost user=postgres dbname=postgres sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	_, err = db.Exec("DROP DATABASE IF EXISTS dbmodel_test")
	if err != nil {
		return err
	}
	return nil
}

func createResourceSQL() ([]byte, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	f, err := os.Open(filepath.Join(wd, "create_resources.sql"))
	defer f.Close()
	if err != nil {
		return nil, err
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func createClient() *dbmodel.Client {
	return dbmodel.NewClient("postgres", createDataSource())
}

func createDataSource() dbmodel.DataSource {
	return dbmodel.NewDataSource("localhost", 5432, "postgres", "", "dbmodel_test", map[string]string{"sslmode": "disable"})
}
<commit_msg>Fix invalid package<commit_after>package pg_test

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/pinzolo/dbmodel"

	_ "github.com/lib/pq"
)

func TestMain(m *testing.M) {
	err := createTestResources()
	if err != nil {
		panic(err)
	}
	code := m.Run()
	defer os.Exit(code)
	err = dropTestResources()
	if err != nil {
		fmt.Println(err)
		code = 2
	}
}

func TestAllTableNames(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.AllTableNames("sales")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 2 {
		t.Errorf("AllTableNames should return 2 table names. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region_currency" {
		t.Errorf("AllTableNames returns invalid table name. expected 'country_region_currency', but actual '%v'", ts[0].Name())
	}
	if ts[0].Comment() != "" {
		t.Errorf("Table comment is null, Comment() should return empty")
	}
	if ts[1].Name() != "currency" {
		t.Errorf("AllTableNames returns invalid table name. expected 'currency', but actual '%v'", ts[1].Name())
	}
	if ts[1].Comment() != "Lookup table containing standard ISO currencies." {
		t.Errorf("AllTableNames should pick up table comment. %+v", ts[1])
	}
}

func TestAllTableNamesOtherSchema(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.AllTableNames("person")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 1 {
		t.Errorf("AllTableNames should return 1 table name. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region" {
		t.Errorf("AllTableNames returns invalid table name. expected 'country_region', but actual '%v'", ts[0].Name())
	}
}

func TestTableNames(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.TableNames("sales", "region")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 1 {
		t.Errorf("TableNames with region should return 1 table names. but actual %v", len(ts))
		return
	}
	if ts[0].Name() != "country_region_currency" {
		t.Errorf("TableNames returns invalid table name. expected 'country_region_currency', but actual '%v'", ts[0].Name())
	}
	if ts[0].Comment() != "" {
		t.Errorf("Table comment is null, Comment() should return empty")
	}
}

func TestTableNamesNoResult(t *testing.T) {
	c := createClient()
	defer c.Disconnect()
	c.Connect()

	ts, err := c.TableNames("sales", "sample")
	if err != nil {
		t.Error(err)
	}
	if len(ts) != 0 {
		t.Errorf("TableNames should return 0 table name. but actual %v", len(ts))
		return
	}
}

func createPgTestDB() error {
	db, err := sql.Open("postgres", "host=localhost user=postgres sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	_, err = db.Exec("CREATE DATABASE dbmodel_test")
	if err != nil {
		return err
	}

	return nil
}

func createTestResources() error {
	err := createPgTestDB()
	if err != nil {
		return err
	}
	db, err := sql.Open("postgres", "host=localhost user=postgres dbname=dbmodel_test sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	bytes, err := createResourceSQL()
	_, err = db.Exec(string(bytes))
	if err != nil {
		return err
	}

	return nil
}

func dropTestResources() error {
	db, err := sql.Open("postgres", "host=localhost user=postgres dbname=postgres sslmode=disable")
	if err != nil {
		return err
	}
	defer db.Close()

	_, err = db.Exec("DROP DATABASE IF EXISTS dbmodel_test")
	if err != nil {
		return err
	}
	return nil
}

func createResourceSQL() ([]byte, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	f, err := os.Open(filepath.Join(wd, "create_resources.sql"))
	defer f.Close()
	if err != nil {
		return nil, err
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func createClient() *dbmodel.Client {
	return dbmodel.NewClient("postgres", createDataSource())
}

func createDataSource() dbmodel.DataSource {
	return dbmodel.NewDataSource("localhost", 5432, "postgres", "", "dbmodel_test", map[string]string{"sslmode": "disable"})
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"reflect"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
)

// Force interface implementations
var (
	_ api.CustomEditable = (*AnimeListItem)(nil)
	_ api.AfterEditable  = (*AnimeListItem)(nil)
)

// Edit ...
func (item *AnimeListItem) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	user := GetUserFromContext(ctx)

	if user == nil {
		return true, errors.New("Not logged in")
	}

	switch key {
	case "Episodes":
		fromEpisode := item.Episodes
		toEpisode := int(newValue.Float())

		// Create or update activity if new episode amount is higher
		if toEpisode > fromEpisode {
			lastActivity := user.LastActivityConsumeAnime(item.AnimeID)

			if lastActivity == nil || time.Since(lastActivity.GetCreatedTime()) > 1*time.Hour {
				// If there is no last activity for the given anime,
				// or if the last activity happened more than an hour ago,
				// create a new activity.
				activity := NewActivityConsumeAnime(item.AnimeID, fromEpisode, toEpisode, user.ID)
				activity.Save()
			} else if toEpisode > lastActivity.ToEpisode {
				// Otherwise, update the last activity if episode count is higher.
				lastActivity.ToEpisode = toEpisode
				lastActivity.Save()
			}
		}

		item.Episodes = toEpisode

		if item.Episodes < 0 {
			item.Episodes = 0
		}

		item.OnEpisodesChange()
		return true, nil

	case "Status":
		item.Status = newValue.String()
		item.OnStatusChange()
		return true, nil
	}

	return false, nil
}

// AfterEdit is called after the item is edited.
func (item *AnimeListItem) AfterEdit(ctx *aero.Context) error {
	item.Rating.Clamp()
	item.Edited = DateTimeUTC()
	return nil
}
<commit_msg>Allow updating last activity<commit_after>package arn

import (
	"errors"
	"reflect"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
)

// Force interface implementations
var (
	_ api.CustomEditable = (*AnimeListItem)(nil)
	_ api.AfterEditable  = (*AnimeListItem)(nil)
)

// Edit ...
func (item *AnimeListItem) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	user := GetUserFromContext(ctx)

	if user == nil {
		return true, errors.New("Not logged in")
	}

	switch key {
	case "Episodes":
		fromEpisode := item.Episodes
		toEpisode := int(newValue.Float())

		// Fetch last activity
		lastActivity := user.LastActivityConsumeAnime(item.AnimeID)

		if lastActivity == nil || time.Since(lastActivity.GetCreatedTime()) > 1*time.Hour {
			// If there is no last activity for the given anime,
			// or if the last activity happened more than an hour ago,
			// create a new activity.
			if toEpisode > fromEpisode {
				activity := NewActivityConsumeAnime(item.AnimeID, fromEpisode, toEpisode, user.ID)
				activity.Save()
			}
		} else if toEpisode >= lastActivity.FromEpisode {
			// Otherwise, update the last activity.
			lastActivity.ToEpisode = toEpisode
			lastActivity.Created = DateTimeUTC()
			lastActivity.Save()
		}

		item.Episodes = toEpisode

		if item.Episodes < 0 {
			item.Episodes = 0
		}

		item.OnEpisodesChange()
		return true, nil

	case "Status":
		item.Status = newValue.String()
		item.OnStatusChange()
		return true, nil
	}

	return false, nil
}

// AfterEdit is called after the item is edited.
func (item *AnimeListItem) AfterEdit(ctx *aero.Context) error {
	item.Rating.Clamp()
	item.Edited = DateTimeUTC()
	return nil
}
<|endoftext|>


<commit_before>// +build !windows

package main

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"

	"github.com/go-check/check"
	"github.com/kr/pty"
)

// save a repo and try to load it using stdout
func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
	name := "test-save-and-load-repo-stdout"
	dockerCmd(c, "run", "--name", name, "busybox", "true")

	repoName := "foobar-save-load-test"
	out, _ := dockerCmd(c, "commit", name, repoName)

	before, _ := dockerCmd(c, "inspect", repoName)

	tmpFile, err := ioutil.TempFile("", "foobar-save-load-test.tar")
	c.Assert(err, check.IsNil)
	defer os.Remove(tmpFile.Name())

	saveCmd := exec.Command(dockerBinary, "save", repoName)
	saveCmd.Stdout = tmpFile

	if _, err = runCommand(saveCmd); err != nil {
		c.Fatalf("failed to save repo: %v", err)
	}

	tmpFile, err = os.Open(tmpFile.Name())
	c.Assert(err, check.IsNil)

	deleteImages(repoName)

	loadCmd := exec.Command(dockerBinary, "load")
	loadCmd.Stdin = tmpFile

	if out, _, err = runCommandWithOutput(loadCmd); err != nil {
		c.Fatalf("failed to load repo: %s, %v", out, err)
	}

	after, _ := dockerCmd(c, "inspect", repoName)

	if before != after {
		c.Fatalf("inspect is not the same after a save / load")
	}

	deleteImages(repoName)

	pty, tty, err := pty.Open()
	if err != nil {
		c.Fatalf("Could not open pty: %v", err)
	}
	cmd := exec.Command(dockerBinary, "save", repoName)
	cmd.Stdin = tty
	cmd.Stdout = tty
	cmd.Stderr = tty
	if err := cmd.Start(); err != nil {
		c.Fatalf("start err: %v", err)
	}
	if err := cmd.Wait(); err == nil {
		c.Fatal("did not break writing to a TTY")
	}

	buf := make([]byte, 1024)

	n, err := pty.Read(buf)
	if err != nil {
		c.Fatal("could not read tty output")
	}

	if !bytes.Contains(buf[:n], []byte("Cowardly refusing")) {
		c.Fatal("help output is not being yielded", out)
	}
}
<commit_msg>update save-load-unix-test<commit_after>// +build !windows

package main

import (
	"io/ioutil"
	"os"
	"os/exec"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
	"github.com/kr/pty"
)

// save a repo and try to load it using stdout
func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
	name := "test-save-and-load-repo-stdout"
	dockerCmd(c, "run", "--name", name, "busybox", "true")

	repoName := "foobar-save-load-test"
	out, _ := dockerCmd(c, "commit", name, repoName)

	before, _ := dockerCmd(c, "inspect", repoName)

	tmpFile, err := ioutil.TempFile("", "foobar-save-load-test.tar")
	c.Assert(err, check.IsNil)
	defer os.Remove(tmpFile.Name())

	saveCmd := exec.Command(dockerBinary, "save", repoName)
	saveCmd.Stdout = tmpFile

	_, err = runCommand(saveCmd)
	c.Assert(err, check.IsNil)

	tmpFile, err = os.Open(tmpFile.Name())
	c.Assert(err, check.IsNil)

	deleteImages(repoName)

	loadCmd := exec.Command(dockerBinary, "load")
	loadCmd.Stdin = tmpFile

	out, _, err = runCommandWithOutput(loadCmd)
	c.Assert(err, check.IsNil, check.Commentf(out))

	after, _ := dockerCmd(c, "inspect", repoName)

	c.Assert(before, check.Equals, after) //inspect is not the same after a save / load

	deleteImages(repoName)

	pty, tty, err := pty.Open()
	c.Assert(err, check.IsNil)
	cmd := exec.Command(dockerBinary, "save", repoName)
	cmd.Stdin = tty
	cmd.Stdout = tty
	cmd.Stderr = tty
	c.Assert(cmd.Start(), check.IsNil)
	c.Assert(cmd.Wait(), check.NotNil) //did not break writing to a TTY

	buf := make([]byte, 1024)

	n, err := pty.Read(buf)
	c.Assert(err, check.IsNil) //could not read tty output
	c.Assert(string(buf[:n]), checker.Contains, "Cowardly refusing", check.Commentf("help output is not being yielded", out))
}
<|endoftext|>


<commit_before><commit_msg>Bug: Fix List dashboard version (#50544)<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwallet/tx"
	"github.com/conformal/btcwire"
	"sort"
	"sync"
	"time"
)

// ErrInsufficientFunds represents an error where there are not enough
// funds from unspent tx outputs for a wallet to create a transaction.
var ErrInsufficientFunds = errors.New("insufficient funds")

// ErrUnknownBitcoinNet represents an error where the parsed or
// requested bitcoin network is invalid (neither mainnet nor testnet).
var ErrUnknownBitcoinNet = errors.New("unknown bitcoin network")

// TxFee represents the global transaction fee added to newly-created
// transactions and sent as a reward to the block miner.  i is measured
// in satoshis.
var TxFee = struct {
	sync.Mutex
	i int64
}{
	i: 10000, // This is a fee of 0.0001 BTC.
}

// CreatedTx is a type holding information regarding a newly-created
// transaction, including the raw bytes, inputs, and an address and UTXO
// for change (if any).
type CreatedTx struct {
	rawTx      []byte
	inputs     []*tx.Utxo
	changeAddr string
	changeUtxo *tx.Utxo
}

// TXID is a transaction hash identifying a transaction.
type TXID string

// UnminedTXs holds a map of transaction IDs as keys mapping to a
// hex string of a raw transaction.  If sending a raw transaction
// succeeds, the tx is added to this map and checked again after each
// new block.  If the new block contains a tx, it is removed from
// this map.  Otherwise, btcwallet will resend the tx to btcd.
var UnminedTxs = struct {
	sync.Mutex
	m map[TXID]*CreatedTx
}{
	m: make(map[TXID]*CreatedTx),
}

// ByAmount defines the methods needed to satisify sort.Interface to
// sort a slice of Utxos by their amount.
type ByAmount []*tx.Utxo

func (u ByAmount) Len() int {
	return len(u)
}

func (u ByAmount) Less(i, j int) bool {
	return u[i].Amt < u[j].Amt
}

func (u ByAmount) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// selectInputs selects the minimum number possible of unspent
// outputs to use to create a new transaction that spends amt satoshis.
// Previous outputs with less than minconf confirmations are ignored.  btcout
// is the total number of satoshis which would be spent by the combination
// of all selected previous outputs.  err will equal ErrInsufficientFunds if there
// are not enough unspent outputs to spend amt.
func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, btcout uint64, err error) {
	bs, err := GetCurBlock()
	if err != nil {
		return nil, 0, err
	}

	// Create list of eligible unspent previous outputs to use as tx
	// inputs, and sort by the amount in reverse order so a minimum number
	// of inputs is needed.
	eligible := make([]*tx.Utxo, 0, len(s))
	for _, utxo := range s {
		// TODO(jrick): if Height is -1, the UTXO is the result of spending
		// to a change address, resulting in a UTXO not yet mined in a block.
		// For now, disallow creating transactions until these UTXOs are mined
		// into a block and show up as part of the balance.
		if utxo.Height != -1 && int(bs.Height-utxo.Height) >= minconf {
			eligible = append(eligible, utxo)
		}
	}
	sort.Sort(sort.Reverse(ByAmount(eligible)))

	// Iterate throguh eligible transactions, appending to outputs and
	// increasing btcout.  This is finished when btcout is greater than the
	// requested amt to spend.
	for _, u := range eligible {
		inputs = append(inputs, u)
		if btcout += u.Amt; btcout >= amt {
			return inputs, btcout, nil
		}
	}
	if btcout < amt {
		return nil, 0, ErrInsufficientFunds
	}

	return inputs, btcout, nil
}

// txToPairs creates a raw transaction sending the amounts for each
// address/amount pair and fee to each address and the miner.  minconf
// specifies the minimum number of confirmations required before an
// unspent output is eligible for spending. Leftover input funds not sent
// to addr or as a fee for the miner are sent to a newly generated
// address. If change is needed to return funds back to an owned
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed
// block hash) Utxo.  ErrInsufficientFunds is returned if there are not
// enough eligible unspent outputs to create the transaction.
func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (*CreatedTx, error) {
	// Recorded unspent transactions should not be modified until this
	// finishes.
	w.UtxoStore.RLock()
	defer w.UtxoStore.RUnlock()

	// Create a new transaction which will include all input scripts.
	msgtx := btcwire.NewMsgTx()

	// Calculate minimum amount needed for inputs.
	var amt uint64
	for _, v := range pairs {
		amt += v
	}

	// Select unspent outputs to be used in transaction.
	inputs, btcout, err := selectInputs(w.UtxoStore.s, amt+fee, minconf)
	if err != nil {
		return nil, err
	}

	// Add outputs to new tx.
	for addr, amt := range pairs {
		addr160, _, err := btcutil.DecodeAddress(addr)
		if err != nil {
			return nil, fmt.Errorf("cannot decode address: %s", err)
		}

		// Spend amt to addr160
		pkScript, err := btcscript.PayToPubKeyHashScript(addr160)
		if err != nil {
			return nil, fmt.Errorf("cannot create txout script: %s", err)
		}
		txout := btcwire.NewTxOut(int64(amt), pkScript)
		msgtx.AddTxOut(txout)
	}

	// Check if there are leftover unspent outputs, and return coins back to
	// a new address we own.
	var changeUtxo *tx.Utxo
	var changeAddr string
	if btcout > amt+fee {
		// Create a new address to spend leftover outputs to.
		// TODO(jrick): use the next chained address, not the next unused.
		var err error
		changeAddr, err = w.NextUnusedAddress()
		if err != nil {
			return nil, fmt.Errorf("failed to get next unused address: %s", err)
		}

		// Spend change
		change := btcout - (amt + fee)
		changeAddrHash, _, err := btcutil.DecodeAddress(changeAddr)
		if err != nil {
			return nil, fmt.Errorf("cannot decode new address: %s", err)
		}
		pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash)
		if err != nil {
			return nil, fmt.Errorf("cannot create txout script: %s", err)
		}
		msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript))

		changeUtxo = &tx.Utxo{
			Amt: change,
			Out: tx.OutPoint{
				// Hash is unset (zeroed) here and must be filled in
				// with the transaction hash of the complete
				// transaction.
				Index: uint32(len(pairs)),
			},
			Height:    -1,
			Subscript: pkScript,
		}
		copy(changeUtxo.AddrHash[:], changeAddrHash)
	}

	// Selected unspent outputs become new transaction's inputs.
	for _, ip := range inputs {
		msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
	}
	for i, ip := range inputs {
		addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net())
		if err != nil {
			return nil, err
		}
		privkey, err := w.GetAddressKey(addrstr)
		if err != nil {
			return nil, fmt.Errorf("cannot get address key: %v", err)
		}
		ai, err := w.GetAddressInfo(addrstr)
		if err != nil {
			return nil, fmt.Errorf("cannot get address info: %v", err)
		}

		// TODO(jrick): we want compressed pubkeys.  Switch wallet to
		// generate addresses from the compressed key.  This will break
		// armory wallet compat but oh well.
		sigscript, err := btcscript.SignatureScript(msgtx, i,
			ip.Subscript, btcscript.SigHashAll, privkey, ai.Compressed)
		if err != nil {
			return nil, fmt.Errorf("cannot create sigscript: %s", err)
		}
		msgtx.TxIn[i].SignatureScript = sigscript
	}

	// Validate msgtx before returning the raw transaction.
	flags := btcscript.ScriptCanonicalSignatures
	bip16 := time.Now().After(btcscript.Bip16Activation)
	if bip16 {
		flags |= btcscript.ScriptBip16
	}
	for i, txin := range msgtx.TxIn {
		engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i,
			msgtx, flags)
		if err != nil {
			return nil, fmt.Errorf("cannot create script engine: %s", err)
		}
		if err = engine.Execute(); err != nil {
			return nil, fmt.Errorf("cannot validate transaction: %s", err)
		}
	}

	// Fill Tx hash of change outpoint with transaction hash.
	if changeUtxo != nil {
		txHash, err := msgtx.TxSha()
		if err != nil {
			return nil, fmt.Errorf("cannot create transaction hash: %s", err)
		}
		copy(changeUtxo.Out.Hash[:], txHash[:])
	}

	buf := new(bytes.Buffer)
	msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
	return &CreatedTx{buf.Bytes(), inputs, changeAddr, changeUtxo}, nil
}
<commit_msg>Remove old TODO comment.<commit_after>/*
 * Copyright (c) 2013 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwallet/tx"
	"github.com/conformal/btcwire"
	"sort"
	"sync"
	"time"
)

// ErrInsufficientFunds represents an error where there are not enough
// funds from unspent tx outputs for a wallet to create a transaction.
var ErrInsufficientFunds = errors.New("insufficient funds")

// ErrUnknownBitcoinNet represents an error where the parsed or
// requested bitcoin network is invalid (neither mainnet nor testnet).
var ErrUnknownBitcoinNet = errors.New("unknown bitcoin network")

// TxFee represents the global transaction fee added to newly-created
// transactions and sent as a reward to the block miner.  i is measured
// in satoshis.
var TxFee = struct {
	sync.Mutex
	i int64
}{
	i: 10000, // This is a fee of 0.0001 BTC.
}

// CreatedTx is a type holding information regarding a newly-created
// transaction, including the raw bytes, inputs, and an address and UTXO
// for change (if any).
type CreatedTx struct {
	rawTx      []byte
	inputs     []*tx.Utxo
	changeAddr string
	changeUtxo *tx.Utxo
}

// TXID is a transaction hash identifying a transaction.
type TXID string

// UnminedTXs holds a map of transaction IDs as keys mapping to a
// hex string of a raw transaction.  If sending a raw transaction
// succeeds, the tx is added to this map and checked again after each
// new block.  If the new block contains a tx, it is removed from
// this map.  Otherwise, btcwallet will resend the tx to btcd.
var UnminedTxs = struct {
	sync.Mutex
	m map[TXID]*CreatedTx
}{
	m: make(map[TXID]*CreatedTx),
}

// ByAmount defines the methods needed to satisify sort.Interface to
// sort a slice of Utxos by their amount.
type ByAmount []*tx.Utxo

func (u ByAmount) Len() int {
	return len(u)
}

func (u ByAmount) Less(i, j int) bool {
	return u[i].Amt < u[j].Amt
}

func (u ByAmount) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// selectInputs selects the minimum number possible of unspent
// outputs to use to create a new transaction that spends amt satoshis.
// Previous outputs with less than minconf confirmations are ignored.  btcout
// is the total number of satoshis which would be spent by the combination
// of all selected previous outputs.  err will equal ErrInsufficientFunds if there
// are not enough unspent outputs to spend amt.
func selectInputs(s tx.UtxoStore, amt uint64, minconf int) (inputs []*tx.Utxo, btcout uint64, err error) {
	bs, err := GetCurBlock()
	if err != nil {
		return nil, 0, err
	}

	// Create list of eligible unspent previous outputs to use as tx
	// inputs, and sort by the amount in reverse order so a minimum number
	// of inputs is needed.
	eligible := make([]*tx.Utxo, 0, len(s))
	for _, utxo := range s {
		// TODO(jrick): if Height is -1, the UTXO is the result of spending
		// to a change address, resulting in a UTXO not yet mined in a block.
		// For now, disallow creating transactions until these UTXOs are mined
		// into a block and show up as part of the balance.
		if utxo.Height != -1 && int(bs.Height-utxo.Height) >= minconf {
			eligible = append(eligible, utxo)
		}
	}
	sort.Sort(sort.Reverse(ByAmount(eligible)))

	// Iterate throguh eligible transactions, appending to outputs and
	// increasing btcout.  This is finished when btcout is greater than the
	// requested amt to spend.
	for _, u := range eligible {
		inputs = append(inputs, u)
		if btcout += u.Amt; btcout >= amt {
			return inputs, btcout, nil
		}
	}
	if btcout < amt {
		return nil, 0, ErrInsufficientFunds
	}

	return inputs, btcout, nil
}

// txToPairs creates a raw transaction sending the amounts for each
// address/amount pair and fee to each address and the miner.  minconf
// specifies the minimum number of confirmations required before an
// unspent output is eligible for spending. Leftover input funds not sent
// to addr or as a fee for the miner are sent to a newly generated
// address. If change is needed to return funds back to an owned
// address, changeUtxo will point to a unconfirmed (height = -1, zeroed
// block hash) Utxo.  ErrInsufficientFunds is returned if there are not
// enough eligible unspent outputs to create the transaction.
func (w *BtcWallet) txToPairs(pairs map[string]uint64, fee uint64, minconf int) (*CreatedTx, error) {
	// Recorded unspent transactions should not be modified until this
	// finishes.
	w.UtxoStore.RLock()
	defer w.UtxoStore.RUnlock()

	// Create a new transaction which will include all input scripts.
	msgtx := btcwire.NewMsgTx()

	// Calculate minimum amount needed for inputs.
	var amt uint64
	for _, v := range pairs {
		amt += v
	}

	// Select unspent outputs to be used in transaction.
	inputs, btcout, err := selectInputs(w.UtxoStore.s, amt+fee, minconf)
	if err != nil {
		return nil, err
	}

	// Add outputs to new tx.
	for addr, amt := range pairs {
		addr160, _, err := btcutil.DecodeAddress(addr)
		if err != nil {
			return nil, fmt.Errorf("cannot decode address: %s", err)
		}

		// Spend amt to addr160
		pkScript, err := btcscript.PayToPubKeyHashScript(addr160)
		if err != nil {
			return nil, fmt.Errorf("cannot create txout script: %s", err)
		}
		txout := btcwire.NewTxOut(int64(amt), pkScript)
		msgtx.AddTxOut(txout)
	}

	// Check if there are leftover unspent outputs, and return coins back to
	// a new address we own.
	var changeUtxo *tx.Utxo
	var changeAddr string
	if btcout > amt+fee {
		// Create a new address to spend leftover outputs to.
		// TODO(jrick): use the next chained address, not the next unused.
		var err error
		changeAddr, err = w.NextUnusedAddress()
		if err != nil {
			return nil, fmt.Errorf("failed to get next unused address: %s", err)
		}

		// Spend change
		change := btcout - (amt + fee)
		changeAddrHash, _, err := btcutil.DecodeAddress(changeAddr)
		if err != nil {
			return nil, fmt.Errorf("cannot decode new address: %s", err)
		}
		pkScript, err := btcscript.PayToPubKeyHashScript(changeAddrHash)
		if err != nil {
			return nil, fmt.Errorf("cannot create txout script: %s", err)
		}
		msgtx.AddTxOut(btcwire.NewTxOut(int64(change), pkScript))

		changeUtxo = &tx.Utxo{
			Amt: change,
			Out: tx.OutPoint{
				// Hash is unset (zeroed) here and must be filled in
				// with the transaction hash of the complete
				// transaction.
				Index: uint32(len(pairs)),
			},
			Height:    -1,
			Subscript: pkScript,
		}
		copy(changeUtxo.AddrHash[:], changeAddrHash)
	}

	// Selected unspent outputs become new transaction's inputs.
	for _, ip := range inputs {
		msgtx.AddTxIn(btcwire.NewTxIn((*btcwire.OutPoint)(&ip.Out), nil))
	}
	for i, ip := range inputs {
		addrstr, err := btcutil.EncodeAddress(ip.AddrHash[:], w.Wallet.Net())
		if err != nil {
			return nil, err
		}
		privkey, err := w.GetAddressKey(addrstr)
		if err != nil {
			return nil, fmt.Errorf("cannot get address key: %v", err)
		}
		ai, err := w.GetAddressInfo(addrstr)
		if err != nil {
			return nil, fmt.Errorf("cannot get address info: %v", err)
		}

		sigscript, err := btcscript.SignatureScript(msgtx, i,
			ip.Subscript, btcscript.SigHashAll, privkey, ai.Compressed)
		if err != nil {
			return nil, fmt.Errorf("cannot create sigscript: %s", err)
		}
		msgtx.TxIn[i].SignatureScript = sigscript
	}

	// Validate msgtx before returning the raw transaction.
	flags := btcscript.ScriptCanonicalSignatures
	bip16 := time.Now().After(btcscript.Bip16Activation)
	if bip16 {
		flags |= btcscript.ScriptBip16
	}
	for i, txin := range msgtx.TxIn {
		engine, err := btcscript.NewScript(txin.SignatureScript, inputs[i].Subscript, i,
			msgtx, flags)
		if err != nil {
			return nil, fmt.Errorf("cannot create script engine: %s", err)
		}
		if err = engine.Execute(); err != nil {
			return nil, fmt.Errorf("cannot validate transaction: %s", err)
		}
	}

	// Fill Tx hash of change outpoint with transaction hash.
	if changeUtxo != nil {
		txHash, err := msgtx.TxSha()
		if err != nil {
			return nil, fmt.Errorf("cannot create transaction hash: %s", err)
		}
		copy(changeUtxo.Out.Hash[:], txHash[:])
	}

	buf := new(bytes.Buffer)
	msgtx.BtcEncode(buf, btcwire.ProtocolVersion)
	return &CreatedTx{buf.Bytes(), inputs, changeAddr, changeUtxo}, nil
}
<|endoftext|>