<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"math/rand"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/juju/errors"
	"github.com/ngaut/log"
)

var (
	dsn                  = flag.String("dsn", "root:@(127.0.0.1:4000)/test", "DB dsn to use.")
	persons              = flag.Int("persons", 5, "Number of concurrent persions.")
	balanceCheckInterval = flag.Duration("balance-check-interval", 1*time.Second, "Interval of balance check.")
)

type Bank struct {
	db *sql.DB
}

// People with id deposit num money into bank
func (b *Bank) deposit(id string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance+%d where id=%s", num, id))
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

// People with id withdraw num money from bank
func (b *Bank) withdraw(id string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance-%d where id=%s", num, id))
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

// Get balance for customer from bank
func (b *Bank) getBalance(id string) (int, error) {
	tx, err := b.db.Begin()
	if err != nil {
		return -1, errors.Trace(err)
	}
	r := tx.QueryRow(fmt.Sprintf("select balance from customers where id=%s", id))
	var bb int
	err = r.Scan(&bb)
	if err != nil {
		return -1, errors.Trace(err)
	}
	return bb, nil
}

// Transfer num money.
func (b *Bank) transfer(from, to string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance-%d where id=%s", num, from))
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance+%d where id=%s", num, to))
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

func (b *Bank) CreateAccount(id string, balance int) error {
	tx, err := b.db.Begin()
	st := fmt.Sprintf("insert into test.customers values (%s, %d)", id, balance)
	_, err = tx.Exec(st)
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	if err != nil {
		log.Errorf("Find error when commit: %v", err)
		return errors.Trace(err)
	}
	return nil
}

func (b *Bank) Open(dsn string) error {
	var err error
	b.db, err = sql.Open("mysql", dsn)
	if err != nil {
		log.Errorf("Open %s error: %v", dsn, err)
		return errors.Trace(err)
	}

	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec("drop table if exists customers;")
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec("create table customers (id VARCHAR(32) PRIMARY KEY, balance INT);")
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

func (b *Bank) Close(dsn string) error {
	if b.db == nil {
		return nil
	}
	return b.db.Close()
}

type Customer struct {
	id      string
	wallet  int   // Money in the wallet
	balance int   // Balance in the bank
	bank    *Bank // Bank
	friends []*Customer
	recvCh  chan int
	working bool
}

func (c *Customer) randomMoney(upper int) int {
	// Get random money greater than or equals 0, smaller than or equals than upper
	if upper == 0 {
		return 0
	}
	return int(rand.Int31n(int32(upper)))
}

// Random deposite
func (c *Customer) randomDeposit() error {
	n := c.randomMoney(c.wallet)
	if n == 0 {
		return nil
	}
	log.Infof("[Customer_%s] Begin deposite %d into bank.", c.id, n)
	err := c.bank.deposit(c.id, n)
	if err != nil {
		//succ
		return errors.Trace(err)
	}
	c.wallet -= n
	c.balance += n
	log.Infof("[Customer_%s] Deposite %d into bank success.", c.id, n)
	return nil
}

// Random withdraw
func (c *Customer) randomWithdraw() error {
	n := c.randomMoney(c.balance)
	if n == 0 {
		return nil
	}
	log.Infof("[Customer_%s] Begin withdraw %d money from bank.", c.id, n)
	err := c.bank.withdraw(c.id, n)
	if err != nil {
		//succ
		return errors.Trace(err)
	}
	c.wallet += n
	c.balance -= n
	log.Infof("[Customer_%s] Withdraw %d money from bank success.", c.id, n)
	return nil
}

func (c *Customer) randomFriend() (string, chan int) {
	i := int(rand.Int31n(int32(len(c.friends))))
	return c.friends[i].id, c.friends[i].recvCh
}

// Random transfer
func (c *Customer) randomTransfer() error {
	n := c.randomMoney(c.balance)
	if n == 0 {
		return nil
	}
	f, ch := c.randomFriend()
	log.Infof("[Customer_%s] Begin transfer %d money to Customer_%s.", c.id, n, f)
	err := c.bank.transfer(c.id, f, n)
	if err != nil {
		return errors.Trace(err)
	}
	ch <- n
	c.balance -= n
	log.Infof("[Customer_%s] Transfer %d money to Customer_%s succ.", c.id, n, f)
	return nil
}

func (c *Customer) randomDo() error {
	if c.working {
		return nil
	}
	c.working = true
	defer func() {
		c.working = false
	}()

	// random action
	// GetRandom Action
	action := int(rand.Int31n(3))
	if action == 0 {
		return c.randomDeposit()
	} else if action == 1 {
		return c.randomWithdraw()
	} else if action == 2 {
		return c.randomTransfer()
	}
	return errors.Errorf("Wrong Type of action ", action)
}

func (c *Customer) run(wg sync.WaitGroup) error {
	ticker := time.NewTicker(1 * time.Second)
	cnt := 0
	defer func() {
		ticker.Stop()
		wg.Done()
		log.Infof("Customer_%s QUIT succ", c.id)
	}()
	for {
		select {
		case n := <-c.recvCh:
			c.balance += n
		case <-ticker.C:
			cnt += 1
			if cnt > 60 {
				return nil
			}
			log.Infof("[Customer_%s] round %d", c.id, cnt)
			err := c.randomDo()
			if err != nil {
				log.Errorf("ERRORRRRR!!!!")
				return errors.Trace(err)
			}
		}
	}
}

func main() {
	bank := &Bank{}
	err := bank.Open(*dsn)
	if err != nil {
		fmt.Println("Open Bank error: ", err)
	}
	customers := make([]*Customer, 0, *persons)
	initWallet := 5000
	for i := 0; i < *persons; i++ {
		id := fmt.Sprintf("%d", i)
		c := &Customer{
			id:      id,
			wallet:  initWallet,
			balance: 0,
			friends: make([]*Customer, 0, *persons-1),
			recvCh:  make(chan int),
			bank:    bank,
		}
		err := bank.CreateAccount(c.id, 0)
		if err != nil {
			log.Errorf("Create user failed: %v", err)
			return
		}
		customers = append(customers, c)
		log.Infof("Create user %s succ", c.id)
	}
	for i := 0; i < *persons-1; i++ {
		for j := i + 1; j < *persons; j++ {
			c1 := customers[i]
			c2 := customers[j]
			c1.friends = append(c1.friends, c2)
			c2.friends = append(c2.friends, c1)
		}
	}
	var wg sync.WaitGroup
	wg.Add(len(customers))
	fmt.Println("Bank Demo begin.......")
	for _, c := range customers {
		go c.run(wg)
	}
	wg.Wait()
	fmt.Println("Bank Demo End!")
	fmt.Println("Start checking........")
	money := 0
	for _, c := range customers {
		bb, err1 := bank.getBalance(c.id)
		if err1 != nil {
			log.Errorf("Get balance error: %v", err1)
			return
		}
		if bb != c.balance {
			log.Errorf("Balance unmatch for %s, %d:%d", c.id, c.balance, bb)
			return
		}
		money += c.wallet + bb
	}
	total := *persons * initWallet
	if money != total {
		log.Errorf("Total money unmatch: %d : %d", money, total)
		return
	}
	fmt.Println("Success!!!!")
}
<commit_msg>*: Fix bug<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"
	// For pprof
	"net/http"
	_ "net/http/pprof"

	_ "github.com/go-sql-driver/mysql"
	"github.com/juju/errors"
	"github.com/ngaut/log"
)

var (
	dsn                  = flag.String("dsn", "root:@(127.0.0.1:4000)/test", "DB dsn to use.")
	persons              = flag.Int("persons", 5, "Number of concurrent persions.")
	balanceCheckInterval = flag.Duration("balance-check-interval", 1*time.Second, "Interval of balance check.")
)

type Bank struct {
	db *sql.DB
}

// People with id deposit num money into bank
func (b *Bank) deposit(id string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance+%d where id=%s", num, id))
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

// People with id withdraw num money from bank
func (b *Bank) withdraw(id string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec(fmt.Sprintf("update customers set balance=balance-%d where id=%s", num, id))
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

// Get balance for customer from bank
func (b *Bank) getBalance(id string) (int, error) {
	tx, err := b.db.Begin()
	if err != nil {
		return -1, errors.Trace(err)
	}
	r := tx.QueryRow(fmt.Sprintf("select balance from customers where id=%s", id))
	var bb int
	err = r.Scan(&bb)
	if err != nil {
		return -1, errors.Trace(err)
	}
	return bb, nil
}

// Transfer num money.
func (b *Bank) transfer(from, to string, num int) error {
	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	sql1 := fmt.Sprintf("update customers set balance=balance-%d where id=%s", num, from)
	sql2 := fmt.Sprintf("update customers set balance=balance+%d where id=%s", num, to)
	var sqls []string
	// Solve deadlock
	fid, err := strconv.Atoi(from)
	if err != nil {
		return errors.Trace(err)
	}
	tid, err := strconv.Atoi(to)
	if err != nil {
		return errors.Trace(err)
	}
	if fid < tid {
		sqls = []string{sql1, sql2}
	} else {
		sqls = []string{sql2, sql1}
	}
	for _, s := range sqls {
		_, err = tx.Exec(s)
		if err != nil {
			return errors.Trace(err)
		}
	}
	err = tx.Commit()
	return errors.Trace(err)
}

func (b *Bank) CreateAccount(id string, balance int) error {
	tx, err := b.db.Begin()
	st := fmt.Sprintf("insert into test.customers values (%s, %d)", id, balance)
	_, err = tx.Exec(st)
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	if err != nil {
		log.Errorf("Find error when commit: %v", err)
		return errors.Trace(err)
	}
	return nil
}

func (b *Bank) Open(dsn string) error {
	var err error
	b.db, err = sql.Open("mysql", dsn)
	if err != nil {
		log.Errorf("Open %s error: %v", dsn, err)
		return errors.Trace(err)
	}

	tx, err := b.db.Begin()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec("drop table if exists customers;")
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tx.Exec("create table customers (id VARCHAR(32) PRIMARY KEY, balance INT);")
	if err != nil {
		return errors.Trace(err)
	}
	err = tx.Commit()
	return errors.Trace(err)
}

func (b *Bank) Close(dsn string) error {
	if b.db == nil {
		return nil
	}
	return b.db.Close()
}

type Customer struct {
	id      string
	wallet  int   // Money in the wallet
	balance int   // Balance in the bank
	bank    *Bank // Bank
	friends []*Customer
	recvCh  chan int
	working bool
}

func (c *Customer) randomMoney(upper int) int {
	// Get random money greater than or equals 0, smaller than or equals than upper
	if upper == 0 {
		return 0
	}
	return int(rand.Int31n(int32(upper)))
}

// Random deposite
func (c *Customer) randomDeposit() error {
	n := c.randomMoney(c.wallet)
	if n == 0 {
		return nil
	}
	//log.Infof("[Customer_%s] Begin deposite %d into bank.", c.id, n)
	err := c.bank.deposit(c.id, n)
	if err != nil {
		//succ
		return errors.Trace(err)
	}
	c.wallet -= n
	c.balance += n
	//log.Infof("[Customer_%s] Deposite %d into bank success.", c.id, n)
	return nil
}

// Random withdraw
func (c *Customer) randomWithdraw() error {
	n := c.randomMoney(c.balance)
	if n == 0 {
		return nil
	}
	//log.Infof("[Customer_%s] Begin withdraw %d money from bank.", c.id, n)
	err := c.bank.withdraw(c.id, n)
	if err != nil {
		//succ
		return errors.Trace(err)
	}
	c.wallet += n
	c.balance -= n
	//log.Infof("[Customer_%s] Withdraw %d money from bank success.", c.id, n)
	return nil
}

func (c *Customer) randomFriend() *Customer {
	i := int(rand.Int31n(int32(len(c.friends))))
	return c.friends[i]
}

// Random transfer
func (c *Customer) randomTransfer() error {
	n := c.randomMoney(c.balance)
	if n == 0 {
		return nil
	}
	f := c.randomFriend()
	//log.Infof("[Customer_%s] Begin transfer %d money to Customer_%s.", c.id, n, f)
	err := c.bank.transfer(c.id, f.id, n)
	if err != nil {
		return errors.Trace(err)
	}
	f.recvCh <- n
	c.balance -= n
	//log.Infof("[Customer_%s] Transfer %d money to Customer_%s succ.", c.id, n, f)
	return nil
}

func (c *Customer) randomDo() error {
	if c.working {
		//log.Infof("[Customer_%s] is working....", c.id)
		return nil
	}
	c.working = true
	defer func() {
		c.working = false
	}()

	// random action
	// GetRandom Action
	action := int(rand.Int31n(3))
	var err error
	if action == 0 {
		err = c.randomDeposit()
	} else if action == 1 {
		err = c.randomWithdraw()
	} else if action == 2 {
		err = c.randomTransfer()
	} else {
		return errors.Errorf("Wrong Type of action ", action)
	}
	if err != nil {
		//log.Errorf("[Customer_%s] action %d failed!!: %v", c.id, action, err)
		return errors.Trace(err)
	}
	//log.Infof("[Customer_%s] action %d succ", c.id, action)
	return nil
}

func (c *Customer) run() error {
	ticker := time.NewTicker(1 * time.Second)
	cnt := 0
	defer func() {
		ticker.Stop()
		wg.Done()
		log.Infof("Customer_%s QUIT succ", c.id)
	}()
	for {
		select {
		case n := <-c.recvCh:
			c.balance += n
		case <-ticker.C:
			cnt += 1
			if cnt > 60 {
				return nil
			}
			log.Infof("[Customer_%s] round %d", c.id, cnt)
			err := c.randomDo()
			if err != nil {
				log.Errorf("ERRORRRRR!!!!")
				return errors.Trace(err)
			}
		}
	}
}

var wg sync.WaitGroup

func main() {
	go http.ListenAndServe("localhost:8889", nil)
	// Create and open bank
	bank := &Bank{}
	err := bank.Open(*dsn)
	if err != nil {
		fmt.Println("Open Bank error: ", err)
	}
	// Create customers
	customers := make([]*Customer, 0, *persons)
	initWallet := 5000
	for i := 0; i < *persons; i++ {
		id := fmt.Sprintf("%d", i)
		c := &Customer{
			id:      id,
			wallet:  initWallet,
			balance: 0,
			friends: make([]*Customer, 0, *persons-1),
			recvCh:  make(chan int),
			bank:    bank,
		}
		err := bank.CreateAccount(c.id, 0)
		if err != nil {
			log.Errorf("Create user failed: %v", err)
			return
		}
		customers = append(customers, c)
		log.Infof("Create user %s succ", c.id)
	}
	// Make customers become friends to each other.
	for i := 0; i < *persons-1; i++ {
		for j := i + 1; j < *persons; j++ {
			c1 := customers[i]
			c2 := customers[j]
			c1.friends = append(c1.friends, c2)
			c2.friends = append(c2.friends, c1)
		}
	}
	// Start to act.
	fmt.Println("Bank Demo begin.......")
	for i, c := range customers {
		log.Infof("wg add %d", i)
		wg.Add(1)
		go c.run()
	}
	wg.Wait()
	fmt.Println("Bank Demo End!")
	fmt.Println("Start checking........")
	money := 0
	for _, c := range customers {
		bb, err1 := bank.getBalance(c.id)
		if err1 != nil {
			log.Errorf("Get balance error: %v", err1)
			return
		}
		if bb != c.balance {
			log.Errorf("Balance unmatch for %s, %d:%d", c.id, c.balance, bb)
			return
		}
		money += c.wallet + bb
	}
	total := *persons * initWallet
	if money != total {
		log.Errorf("Total money unmatch: %d : %d", money, total)
		return
	}
	fmt.Println("Success!!!!")
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/JREAMLU/core/logs"
	_ "github.com/JREAMLU/jkernel/base/routers"

	"github.com/astaxie/beego"
	"github.com/beego/i18n"
)

func init() {
	//timezone set
	time.LoadLocation(beego.AppConfig.String("Timezone"))

	//beego log
	jlogs.InitLogs()
}

func main() {
	if beego.BConfig.RunMode == "dev" {
		beego.BConfig.WebConfig.DirectoryIndex = true
	}

	beego.AddFuncMap("i18n", i18n.Tr)

	beego.Run()
}
<commit_msg>add error<commit_after>package main

import (
	"time"

	"github.com/JREAMLU/core/global"
	"github.com/JREAMLU/core/jlogs"
	_ "github.com/JREAMLU/jkernel/base/routers"

	"github.com/astaxie/beego"
	"github.com/beego/i18n"
)

func init() {
	//timezone set
	time.LoadLocation(beego.AppConfig.String("Timezone"))

	//beego log
	jlogs.InitLogs()
}

func main() {
	if beego.BConfig.RunMode == "dev" {
		beego.BConfig.WebConfig.DirectoryIndex = true
	}

	beego.AddFuncMap("i18n", i18n.Tr)

	beego.ErrorController(&global.ErrorController{})
	beego.Run()
}
<|endoftext|>


<commit_before>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxApp returns the Resource schema describing a Convox App
func ResourceConvoxApp(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"generation": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
				ForceNew: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:             schema.TypeMap,
				Optional:         true,
				Sensitive:        true,
				DiffSuppressFunc: environmentDiffSuppress,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"balancers": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
		Create: ResourceConvoxAppCreateFactory(clientUnpacker),
		Read:   ResourceConvoxAppReadFactory(clientUnpacker),
		Update: ResourceConvoxAppUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxAppDeleteFactory(clientUnpacker),
	}
}

// ResourceConvoxAppCreateFactory builds the resource CreateFunc for a Convox App resource
func ResourceConvoxAppCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App CreateFunc: {{err}}", err)
		}

		name := d.Get("name").(string)
		generation := d.Get("generation").(string)
		if generation == "" {
			generation == "1"
		}

		app, err := c.CreateApp(name, generation)
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf(
				"Error creating app (%s): {{err}}", name), err)
		}

		d.SetId(app.Name)
		stateConf := &resource.StateChangeConf{
			Pending: []string{"creating"},
			Target:  []string{"running"},
			Refresh: appRefreshFunc(c, app.Name),
			Timeout: 10 * time.Minute,
			Delay:   25 * time.Second,
		}

		if _, err = stateConf.WaitForState(); err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Error waiting for app (%s) to be created: {{err}}", app.Name), err)
		}

		// and then run update to set it up
		return ResourceConvoxAppUpdateFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppReadFactory create the ReadFunc for a Convox App resource
func ResourceConvoxAppReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App ReadFunc: {{err}}", err)
		}

		app, err := c.GetApp(d.Get("name").(string))
		if err != nil {
			return err
		}
		d.SetId(app.Name)

		err = d.Set("status", app.Status)
		if err != nil {
			return fmt.Errorf("Error setting the status key: %s", err.Error())
		}

		params, err := c.ListParameters(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling ListParameters(%s): %s", app.Name, err.Error())
		}
		err = d.Set("params", params)
		if err != nil {
			return errwrap.Wrapf("Error while setting params: {{err}}", err)
		}

		env, err := c.GetEnvironment(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling GetEnvironment(%s): %s", app.Name, err.Error())
		}

		d.Set("environment", env)

		formation, err := c.ListFormation(app.Name)
		if err != nil {
			return errwrap.Wrapf("Error while reading formation from Convox API: {{err}}", err)
		}
		return readFormation(d, formation)
	}
}

// ResourceConvoxAppUpdateFactory builds the UpdateFunc for a Convox App resource
func ResourceConvoxAppUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		d.Partial(true)

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App UpdateFunc: {{err}}", err)
		}

		if err := setParams(c, d); err != nil {
			return err
		}
		d.SetPartial("params")

		if err := setEnv(c, d); err != nil {
			return err
		}
		d.SetPartial("environment")

		d.Partial(false)

		return ResourceConvoxAppReadFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppDeleteFactory builds the DeleteFunc for a Convox App resource
func ResourceConvoxAppDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App DeleteFunc: {{err}}", err)
		}
		_, err = c.DeleteApp(d.Id())
		return err
	}
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	balancers := make(map[string]string, len(v))

	for _, f := range v {
		balancers[f.Name] = f.Balancer
	}

	if err := d.Set("balancers", balancers); err != nil {
		return errwrap.Wrapf("Unable to store balancers from formation: {{err}}", err)
	}

	return nil
}

func setParams(c Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	if err := c.SetParameters(d.Id(), params); err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Error setting params (%#v) for %s: {{err}}", params, d.Id()), err)
	}

	return nil
}

func environmentDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
	// Return true if the diff should be suppressed, false to retain it.
	return strings.TrimSpace(old) == strings.TrimSpace(new)
}

func setEnv(c Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(client Client, name string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := client.GetApp(name)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<commit_msg>dont be dumb<commit_after>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxApp returns the Resource schema describing a Convox App
func ResourceConvoxApp(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"generation": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
				ForceNew: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:             schema.TypeMap,
				Optional:         true,
				Sensitive:        true,
				DiffSuppressFunc: environmentDiffSuppress,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"balancers": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
		Create: ResourceConvoxAppCreateFactory(clientUnpacker),
		Read:   ResourceConvoxAppReadFactory(clientUnpacker),
		Update: ResourceConvoxAppUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxAppDeleteFactory(clientUnpacker),
	}
}

// ResourceConvoxAppCreateFactory builds the resource CreateFunc for a Convox App resource
func ResourceConvoxAppCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App CreateFunc: {{err}}", err)
		}

		name := d.Get("name").(string)
		generation := d.Get("generation").(string)
		if generation == "" {
			generation = "1"
		}

		app, err := c.CreateApp(name, generation)
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf(
				"Error creating app (%s): {{err}}", name), err)
		}

		d.SetId(app.Name)
		stateConf := &resource.StateChangeConf{
			Pending: []string{"creating"},
			Target:  []string{"running"},
			Refresh: appRefreshFunc(c, app.Name),
			Timeout: 10 * time.Minute,
			Delay:   25 * time.Second,
		}

		if _, err = stateConf.WaitForState(); err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Error waiting for app (%s) to be created: {{err}}", app.Name), err)
		}

		// and then run update to set it up
		return ResourceConvoxAppUpdateFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppReadFactory create the ReadFunc for a Convox App resource
func ResourceConvoxAppReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App ReadFunc: {{err}}", err)
		}

		app, err := c.GetApp(d.Get("name").(string))
		if err != nil {
			return err
		}
		d.SetId(app.Name)

		err = d.Set("status", app.Status)
		if err != nil {
			return fmt.Errorf("Error setting the status key: %s", err.Error())
		}

		params, err := c.ListParameters(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling ListParameters(%s): %s", app.Name, err.Error())
		}
		err = d.Set("params", params)
		if err != nil {
			return errwrap.Wrapf("Error while setting params: {{err}}", err)
		}

		env, err := c.GetEnvironment(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling GetEnvironment(%s): %s", app.Name, err.Error())
		}

		d.Set("environment", env)

		formation, err := c.ListFormation(app.Name)
		if err != nil {
			return errwrap.Wrapf("Error while reading formation from Convox API: {{err}}", err)
		}
		return readFormation(d, formation)
	}
}

// ResourceConvoxAppUpdateFactory builds the UpdateFunc for a Convox App resource
func ResourceConvoxAppUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		d.Partial(true)

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App UpdateFunc: {{err}}", err)
		}

		if err := setParams(c, d); err != nil {
			return err
		}
		d.SetPartial("params")

		if err := setEnv(c, d); err != nil {
			return err
		}
		d.SetPartial("environment")

		d.Partial(false)

		return ResourceConvoxAppReadFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppDeleteFactory builds the DeleteFunc for a Convox App resource
func ResourceConvoxAppDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App DeleteFunc: {{err}}", err)
		}
		_, err = c.DeleteApp(d.Id())
		return err
	}
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	balancers := make(map[string]string, len(v))

	for _, f := range v {
		balancers[f.Name] = f.Balancer
	}

	if err := d.Set("balancers", balancers); err != nil {
		return errwrap.Wrapf("Unable to store balancers from formation: {{err}}", err)
	}

	return nil
}

func setParams(c Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	if err := c.SetParameters(d.Id(), params); err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Error setting params (%#v) for %s: {{err}}", params, d.Id()), err)
	}

	return nil
}

func environmentDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
	// Return true if the diff should be suppressed, false to retain it.
	return strings.TrimSpace(old) == strings.TrimSpace(new)
}

func setEnv(c Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(client Client, name string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := client.GetApp(name)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<|endoftext|>


<commit_before>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxApp returns the Resource schema describing a Convox App
func ResourceConvoxApp(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"generation": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "",
				ForceNew: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:             schema.TypeMap,
				Optional:         true,
				Sensitive:        true,
				DiffSuppressFunc: environmentDiffSuppress,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"balancers": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
		Create: ResourceConvoxAppCreateFactory(clientUnpacker),
		Read:   ResourceConvoxAppReadFactory(clientUnpacker),
		Update: ResourceConvoxAppUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxAppDeleteFactory(clientUnpacker),
	}
}

// ResourceConvoxAppCreateFactory builds the resource CreateFunc for a Convox App resource
func ResourceConvoxAppCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App CreateFunc: {{err}}", err)
		}

		name := d.Get("name").(string)
		generation := d.Get("generation").(string)
		if generation == "" {
			generation = "1"
		}

		app, err := c.CreateApp(name, generation)
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf(
				"Error creating app (%s): {{err}}", name), err)
		}

		d.SetId(app.Name)
		stateConf := &resource.StateChangeConf{
			Pending: []string{"creating"},
			Target:  []string{"running"},
			Refresh: appRefreshFunc(c, app.Name),
			Timeout: 10 * time.Minute,
			Delay:   25 * time.Second,
		}

		if _, err = stateConf.WaitForState(); err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Error waiting for app (%s) to be created: {{err}}", app.Name), err)
		}

		// and then run update to set it up
		return ResourceConvoxAppUpdateFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppReadFactory create the ReadFunc for a Convox App resource
func ResourceConvoxAppReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App ReadFunc: {{err}}", err)
		}

		app, err := c.GetApp(d.Get("name").(string))
		if err != nil {
			return err
		}
		d.SetId(app.Name)

		err = d.Set("status", app.Status)
		if err != nil {
			return fmt.Errorf("Error setting the status key: %s", err.Error())
		}

		params, err := c.ListParameters(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling ListParameters(%s): %s", app.Name, err.Error())
		}
		err = d.Set("params", params)
		if err != nil {
			return errwrap.Wrapf("Error while setting params: {{err}}", err)
		}

		env, err := c.GetEnvironment(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling GetEnvironment(%s): %s", app.Name, err.Error())
		}

		d.Set("environment", env)

		formation, err := c.ListFormation(app.Name)
		if err != nil {
			return errwrap.Wrapf("Error while reading formation from Convox API: {{err}}", err)
		}
		return readFormation(d, formation)
	}
}

// ResourceConvoxAppUpdateFactory builds the UpdateFunc for a Convox App resource
func ResourceConvoxAppUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		d.Partial(true)

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App UpdateFunc: {{err}}", err)
		}

		if err := setParams(c, d); err != nil {
			return err
		}
		d.SetPartial("params")

		if err := setEnv(c, d); err != nil {
			return err
		}
		d.SetPartial("environment")

		d.Partial(false)

		return ResourceConvoxAppReadFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppDeleteFactory builds the DeleteFunc for a Convox App resource
func ResourceConvoxAppDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App DeleteFunc: {{err}}", err)
		}
		_, err = c.DeleteApp(d.Id())
		return err
	}
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	balancers := make(map[string]string, len(v))

	for _, f := range v {
		balancers[f.Name] = f.Balancer
	}

	if err := d.Set("balancers", balancers); err != nil {
		return errwrap.Wrapf("Unable to store balancers from formation: {{err}}", err)
	}

	return nil
}

func setParams(c Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	if err := c.SetParameters(d.Id(), params); err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Error setting params (%#v) for %s: {{err}}", params, d.Id()), err)
	}

	return nil
}

func environmentDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
	// Return true if the diff should be suppressed, false to retain it.
	if old == new {
		return true
	}
	return strings.TrimSpace(old) == strings.TrimSpace(new)
}

func setEnv(c Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(client Client, name string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := client.GetApp(name)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<commit_msg>I think this should let us add `generation = "1"`<commit_after>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceConvoxApp returns the Resource schema describing a Convox App
func ResourceConvoxApp(clientUnpacker ClientUnpacker) *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"generation": &schema.Schema{
				Type:             schema.TypeString,
				Optional:         true,
				Default:          "1",
				ForceNew:         true,
				DiffSuppressFunc: generationDiffSuppress,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:             schema.TypeMap,
				Optional:         true,
				Sensitive:        true,
				DiffSuppressFunc: environmentDiffSuppress,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"balancers": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
		Create: ResourceConvoxAppCreateFactory(clientUnpacker),
		Read:   ResourceConvoxAppReadFactory(clientUnpacker),
		Update: ResourceConvoxAppUpdateFactory(clientUnpacker),
		Delete: ResourceConvoxAppDeleteFactory(clientUnpacker),
	}
}

// ResourceConvoxAppCreateFactory builds the resource CreateFunc for a Convox App resource
func ResourceConvoxAppCreateFactory(clientUnpacker ClientUnpacker) schema.CreateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App CreateFunc: {{err}}", err)
		}

		name := d.Get("name").(string)
		generation := d.Get("generation").(string)
		if generation == "" {
			generation = "1"
		}

		app, err := c.CreateApp(name, generation)
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf(
				"Error creating app (%s): {{err}}", name), err)
		}

		d.SetId(app.Name)
		stateConf := &resource.StateChangeConf{
			Pending: []string{"creating"},
			Target:  []string{"running"},
			Refresh: appRefreshFunc(c, app.Name),
			Timeout: 10 * time.Minute,
			Delay:   25 * time.Second,
		}

		if _, err = stateConf.WaitForState(); err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Error waiting for app (%s) to be created: {{err}}", app.Name), err)
		}

		// and then run update to set it up
		return ResourceConvoxAppUpdateFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppReadFactory create the ReadFunc for a Convox App resource
func ResourceConvoxAppReadFactory(clientUnpacker ClientUnpacker) schema.ReadFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App ReadFunc: {{err}}", err)
		}

		app, err := c.GetApp(d.Get("name").(string))
		if err != nil {
			return err
		}
		d.SetId(app.Name)

		err = d.Set("status", app.Status)
		if err != nil {
			return fmt.Errorf("Error setting the status key: %s", err.Error())
		}

		params, err := c.ListParameters(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling ListParameters(%s): %s", app.Name, err.Error())
		}
		err = d.Set("params", params)
		if err != nil {
			return errwrap.Wrapf("Error while setting params: {{err}}", err)
		}

		env, err := c.GetEnvironment(app.Name)
		if err != nil {
			return fmt.Errorf("Error calling GetEnvironment(%s): %s", app.Name, err.Error())
		}

		d.Set("environment", env)

		formation, err := c.ListFormation(app.Name)
		if err != nil {
			return errwrap.Wrapf("Error while reading formation from Convox API: {{err}}", err)
		}
		return readFormation(d, formation)
	}
}

// ResourceConvoxAppUpdateFactory builds the UpdateFunc for a Convox App resource
func ResourceConvoxAppUpdateFactory(clientUnpacker ClientUnpacker) schema.UpdateFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		d.Partial(true)

		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App UpdateFunc: {{err}}", err)
		}

		if err := setParams(c, d); err != nil {
			return err
		}
		d.SetPartial("params")

		if err := setEnv(c, d); err != nil {
			return err
		}
		d.SetPartial("environment")

		d.Partial(false)

		return ResourceConvoxAppReadFactory(clientUnpacker)(d, meta)
	}
}

// ResourceConvoxAppDeleteFactory builds the DeleteFunc for a Convox App resource
func ResourceConvoxAppDeleteFactory(clientUnpacker ClientUnpacker) schema.DeleteFunc {
	if clientUnpacker == nil {
		panic("clientUnpacker is required")
	}

	return func(d *schema.ResourceData, meta interface{}) error {
		c, err := clientUnpacker(d, meta)
		if err != nil {
			return errwrap.Wrapf("Error unpacking Convox client in App DeleteFunc: {{err}}", err)
		}
		_, err = c.DeleteApp(d.Id())
		return err
	}
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	balancers := make(map[string]string, len(v))

	for _, f := range v {
		balancers[f.Name] = f.Balancer
	}

	if err := d.Set("balancers", balancers); err != nil {
		return errwrap.Wrapf("Unable to store balancers from formation: {{err}}", err)
	}

	return nil
}

func setParams(c Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	if err := c.SetParameters(d.Id(), params); err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Error setting params (%#v) for %s: {{err}}", params, d.Id()), err)
	}

	return nil
}

func environmentDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
	// Return true if the diff should be suppressed, false to retain it.
	if old == new {
		return true
	}
	return strings.TrimSpace(old) == strings.TrimSpace(new)
}

func generationDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
	// Return true if the diff should be suppressed, false to retain it.
	if old == "" && new == "1" {
		return true
	}

	return old == new
}

func setEnv(c Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(client Client, name string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := client.GetApp(name)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<|endoftext|>


<commit_before>// The package internal provides a simple middleware that (a) prevents access
// to internal locations and (b) allows to return files from internal location
// by setting a special header, e.g. in a proxy response.
package internal

import (
	"net/http"

	"github.com/mholt/caddy/middleware"
)

// Internal middleware protects internal locations from external requests -
// but allows access from the inside by using a special HTTP header.
type Internal struct {
	Next  middleware.Handler
	Paths []string
}

const redirectHeader string = "X-Accel-Redirect"

func isInternalRedirect(w http.ResponseWriter) bool {
	return w.Header().Get(redirectHeader) != ""
}

// ServeHTTP implements the middlware.Handler interface.
func (i Internal) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {

	// Internal location requested? -> Not found.
	for _, prefix := range i.Paths {
		if middleware.Path(r.URL.Path).Matches(prefix) {
			return http.StatusNotFound, nil
		}
	}

	// Use internal response writer to ignore responses that will be
	// redirected to internal locations
	iw := internalResponseWriter{ResponseWriter: w}
	status, err := i.Next.ServeHTTP(iw, r)

	if isInternalRedirect(iw) && status < 400 {
		// Redirect - adapt request URL path and send it again
		// "down the chain"
		r.URL.Path = iw.Header().Get(redirectHeader)
		iw.ClearHeader()

		status, err = i.Next.ServeHTTP(iw, r)

		if isInternalRedirect(iw) {
			// multiple redirects not supported
			iw.ClearHeader()
			return http.StatusInternalServerError, nil
		}
	}

	return status, err
}

// internalResponseWriter wraps the underlying http.ResponseWriter and ignores
// calls to Write and WriteHeader if the response should be redirected to an
// internal location.
type internalResponseWriter struct {
	http.ResponseWriter
}

// ClearHeader removes all header fields that are already set.
func (w internalResponseWriter) ClearHeader() {
	for k := range w.Header() {
		w.Header().Del(k)
	}
}

// WriteHeader ignores the call if the response should be redirected to an
// internal location.
func (w internalResponseWriter) WriteHeader(code int) {
	if !isInternalRedirect(w) && code < 400 {
		w.ResponseWriter.WriteHeader(code)
	}
}

// Write ignores the call if the response should be redirected to an internal
// location.
func (w internalResponseWriter) Write(b []byte) (int, error) {
	if isInternalRedirect(w) {
		return 0, nil
	} else {
		return w.ResponseWriter.Write(b)
	}
}
<commit_msg>Adapted internal middleware<commit_after>// The package internal provides a simple middleware that (a) prevents access
// to internal locations and (b) allows to return files from internal location
// by setting a special header, e.g. in a proxy response.
package internal

import (
	"net/http"

	"github.com/mholt/caddy/middleware"
)

// Internal middleware protects internal locations from external requests -
// but allows access from the inside by using a special HTTP header.
type Internal struct {
	Next  middleware.Handler
	Paths []string
}

const redirectHeader string = "X-Accel-Redirect"

func isInternalRedirect(w http.ResponseWriter) bool {
	return w.Header().Get(redirectHeader) != ""
}

// ServeHTTP implements the middlware.Handler interface.
func (i Internal) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {

	// Internal location requested? -> Not found.
	for _, prefix := range i.Paths {
		if middleware.Path(r.URL.Path).Matches(prefix) {
			return http.StatusNotFound, nil
		}
	}

	// Use internal response writer to ignore responses that will be
	// redirected to internal locations
	iw := internalResponseWriter{ResponseWriter: w}
	status, err := i.Next.ServeHTTP(iw, r)

	for isInternalRedirect(iw) {
		// Redirect - adapt request URL path and send it again
		// "down the chain"
		r.URL.Path = iw.Header().Get(redirectHeader)
		iw.ClearHeader()

		status, err = i.Next.ServeHTTP(iw, r)
	}

	return status, err
}

// internalResponseWriter wraps the underlying http.ResponseWriter and ignores
// calls to Write and WriteHeader if the response should be redirected to an
// internal location.
type internalResponseWriter struct {
	http.ResponseWriter
}

// ClearHeader removes all header fields that are already set.
func (w internalResponseWriter) ClearHeader() {
	for k := range w.Header() {
		w.Header().Del(k)
	}
}

// WriteHeader ignores the call if the response should be redirected to an
// internal location.
func (w internalResponseWriter) WriteHeader(code int) {
	if !isInternalRedirect(w) {
		w.ResponseWriter.WriteHeader(code)
	}
}

// Write ignores the call if the response should be redirected to an internal
// location.
func (w internalResponseWriter) Write(b []byte) (int, error) {
	if isInternalRedirect(w) {
		return 0, nil
	} else {
		return w.ResponseWriter.Write(b)
	}
}
<|endoftext|>


<commit_before>// Package redirect is middleware for redirecting certain requests
// to other locations.
package redirect

import (
	"net/http"

	"github.com/mholt/caddy/middleware"
)

// New creates a new redirect middleware.
func New(c middleware.Controller) (middleware.Middleware, error) {
	var redirects []redirect

	for c.Next() {
		var rule redirect

		// From
		if !c.NextArg() {
			return nil, c.ArgErr()
		}
		rule.From = c.Val()

		// To
		if !c.NextArg() {
			return nil, c.ArgErr()
		}
		rule.To = c.Val()

		// Status Code
		if !c.NextArg() {
			return nil, c.ArgErr()
		}

		if code, ok := httpRedirs[c.Val()]; !ok {
			return nil, c.Err("Invalid redirect code '" + c.Val() + "'")
		} else {
			rule.Code = code
		}

		redirects = append(redirects, rule)
	}

	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			for _, rule := range redirects {
				if r.URL.Path == rule.From {
					http.Redirect(w, r, rule.To, rule.Code)
					break
				}
			}
			next(w, r)
		}
	}, nil
}

// redirect describes an HTTP redirect rule.
type redirect struct {
	From string
	To   string
	Code int
}

// httpRedirs is a list of supported HTTP redirect codes.
var httpRedirs = map[string]int{
	"300": 300,
	"301": 301,
	"302": 302,
	"303": 303,
	"304": 304,
	"305": 305,
	"306": 306,
	"307": 307,
	"308": 308,
}
<commit_msg>Refactored redir middleware to return errors<commit_after>// Package redirect is middleware for redirecting certain requests
// to other locations.
package redirect

import (
	"net/http"

	"github.com/mholt/caddy/middleware"
)

// New creates a new redirect middleware.
func New(c middleware.Controller) (middleware.Middleware, error) {
	var redirects []redirect

	for c.Next() {
		var rule redirect

		// From
		if !c.NextArg() {
			return nil, c.ArgErr()
		}
		rule.From = c.Val()

		// To
		if !c.NextArg() {
			return nil, c.ArgErr()
		}
		rule.To = c.Val()

		// Status Code
		if !c.NextArg() {
			return nil, c.ArgErr()
		}

		if code, ok := httpRedirs[c.Val()]; !ok {
			return nil, c.Err("Invalid redirect code '" + c.Val() + "'")
		} else {
			rule.Code = code
		}

		redirects = append(redirects, rule)
	}

	return func(next middleware.HandlerFunc) middleware.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) (int, error) {
			for _, rule := range redirects {
				if r.URL.Path == rule.From {
					http.Redirect(w, r, rule.To, rule.Code)
					break
				}
			}
			return next(w, r)
		}
	}, nil
}

// redirect describes an HTTP redirect rule.
type redirect struct {
	From string
	To   string
	Code int
}

// httpRedirs is a list of supported HTTP redirect codes.
var httpRedirs = map[string]int{
	"300": 300,
	"301": 301,
	"302": 302,
	"303": 303,
	"304": 304,
	"305": 305,
	"306": 306,
	"307": 307,
	"308": 308,
}
<|endoftext|>


<commit_before>package reform_test

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/jackc/pgx/stdlib"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
	_ "github.com/ziutek/mymysql/godrv"

	"github.com/AlekSi/pointer"
	"github.com/enodata/faker"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/mssql"
	"gopkg.in/reform.v1/dialects/mysql"
	"gopkg.in/reform.v1/dialects/postgresql"
	"gopkg.in/reform.v1/dialects/sqlite3"
	"gopkg.in/reform.v1/internal/test/models"
)

var (
	DB *reform.DB
)

func TestMain(m *testing.M) {
	driver := os.Getenv("REFORM_TEST_DRIVER")
	source := os.Getenv("REFORM_TEST_SOURCE")
	log.Printf("driver = %q, source = %q", driver, source)
	if driver == "" || source == "" {
		log.Fatal("no driver or source, set REFORM_TEST_DRIVER and REFORM_TEST_SOURCE")
	}

	db, err := sql.Open(driver, source)
	if err != nil {
		log.Fatal(err)
	}
	db.SetMaxIdleConns(1)
	db.SetMaxOpenConns(1)
	db.SetConnMaxLifetime(-1)
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	now := time.Now()
	log.Printf("time.Now()       = %s", now)
	log.Printf("time.Now().UTC() = %s", now.UTC())

	var dialect reform.Dialect
	switch driver {
	case "mysql", "mymysql":
		dialect = mysql.Dialect

		var tz string
		err = db.QueryRow("SHOW VARIABLES LIKE 'time_zone'").Scan(&tz, &tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("MySQL time_zone = %q", tz)

	case "postgres", "pgx":
		dialect = postgresql.Dialect

		var tz string
		err = db.QueryRow("SHOW TimeZone").Scan(&tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("PostgreSQL TimeZone = %q", tz)

	case "sqlite3":
		dialect = sqlite3.Dialect

		_, err = db.Exec("PRAGMA foreign_keys = ON")
		if err != nil {
			log.Fatal(err)
		}

	case "mssql":
		dialect = mssql.Dialect

	default:
		log.Fatal("reform: no dialect for driver " + driver)
	}

	DB = reform.NewDB(db, dialect, nil)

	os.Exit(m.Run())
}

func setIdentityInsert(t *testing.T, tx *reform.TX, table string, allow bool) {
	if tx.Dialect != mssql.Dialect {
		return
	}

	allowString := "OFF"
	if allow {
		allowString = "ON"
	}
	sql := fmt.Sprintf("SET IDENTITY_INSERT %s %s", tx.QuoteIdentifier(table), allowString)
	_, err := tx.Exec(sql)
	require.NoError(t, err)
}

type ReformSuite struct {
	suite.Suite
	q *reform.TX
}

func TestReformSuite(t *testing.T) {
	suite.Run(t, new(ReformSuite))
}

func (s *ReformSuite) SetupTest() {
	pl := reform.NewPrintfLogger(s.T().Logf)
	pl.LogTypes = true
	DB.Logger = pl

	var err error
	s.q, err = DB.Begin()
	s.Require().NoError(err)

	setIdentityInsert(s.T(), s.q, "people", false)
}

func (s *ReformSuite) TearDownTest() {
	// some transactional tests rollback and nilify transaction
	if s.q != nil {
		err := s.q.Rollback()
		s.Require().NoError(err)
	}
}

func (s *ReformSuite) RestartTransaction() {
	s.TearDownTest()
	s.SetupTest()
}

func (s *ReformSuite) TestStringer() {
	person, err := s.q.FindByPrimaryKeyFrom(models.PersonTable, 1)
	s.NoError(err)
	expected := "ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: <nil> (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)"
	s.Equal(expected, person.String())

	project, err := s.q.FindByPrimaryKeyFrom(models.ProjectTable, "baron")
	s.NoError(err)
	expected = "Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time)"
	s.Equal(expected, project.String())
}

func (s *ReformSuite) TestNeverNil() {
	project := new(models.Project)

	for i, v := range project.Values() {
		if v == nil {
			s.Fail("Value is nil", "%s %#v", models.ProjectTable.Columns()[i], v)
		}
	}

	for i, v := range project.Pointers() {
		if v == nil {
			s.Fail("Pointer is nil", "%s %#v", models.ProjectTable.Columns()[i], v)
		}
	}

	v := project.PKValue()
	if v == nil {
		s.Fail("PKValue is nil")
	}

	v = project.PKPointer()
	if v == nil {
		s.Fail("PKPointer is nil")
	}
}

func (s *ReformSuite) TestHasPK() {
	person := new(models.Person)
	project := new(models.Project)
	s.False(person.HasPK())
	s.False(project.HasPK())

	person.ID = 1
	project.ID = "id"
	s.True(person.HasPK())
	s.True(project.HasPK())
}

func (s *ReformSuite) TestPlaceholders() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("PostgreSQL-specific test")
	}

	s.Equal([]string{"$1", "$2", "$3", "$4", "$5"}, s.q.Placeholders(1, 5))
	s.Equal([]string{"$2", "$3", "$4", "$5", "$6"}, s.q.Placeholders(2, 5))
}

func (s *ReformSuite) TestInTransaction() {
	setIdentityInsert(s.T(), s.q, "people", true)

	err := s.q.Rollback()
	s.Require().NoError(err)
	s.q = nil

	person := &models.Person{ID: 42, Email: pointer.ToString(faker.Internet().Email())}

	err = DB.InTransaction(func(tx *reform.TX) error {
		err := tx.Insert(person)
		s.NoError(err)
		return errors.New("epic error")
	})
	s.EqualError(err, "epic error")

	s.Panics(func() {
		err = DB.InTransaction(func(tx *reform.TX) error {
			err := tx.Insert(person)
			s.NoError(err)
			panic("epic panic!")
		})
	})

	err = DB.InTransaction(func(tx *reform.TX) error {
		err := tx.Insert(person)
		s.NoError(err)
		return nil
	})
	s.NoError(err)

	err = DB.Insert(person)
	s.Error(err)

	err = DB.Delete(person)
	s.NoError(err)
}

func (s *ReformSuite) TestTimezones() {
	setIdentityInsert(s.T(), s.q, "people", true)

	t1 := time.Now()
	t2 := t1.UTC()
	vlat, err := time.LoadLocation("Asia/Vladivostok")
	s.NoError(err)
	tVLAT := t1.In(vlat)
	hst, err := time.LoadLocation("US/Hawaii")
	s.NoError(err)
	tHST := t1.In(hst)

	{
		q := fmt.Sprintf(`INSERT INTO people (id, name, created_at) VALUES `+
			`(11, '11', %s), (12, '12', %s), (13, '13', %s), (14, '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT created_at, created_at FROM people WHERE id IN (11, 12, 13, 14) ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var createdS string
			var createdT time.Time
			rows.Next()
			err = rows.Scan(&createdS, &createdT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, createdS, createdT)
		}

		err = rows.Close()
		s.NoError(err)
	}

	{
		q := fmt.Sprintf(`INSERT INTO projects (id, name, start) VALUES `+
			`('11', '11', %s), ('12', '12', %s), ('13', '13', %s), ('14', '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT start, start FROM projects WHERE id IN ('11', '12', '13', '14') ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)
		defer rows.Close()

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var startS string
			var startT time.Time
			rows.Next()
			err = rows.Scan(&startS, &startT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, startS, startT)
		}

		err = rows.Close()
		s.NoError(err)
	}
}
<commit_msg>Add test for database/sql.(*Rows).Columns().<commit_after>package reform_test

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/jackc/pgx/stdlib"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
	_ "github.com/ziutek/mymysql/godrv"

	"github.com/AlekSi/pointer"
	"github.com/enodata/faker"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/mssql"
	"gopkg.in/reform.v1/dialects/mysql"
	"gopkg.in/reform.v1/dialects/postgresql"
	"gopkg.in/reform.v1/dialects/sqlite3"
	. "gopkg.in/reform.v1/internal/test/models"
)

var (
	DB *reform.DB
)

func TestMain(m *testing.M) {
	driver := os.Getenv("REFORM_TEST_DRIVER")
	source := os.Getenv("REFORM_TEST_SOURCE")
	log.Printf("driver = %q, source = %q", driver, source)
	if driver == "" || source == "" {
		log.Fatal("no driver or source, set REFORM_TEST_DRIVER and REFORM_TEST_SOURCE")
	}

	db, err := sql.Open(driver, source)
	if err != nil {
		log.Fatal(err)
	}
	db.SetMaxIdleConns(1)
	db.SetMaxOpenConns(1)
	db.SetConnMaxLifetime(-1)
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	now := time.Now()
	log.Printf("time.Now()       = %s", now)
	log.Printf("time.Now().UTC() = %s", now.UTC())

	var dialect reform.Dialect
	switch driver {
	case "mysql", "mymysql":
		dialect = mysql.Dialect

		var tz string
		err = db.QueryRow("SHOW VARIABLES LIKE 'time_zone'").Scan(&tz, &tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("MySQL time_zone = %q", tz)

	case "postgres", "pgx":
		dialect = postgresql.Dialect

		var tz string
		err = db.QueryRow("SHOW TimeZone").Scan(&tz)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("PostgreSQL TimeZone = %q", tz)

	case "sqlite3":
		dialect = sqlite3.Dialect

		_, err = db.Exec("PRAGMA foreign_keys = ON")
		if err != nil {
			log.Fatal(err)
		}

	case "mssql":
		dialect = mssql.Dialect

	default:
		log.Fatal("reform: no dialect for driver " + driver)
	}

	DB = reform.NewDB(db, dialect, nil)

	os.Exit(m.Run())
}

func setIdentityInsert(t *testing.T, tx *reform.TX, table string, allow bool) {
	if tx.Dialect != mssql.Dialect {
		return
	}

	allowString := "OFF"
	if allow {
		allowString = "ON"
	}
	sql := fmt.Sprintf("SET IDENTITY_INSERT %s %s", tx.QuoteIdentifier(table), allowString)
	_, err := tx.Exec(sql)
	require.NoError(t, err)
}

type ReformSuite struct {
	suite.Suite
	q *reform.TX
}

func TestReformSuite(t *testing.T) {
	suite.Run(t, new(ReformSuite))
}

func (s *ReformSuite) SetupTest() {
	pl := reform.NewPrintfLogger(s.T().Logf)
	pl.LogTypes = true
	DB.Logger = pl

	var err error
	s.q, err = DB.Begin()
	s.Require().NoError(err)

	setIdentityInsert(s.T(), s.q, "people", false)
}

func (s *ReformSuite) TearDownTest() {
	// some transactional tests rollback and nilify transaction
	if s.q != nil {
		err := s.q.Rollback()
		s.Require().NoError(err)
	}
}

func (s *ReformSuite) RestartTransaction() {
	s.TearDownTest()
	s.SetupTest()
}

func (s *ReformSuite) TestStringer() {
	person, err := s.q.FindByPrimaryKeyFrom(PersonTable, 1)
	s.NoError(err)
	expected := "ID: 1 (int32), GroupID: 65534 (*int32), Name: `Denis Mills` (string), Email: <nil> (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)"
	s.Equal(expected, person.String())

	project, err := s.q.FindByPrimaryKeyFrom(ProjectTable, "baron")
	s.NoError(err)
	expected = "Name: `Vicious Baron` (string), ID: `baron` (string), Start: 2014-06-01 00:00:00 +0000 UTC (time.Time), End: 2016-02-21 00:00:00 +0000 UTC (*time.Time)"
	s.Equal(expected, project.String())
}

func (s *ReformSuite) TestNeverNil() {
	project := new(Project)

	for i, v := range project.Values() {
		if v == nil {
			s.Fail("Value is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	for i, v := range project.Pointers() {
		if v == nil {
			s.Fail("Pointer is nil", "%s %#v", ProjectTable.Columns()[i], v)
		}
	}

	v := project.PKValue()
	if v == nil {
		s.Fail("PKValue is nil")
	}

	v = project.PKPointer()
	if v == nil {
		s.Fail("PKPointer is nil")
	}
}

func (s *ReformSuite) TestHasPK() {
	person := new(Person)
	project := new(Project)
	s.False(person.HasPK())
	s.False(project.HasPK())

	person.ID = 1
	project.ID = "id"
	s.True(person.HasPK())
	s.True(project.HasPK())
}

func (s *ReformSuite) TestPlaceholders() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("PostgreSQL-specific test")
	}

	s.Equal([]string{"$1", "$2", "$3", "$4", "$5"}, s.q.Placeholders(1, 5))
	s.Equal([]string{"$2", "$3", "$4", "$5", "$6"}, s.q.Placeholders(2, 5))
}

func (s *ReformSuite) TestInTransaction() {
	setIdentityInsert(s.T(), s.q, "people", true)

	err := s.q.Rollback()
	s.Require().NoError(err)
	s.q = nil

	person := &Person{ID: 42, Email: pointer.ToString(faker.Internet().Email())}

	err = DB.InTransaction(func(tx *reform.TX) error {
		err := tx.Insert(person)
		s.NoError(err)
		return errors.New("epic error")
	})
	s.EqualError(err, "epic error")

	s.Panics(func() {
		err = DB.InTransaction(func(tx *reform.TX) error {
			err := tx.Insert(person)
			s.NoError(err)
			panic("epic panic!")
		})
	})

	err = DB.InTransaction(func(tx *reform.TX) error {
		err := tx.Insert(person)
		s.NoError(err)
		return nil
	})
	s.NoError(err)

	err = DB.Insert(person)
	s.Error(err)

	err = DB.Delete(person)
	s.NoError(err)
}

func (s *ReformSuite) TestTimezones() {
	setIdentityInsert(s.T(), s.q, "people", true)

	t1 := time.Now()
	t2 := t1.UTC()
	vlat, err := time.LoadLocation("Asia/Vladivostok")
	s.NoError(err)
	tVLAT := t1.In(vlat)
	hst, err := time.LoadLocation("US/Hawaii")
	s.NoError(err)
	tHST := t1.In(hst)

	{
		q := fmt.Sprintf(`INSERT INTO people (id, name, created_at) VALUES `+
			`(11, '11', %s), (12, '12', %s), (13, '13', %s), (14, '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT created_at, created_at FROM people WHERE id IN (11, 12, 13, 14) ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var createdS string
			var createdT time.Time
			rows.Next()
			err = rows.Scan(&createdS, &createdT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, createdS, createdT)
		}

		err = rows.Close()
		s.NoError(err)
	}

	{
		q := fmt.Sprintf(`INSERT INTO projects (id, name, start) VALUES `+
			`('11', '11', %s), ('12', '12', %s), ('13', '13', %s), ('14', '14', %s)`,
			s.q.Placeholder(1), s.q.Placeholder(2), s.q.Placeholder(3), s.q.Placeholder(4))
		_, err := s.q.Exec(q, t1, t2, tVLAT, tHST)
		s.NoError(err)

		q = `SELECT start, start FROM projects WHERE id IN ('11', '12', '13', '14') ORDER BY id`
		rows, err := s.q.Query(q)
		s.NoError(err)
		defer rows.Close()

		for _, t := range []time.Time{t1, t2, tVLAT, tHST} {
			var startS string
			var startT time.Time
			rows.Next()
			err = rows.Scan(&startS, &startT)
			s.NoError(err)
			log.Printf("%s read from database as %q and %s", t, startS, startT)
		}

		err = rows.Close()
		s.NoError(err)
	}
}

// database/sql.(*Rows).Columns() is not currently used, but may be useful in the future.
// Test is in place to track drivers supporting it.
func (s *ReformSuite) TestColumns() {
	rows, err := s.q.SelectRows(PersonTable, "WHERE name = "+s.q.Placeholder(1)+" ORDER BY id", "Elfrieda Abbott")
	s.NoError(err)
	s.Require().NotNil(rows)
	defer rows.Close()

	columns, err := rows.Columns()
	s.NoError(err)
	s.Equal(PersonTable.Columns(), columns)
}
<|endoftext|>


<commit_before>package hcsshim

import (
	"errors"
	"os"
	"path/filepath"
	"syscall"

	"github.com/Microsoft/go-winio"
)

type baseLayerWriter struct {
	root         string
	f            *os.File
	bw           *winio.BackupFileWriter
	err          error
	hasUtilityVM bool
}

func (w *baseLayerWriter) closeCurrentFile() error {
	if w.f != nil {
		err := w.bw.Close()
		err2 := w.f.Close()
		w.f = nil
		w.bw = nil
		if err != nil {
			return err
		}
		if err2 != nil {
			return err2
		}
	}
	return nil
}

func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) {
	defer func() {
		if err != nil {
			w.err = err
		}
	}()

	err = w.closeCurrentFile()
	if err != nil {
		return err
	}

	if filepath.ToSlash(name) == `UtilityVM/Files` {
		w.hasUtilityVM = true
	}

	path := filepath.Join(w.root, name)
	path, err = makeLongAbsPath(path)
	if err != nil {
		return err
	}

	var f *os.File
	defer func() {
		if f != nil {
			f.Close()
		}
	}()

	createmode := uint32(syscall.CREATE_NEW)
	if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
		err := os.Mkdir(path, 0)
		if err != nil && !os.IsExist(err) {
			return err
		}
		createmode = syscall.OPEN_EXISTING
	}

	mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
	f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
	if err != nil {
		return err
	}

	err = winio.SetFileBasicInfo(f, fileInfo)
	if err != nil {
		return err
	}

	w.f = f
	w.bw = winio.NewBackupFileWriter(f, true)
	f = nil
	return nil
}

func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
	defer func() {
		if err != nil {
			w.err = err
		}
	}()

	err = w.closeCurrentFile()
	if err != nil {
		return err
	}

	linkpath, err := makeLongAbsPath(filepath.Join(w.root, name))
	if err != nil {
		return err
	}

	linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
	if err != nil {
		return err
	}

	return os.Link(linktarget, linkpath)
}

func (w *baseLayerWriter) Remove(name string) error {
	return errors.New("base layer cannot have tombstones")
}

func (w *baseLayerWriter) Write(b []byte) (int, error) {
	n, err := w.bw.Write(b)
	if err != nil {
		w.err = err
	}
	return n, err
}

func (w *baseLayerWriter) Close() error {
	err := w.closeCurrentFile()
	if err != nil {
		return err
	}
	if w.err == nil {
		err = ProcessBaseLayer(w.root)
		if err != nil {
			return err
		}

		if w.hasUtilityVM {
			err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM"))
			if err != nil {
				return err
			}
		}
	}
	return w.err
}
<commit_msg>Preserve directory times when writing base layer<commit_after>package hcsshim

import (
	"errors"
	"os"
	"path/filepath"
	"syscall"

	"github.com/Microsoft/go-winio"
)

type baseLayerWriter struct {
	root         string
	f            *os.File
	bw           *winio.BackupFileWriter
	err          error
	hasUtilityVM bool
	dirInfo      []dirInfo
}

type dirInfo struct {
	path     string
	fileInfo winio.FileBasicInfo
}

func (w *baseLayerWriter) closeCurrentFile() error {
	if w.f != nil {
		err := w.bw.Close()
		err2 := w.f.Close()
		w.f = nil
		w.bw = nil
		if err != nil {
			return err
		}
		if err2 != nil {
			return err2
		}
	}
	return nil
}

func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) {
	defer func() {
		if err != nil {
			w.err = err
		}
	}()

	err = w.closeCurrentFile()
	if err != nil {
		return err
	}

	if filepath.ToSlash(name) == `UtilityVM/Files` {
		w.hasUtilityVM = true
	}

	path := filepath.Join(w.root, name)
	path, err = makeLongAbsPath(path)
	if err != nil {
		return err
	}

	var f *os.File
	defer func() {
		if f != nil {
			f.Close()
		}
	}()

	createmode := uint32(syscall.CREATE_NEW)
	if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
		err := os.Mkdir(path, 0)
		if err != nil && !os.IsExist(err) {
			return err
		}
		createmode = syscall.OPEN_EXISTING
		w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo})
	}

	mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
	f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
	if err != nil {
		return err
	}

	err = winio.SetFileBasicInfo(f, fileInfo)
	if err != nil {
		return err
	}

	w.f = f
	w.bw = winio.NewBackupFileWriter(f, true)
	f = nil
	return nil
}

func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
	defer func() {
		if err != nil {
			w.err = err
		}
	}()

	err = w.closeCurrentFile()
	if err != nil {
		return err
	}

	linkpath, err := makeLongAbsPath(filepath.Join(w.root, name))
	if err != nil {
		return err
	}

	linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
	if err != nil {
		return err
	}

	return os.Link(linktarget, linkpath)
}

func (w *baseLayerWriter) Remove(name string) error {
	return errors.New("base layer cannot have tombstones")
}

func (w *baseLayerWriter) Write(b []byte) (int, error) {
	n, err := w.bw.Write(b)
	if err != nil {
		w.err = err
	}
	return n, err
}

func (w *baseLayerWriter) Close() error {
	err := w.closeCurrentFile()
	if err != nil {
		return err
	}
	if w.err == nil {
		// Restore the file times of all the directories, since they may have
		// been modified by creating child directories.
		for i := range w.dirInfo {
			di := &w.dirInfo[len(w.dirInfo)-i-1]
			f, err := winio.OpenForBackup(di.path, uint32(syscall.GENERIC_READ|syscall.GENERIC_WRITE), syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING)
			if err != nil {
				return err
			}

			err = winio.SetFileBasicInfo(f, &di.fileInfo)
			f.Close()
			if err != nil {
				return err
			}
		}

		err = ProcessBaseLayer(w.root)
		if err != nil {
			return err
		}

		if w.hasUtilityVM {
			err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM"))
			if err != nil {
				return err
			}
		}
	}
	return w.err
}
<|endoftext|>


<commit_before>package fs

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
	"sort"
	"time"

	"github.com/spacemonkeygo/errors"
	"github.com/ugorji/go/codec"
	"polydawn.net/repeatr/def"
	"polydawn.net/repeatr/lib/fspatch"
)

type Metadata tar.Header

/*
	FileMode returns the permission and mode bits (setuid, setguid, sticky)
	in golang `os.FileMode` format.

	We decline to use `tar.Header.FileInfo()` because the `Mode()` method
	checks *both* the higher mode bits *and* the typeflag, which... seems strange, to
	say the least, since it can result in conflicting information.
	The bits emitted by this method are the only bits that make sense to use anyway --
	see `syscallMode` in stdlib file_posix.go as used in e.g. `os.Mkdir`.

	Note that if you're about to hand a number to a direct linux syscall, you do
	*not* want to use this.  `m.Mode&07777` is safe/correct instead.
*/
func (m Metadata) FileMode() os.FileMode {
	// Set file permission bits.  They're pretty universal.
	mode := os.FileMode(m.Mode).Perm()
	// Map suid, guid, and sticky -- they differ between go os and tar format.
	// Arbitrary constants from tar "spec" -- same as in archive/tar; they're unexported.
	if m.Mode&04000 != 0 {
		mode |= os.ModeSetuid
	}
	if m.Mode&02000 != 0 {
		mode |= os.ModeSetgid
	}
	if m.Mode&01000 != 0 {
		mode |= os.ModeSticky
	}
	return mode
}

/*
	Scan file attributes into a repeatr Metadata struct.  FileInfo
	may be provided if it is already available (this will save a stat call).
	The path is expected to exist (nonexistence is a panicable offense, along
	with all other IO errors).
*/
func ReadMetadata(path string, optional ...os.FileInfo) Metadata {
	// get fileinfo (or reuse, if the caller was kind enough to already have it)
	var fi os.FileInfo
	var err error
	if len(optional) > 0 {
		fi = optional[0]
	} else if len(optional) == 0 {
		fi, err = os.Lstat(path)
		if err != nil {
			// yes, also consider ENOEXIST a problem -- this is
			// never used in a context where a race between the tree
			// walker and the full attribute scan is considered acceptable.
			panic(errors.IOError.Wrap(err))
		}
	} else {
		panic(errors.ProgrammerError.New("optional fileinfo may only be one"))
	}
	// use stdlib tar to lift most of the metadata from fileinfo format
	hdr, err := tar.FileInfoHeader(fi, "")
	if err != nil {
		panic(errors.IOError.Wrap(err))
	}
	// handle extra metadata on the interesting types
	// not handled: hardlinks
	switch hdr.Typeflag {
	case tar.TypeSymlink:
		// readlink needs the file path again  ヽ(´ー｀)ノ
		if hdr.Linkname, err = os.Readlink(path); err != nil {
			panic(errors.IOError.Wrap(err))
		}
	case tar.TypeBlock, tar.TypeChar:
		hdr.Devmajor, hdr.Devminor = fspatch.ReadDev(fi)
	}
	// ctimes are uncontrollable, pave them (╯°□°）╯︵ ┻━┻
	// atimes mutate on read, pave them
	// we don't include these when hashing, but we wouldn't want uncontrolled bytes in a tar output either
	hdr.ChangeTime = def.Epochwhen
	hdr.AccessTime = def.Epochwhen
	return Metadata(*hdr)
}

/*
	Encodes the metadata as a CBOR map -- deterministically; the output is appropriate to feed to a hash and expect consistency.

	We follow the rfc7049 section 3.9 description of "canonical CBOR": namely, map keys are here entered consistently, and in sorted order.
	Except when maps are representing a struct; then it's deterministic order, but specified by (fairly arbitrary) hardcoded choices.

	Errors are panicked.
	(Note that if your writer ever returns an error, the codec library will panic with exactly that.  Yes, including `io.EOF`.)
*/
func (m Metadata) Marshal(out io.Writer) {
	// This doesn't implement `BinaryMarshaller` because we A: don't care and B: are invariably writing to another stream anyway.
	_, enc := codec.GenHelperEncoder(codec.NewEncoder(out, new(codec.CborHandle)))
	// Hack around codec not exporting things very usefully -.-
	const magic_UTF8 = 1
	// Count up how many fields we're about to encode.
	fieldCount := 7
	if m.Linkname != "" {
		fieldCount++
	}
	xattrsLen := len(m.Xattrs)
	if xattrsLen > 0 {
		fieldCount++
	}
	if m.Typeflag == tar.TypeBlock || m.Typeflag == tar.TypeChar {
		fieldCount += 2
	}
	// Let us begin!
	enc.EncodeMapStart(fieldCount)
	enc.EncodeString(magic_UTF8, "n") // name
	// use basename so hash subtrees are severable
	enc.EncodeString(magic_UTF8, filepath.Base(m.Name))
	// tar format magic numbers for file type  aren't particularly human readable but they're no more or less arbitrary than anyone else's
	enc.EncodeString(magic_UTF8, "t") // type
	enc.EncodeInt(int64(m.Typeflag))
	enc.EncodeString(magic_UTF8, "m") // mode -- note this is *not* `os.FileMode`, it's just the perm bits
	enc.EncodeInt(m.Mode & 07777)
	enc.EncodeString(magic_UTF8, "u") // uid
	enc.EncodeInt(int64(m.Uid))
	enc.EncodeString(magic_UTF8, "g") // gid
	enc.EncodeInt(int64(m.Gid))
	// skipped size because that's fairly redundant (and we never use hashes that are subject to length extension)
	if m.ModTime.IsZero() { // pretend that golang's zero time is unix epoch
		m.ModTime = time.Unix(0, 0)
	}
	enc.EncodeString(magic_UTF8, "tm") // modified time
	enc.EncodeInt(m.ModTime.Unix())
	enc.EncodeString(magic_UTF8, "tmn") // modified time, nano component
	enc.EncodeInt(int64(m.ModTime.Nanosecond()))
	// disregard atime and ctime because they are almost and completely unusable, respectively (change on read and unsettable)
	if m.Linkname != "" {
		enc.EncodeString(magic_UTF8, "l") // link name (optional)
		enc.EncodeString(magic_UTF8, m.Linkname)
	}
	// disregard uname and gname because they're not very helpful
	// dev numbers only if relevant
	if m.Typeflag == tar.TypeBlock || m.Typeflag == tar.TypeChar {
		enc.EncodeString(magic_UTF8, "dM") // dev major
		enc.EncodeInt(m.Devmajor)
		enc.EncodeString(magic_UTF8, "dm") // dev minor
		enc.EncodeInt(m.Devminor)
	}
	// Xattrs are a mite more complicated because we have to handle unknown keys:
	if xattrsLen > 0 {
		enc.EncodeString(magic_UTF8, "x")
		sorted := make([]stringPair, 0, xattrsLen)
		for k, v := range m.Xattrs {
			sorted = append(sorted, stringPair{k, v})
		}
		sort.Sort(sortableStringPair(sorted))
		enc.EncodeMapStart(xattrsLen)
		for _, line := range sorted {
			enc.EncodeString(magic_UTF8, line.a)
			enc.EncodeString(magic_UTF8, line.b)
		}
	}
	// There is no map-end to encode in cbor since we used the fixed-length map.  We're done.
}

type stringPair struct{ a, b string }
type sortableStringPair []stringPair

func (p sortableStringPair) Len() int           { return len(p) }
func (p sortableStringPair) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p sortableStringPair) Less(i, j int) bool { return p[i].a < p[j].a }
<commit_msg>Add comments.<commit_after>package fs

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
	"sort"
	"time"

	"github.com/spacemonkeygo/errors"
	"github.com/ugorji/go/codec"
	"polydawn.net/repeatr/def"
	"polydawn.net/repeatr/lib/fspatch"
)

type Metadata tar.Header

/*
	FileMode returns the permission and mode bits (setuid, setguid, sticky)
	in golang `os.FileMode` format.

	We decline to use `tar.Header.FileInfo()` because the `Mode()` method
	checks *both* the higher mode bits *and* the typeflag, which... seems strange, to
	say the least, since it can result in conflicting information.
	The bits emitted by this method are the only bits that make sense to use anyway --
	see `syscallMode` in stdlib file_posix.go as used in e.g. `os.Mkdir`.

	Note that if you're about to hand a number to a direct linux syscall, you do
	*not* want to use this.  `m.Mode&07777` is safe/correct instead.
*/
func (m Metadata) FileMode() os.FileMode {
	// Set file permission bits.  They're pretty universal.
	mode := os.FileMode(m.Mode).Perm()
	// Map suid, guid, and sticky -- they differ between go os and tar format.
	// Arbitrary constants from tar "spec" -- same as in archive/tar; they're unexported.
	if m.Mode&04000 != 0 {
		mode |= os.ModeSetuid
	}
	if m.Mode&02000 != 0 {
		mode |= os.ModeSetgid
	}
	if m.Mode&01000 != 0 {
		mode |= os.ModeSticky
	}
	return mode
}

/*
	Scan file attributes into a repeatr Metadata struct.  FileInfo
	may be provided if it is already available (this will save a stat call).
	The path is expected to exist (nonexistence is a panicable offense, along
	with all other IO errors).
*/
func ReadMetadata(path string, optional ...os.FileInfo) Metadata {
	// get fileinfo (or reuse, if the caller was kind enough to already have it)
	var fi os.FileInfo
	var err error
	if len(optional) > 0 {
		fi = optional[0]
	} else if len(optional) == 0 {
		fi, err = os.Lstat(path)
		if err != nil {
			// yes, also consider ENOEXIST a problem -- this is
			// never used in a context where a race between the tree
			// walker and the full attribute scan is considered acceptable.
			panic(errors.IOError.Wrap(err))
		}
	} else {
		panic(errors.ProgrammerError.New("optional fileinfo may only be one"))
	}
	// use stdlib tar to lift most of the metadata from fileinfo format
	hdr, err := tar.FileInfoHeader(fi, "")
	if err != nil {
		panic(errors.IOError.Wrap(err))
	}
	// handle extra metadata on the interesting types
	// not handled: hardlinks
	switch hdr.Typeflag {
	case tar.TypeSymlink:
		// readlink needs the file path again  ヽ(´ー｀)ノ
		if hdr.Linkname, err = os.Readlink(path); err != nil {
			panic(errors.IOError.Wrap(err))
		}
	case tar.TypeBlock, tar.TypeChar:
		hdr.Devmajor, hdr.Devminor = fspatch.ReadDev(fi)
	}
	// ctimes are uncontrollable, pave them (╯°□°）╯︵ ┻━┻
	// atimes mutate on read, pave them
	// we don't include these when hashing, but we wouldn't want uncontrolled bytes in a tar output either
	hdr.ChangeTime = def.Epochwhen
	hdr.AccessTime = def.Epochwhen
	return Metadata(*hdr)
}

/*
	Encodes the metadata as a CBOR map -- deterministically; the output is appropriate to feed to a hash and expect consistency.

	We follow the rfc7049 section 3.9 description of "canonical CBOR": namely, map keys are here entered consistently, and in sorted order.
	Except when maps are representing a struct; then it's deterministic order, but specified by (fairly arbitrary) hardcoded choices.

	Errors are panicked.
	(Note that if your writer ever returns an error, the codec library will panic with exactly that.  Yes, including `io.EOF`.)
*/
func (m Metadata) Marshal(out io.Writer) {
	// This doesn't implement `BinaryMarshaller` because we A: don't care and B: are invariably writing to another stream anyway.
	_, enc := codec.GenHelperEncoder(codec.NewEncoder(out, new(codec.CborHandle)))
	// Hack around codec not exporting things very usefully -.-
	const magic_UTF8 = 1
	// Count up how many fields we're about to encode.
	fieldCount := 7
	if m.Linkname != "" {
		fieldCount++
	}
	xattrsLen := len(m.Xattrs)
	if xattrsLen > 0 {
		fieldCount++
	}
	if m.Typeflag == tar.TypeBlock || m.Typeflag == tar.TypeChar {
		fieldCount += 2 // devmajor and devminor will be included for these types
	}
	// Let us begin!
	enc.EncodeMapStart(fieldCount)
	enc.EncodeString(magic_UTF8, "n") // name
	// use basename so hash subtrees are severable
	enc.EncodeString(magic_UTF8, filepath.Base(m.Name))
	// tar format magic numbers for file type  aren't particularly human readable but they're no more or less arbitrary than anyone else's
	enc.EncodeString(magic_UTF8, "t") // type
	enc.EncodeInt(int64(m.Typeflag))
	// mode bits whitelisted, matching `FileMode()`.  basic perms (0777) and setuid/setgid/sticky (07000) only.
	enc.EncodeString(magic_UTF8, "m") // mode -- note this is *not* `os.FileMode`, it's just the perm bits
	enc.EncodeInt(m.Mode & 07777)
	enc.EncodeString(magic_UTF8, "u") // uid
	enc.EncodeInt(int64(m.Uid))
	enc.EncodeString(magic_UTF8, "g") // gid
	enc.EncodeInt(int64(m.Gid))
	// skipped size because that's fairly redundant (and we never use hashes that are subject to length extension)
	if m.ModTime.IsZero() { // pretend that golang's zero time is unix epoch
		m.ModTime = time.Unix(0, 0)
	}
	enc.EncodeString(magic_UTF8, "tm") // modified time
	enc.EncodeInt(m.ModTime.Unix())
	enc.EncodeString(magic_UTF8, "tmn") // modified time, nano component
	enc.EncodeInt(int64(m.ModTime.Nanosecond()))
	// disregard atime and ctime because they are almost and completely unusable, respectively (change on read and unsettable)
	if m.Linkname != "" {
		enc.EncodeString(magic_UTF8, "l") // link name (optional)
		enc.EncodeString(magic_UTF8, m.Linkname)
	}
	// disregard uname and gname because they're not very helpful
	// dev numbers only if relevant
	if m.Typeflag == tar.TypeBlock || m.Typeflag == tar.TypeChar {
		enc.EncodeString(magic_UTF8, "dM") // dev major
		enc.EncodeInt(m.Devmajor)
		enc.EncodeString(magic_UTF8, "dm") // dev minor
		enc.EncodeInt(m.Devminor)
	}
	// Xattrs are a mite more complicated because we have to handle unknown keys:
	if xattrsLen > 0 {
		enc.EncodeString(magic_UTF8, "x")
		sorted := make([]stringPair, 0, xattrsLen)
		for k, v := range m.Xattrs {
			sorted = append(sorted, stringPair{k, v})
		}
		sort.Sort(sortableStringPair(sorted))
		enc.EncodeMapStart(xattrsLen)
		for _, line := range sorted {
			enc.EncodeString(magic_UTF8, line.a)
			enc.EncodeString(magic_UTF8, line.b)
		}
	}
	// There is no map-end to encode in cbor since we used the fixed-length map.  We're done.
}

type stringPair struct{ a, b string }
type sortableStringPair []stringPair

func (p sortableStringPair) Len() int           { return len(p) }
func (p sortableStringPair) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p sortableStringPair) Less(i, j int) bool { return p[i].a < p[j].a }
<|endoftext|>


<commit_before>package queue

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"

	"cloud.google.com/go/pubsub"
	"cloud.google.com/go/trace"
	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"

	"cred-alert/crypto"
	"cred-alert/metrics"
	"cred-alert/revok"
)

type pushEventProcessor struct {
	changeFetcher revok.ChangeFetcher
	verifier      crypto.Verifier
	clock         clock.Clock
	traceClient   *trace.Client

	verifyFailedCounter metrics.Counter
	endToEndGauge       metrics.Gauge
}

func NewPushEventProcessor(
	changeFetcher revok.ChangeFetcher,
	verifier crypto.Verifier,
	emitter metrics.Emitter,
	clock clock.Clock,
	traceClient *trace.Client,
) *pushEventProcessor {
	return &pushEventProcessor{
		changeFetcher: changeFetcher,
		verifier:      verifier,
		clock:         clock,
		traceClient:   traceClient,

		verifyFailedCounter: emitter.Counter("queue.push_event_processor.verify.failed"),
		endToEndGauge:       emitter.Gauge("queue.end-to-end.duration"),
	}
}

func (h *pushEventProcessor) Process(ctx context.Context, logger lager.Logger, message *pubsub.Message) (bool, error) {
	logger = logger.Session("processing-push-event")

	span := h.traceClient.NewSpan("io.pivotal.red.revok/CodePush")
	defer span.Finish()
	ctx = trace.NewContext(ctx, span)

	decodedSignature, err := base64.StdEncoding.DecodeString(message.Attributes["signature"])
	if err != nil {
		logger.Error("signature-malformed", err, lager.Data{
			"signature": message.Attributes["signature"],
		})
		return false, err
	}

	err = h.verifier.Verify(message.Data, decodedSignature)
	if err != nil {
		logger.Error("signature-invalid", err, lager.Data{
			"signature": message.Attributes["signature"],
		})
		h.verifyFailedCounter.Inc(logger)
		return false, err
	}

	decoder := json.NewDecoder(bytes.NewBuffer(message.Data))

	var p PushEventPlan
	err = decoder.Decode(&p)
	if err != nil {
		logger.Error("payload-malformed", err)
		return false, err
	}

	if p.Owner == "" || p.Repository == "" {
		err := errors.New("invalid payload: missing owner or repository")
		logger.Error("payload-incomplete", err)
		return false, err
	}

	logger = logger.WithData(lager.Data{
		"owner":      p.Owner,
		"repository": p.Repository,
	})
	span.SetLabel("owner", p.Owner)
	span.SetLabel("repository", p.Repository)

	err = h.changeFetcher.Fetch(ctx, logger, p.Owner, p.Repository, true)
	if err != nil {
		return true, err
	}

	duration := h.clock.Since(p.PushTime).Seconds()
	h.endToEndGauge.Update(logger, float32(duration))

	return false, nil
}
<commit_msg>refactor push event processor<commit_after>package queue

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"

	"cloud.google.com/go/pubsub"
	"cloud.google.com/go/trace"
	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"

	"cred-alert/crypto"
	"cred-alert/metrics"
	"cred-alert/revok"
)

type pushEventProcessor struct {
	changeFetcher revok.ChangeFetcher
	verifier      crypto.Verifier
	clock         clock.Clock
	traceClient   *trace.Client

	verifyFailedCounter metrics.Counter
	endToEndGauge       metrics.Gauge
}

func NewPushEventProcessor(
	changeFetcher revok.ChangeFetcher,
	verifier crypto.Verifier,
	emitter metrics.Emitter,
	clock clock.Clock,
	traceClient *trace.Client,
) *pushEventProcessor {
	return &pushEventProcessor{
		changeFetcher: changeFetcher,
		verifier:      verifier,
		clock:         clock,
		traceClient:   traceClient,

		verifyFailedCounter: emitter.Counter("queue.push_event_processor.verify.failed"),
		endToEndGauge:       emitter.Gauge("queue.end-to-end.duration"),
	}
}

func (proc *pushEventProcessor) Process(ctx context.Context, logger lager.Logger, message *pubsub.Message) (bool, error) {
	logger = logger.Session("processing-push-event")

	span := proc.traceClient.NewSpan("io.pivotal.red.revok/CodePush")
	defer span.Finish()
	ctx = trace.NewContext(ctx, span)

	if err := proc.checkSignature(ctx, logger, message); err != nil {
		return false, err
	}

	p, err := proc.decodeMessage(ctx, logger, message)
	if err != nil {
		return false, err
	}

	logger = logger.WithData(lager.Data{
		"owner":      p.Owner,
		"repository": p.Repository,
	})
	span.SetLabel("owner", p.Owner)
	span.SetLabel("repository", p.Repository)

	err = proc.changeFetcher.Fetch(ctx, logger, p.Owner, p.Repository, true)
	if err != nil {
		return true, err
	}

	duration := proc.clock.Since(p.PushTime).Seconds()
	proc.endToEndGauge.Update(logger, float32(duration))

	return false, nil
}

func (proc *pushEventProcessor) checkSignature(ctx context.Context, logger lager.Logger, message *pubsub.Message) error {
	decodedSignature, err := base64.StdEncoding.DecodeString(message.Attributes["signature"])
	if err != nil {
		logger.Error("signature-malformed", err, lager.Data{
			"signature": message.Attributes["signature"],
		})
		return err
	}

	err = proc.verifier.Verify(message.Data, decodedSignature)
	if err != nil {
		logger.Error("signature-invalid", err, lager.Data{
			"signature": message.Attributes["signature"],
		})
		proc.verifyFailedCounter.Inc(logger)
		return err
	}

	return nil
}

func (proc *pushEventProcessor) decodeMessage(ctx context.Context, logger lager.Logger, message *pubsub.Message) (PushEventPlan, error) {
	buffer := bytes.NewBuffer(message.Data)

	var p PushEventPlan
	if err := json.NewDecoder(buffer).Decode(&p); err != nil {
		logger.Error("payload-malformed", err)
		return p, err
	}

	if p.Owner == "" || p.Repository == "" {
		err := errors.New("invalid payload: missing owner or repository")
		logger.Error("payload-incomplete", err)
		return p, err
	}

	return p, nil
}
<|endoftext|>


<commit_before>package revel

import (
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"reflect"

	"github.com/golang/glog"
)

type Hotel struct {
	HotelId          int
	Name, Address    string
	City, State, Zip string
	Country          string
	Price            int
}

type Hotels struct {
	*Controller
}

type Static struct {
	*Controller
}

func (c Hotels) Show(id int) Result {
	title := "View Hotel"
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.Render(title, hotel)
}

func (c Hotels) Book(id int) Result {
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.RenderJson(hotel)
}

func (c Hotels) Index() Result {
	return c.RenderText("Hello, World!")
}

func (c Static) Serve(prefix, filepath string) Result {
	var basePath, dirName string

	if !path.IsAbs(dirName) {
		basePath = BasePath
	}

	fname := path.Join(basePath, prefix, filepath)
	file, err := os.Open(fname)
	if os.IsNotExist(err) {
		return c.NotFound("")
	} else if err != nil {
		glog.Warningf("Problem opening file (%s): %s ", fname, err)
		return c.NotFound("This was found but not sure why we couldn't open it.")
	}
	return c.RenderFile(file, "")
}

func startFakeBookingApp() {
	Init("prod", "github.com/robfig/revel/samples/booking", "")

	// TODO: Disable logging.

	runStartupHooks()

	MainRouter = NewRouter("")
	routesFile, _ := ioutil.ReadFile(filepath.Join(BasePath, "conf", "routes"))
	MainRouter.Routes, _ = parseRoutes("", string(routesFile), false)
	MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, path.Join(RevelPath, "templates")})
	MainTemplateLoader.Refresh()

	RegisterController((*Hotels)(nil),
		[]*MethodType{
			&MethodType{
				Name: "Index",
			},
			&MethodType{
				Name: "Show",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
				RenderArgNames: map[int][]string{31: []string{"title", "hotel"}},
			},
			&MethodType{
				Name: "Book",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
			},
		})

	RegisterController((*Static)(nil),
		[]*MethodType{
			&MethodType{
				Name: "Serve",
				Args: []*MethodArg{
					&MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
					&MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
				},
				RenderArgNames: map[int][]string{},
			},
		})
}
<commit_msg>Fix fakeapp<commit_after>package revel

import (
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"reflect"

	"github.com/golang/glog"
)

type Hotel struct {
	HotelId          int
	Name, Address    string
	City, State, Zip string
	Country          string
	Price            int
}

type Hotels struct {
	*Controller
}

type Static struct {
	*Controller
}

func (c Hotels) Show(id int) Result {
	title := "View Hotel"
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.Render(title, hotel)
}

func (c Hotels) Book(id int) Result {
	hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
	return c.RenderJson(hotel)
}

func (c Hotels) Index() Result {
	return c.RenderText("Hello, World!")
}

func (c Static) Serve(prefix, filepath string) Result {
	var basePath, dirName string

	if !path.IsAbs(dirName) {
		basePath = BasePath
	}

	fname := path.Join(basePath, prefix, filepath)
	file, err := os.Open(fname)
	if os.IsNotExist(err) {
		return c.NotFound("")
	} else if err != nil {
		glog.Warningf("Problem opening file (%s): %s ", fname, err)
		return c.NotFound("This was found but not sure why we couldn't open it.")
	}
	return c.RenderFile(file, "")
}

func startFakeBookingApp() {
	Init("prod", "github.com/robfig/revel/samples/booking", "")

	// TODO: Disable logging.

	runStartupHooks()

	MainRouter = NewRouter("")
	routesFile, _ := ioutil.ReadFile(filepath.Join(BasePath, "conf", "routes"))
	MainRouter.Routes, _ = parseRoutes("", string(routesFile), false)
	MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, path.Join(RevelPath, "templates")})
	MainTemplateLoader.Refresh()

	RegisterController((*Hotels)(nil),
		[]*MethodType{
			&MethodType{
				Name: "Index",
			},
			&MethodType{
				Name: "Show",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
				RenderArgNames: map[int][]string{32: []string{"title", "hotel"}},
			},
			&MethodType{
				Name: "Book",
				Args: []*MethodArg{
					{"id", reflect.TypeOf((*int)(nil))},
				},
			},
		})

	RegisterController((*Static)(nil),
		[]*MethodType{
			&MethodType{
				Name: "Serve",
				Args: []*MethodArg{
					&MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
					&MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
				},
				RenderArgNames: map[int][]string{},
			},
		})
}
<|endoftext|>