<commit_before>package server

import (
	"net"
	"sync"
	"time"

	"github.com/cashshuffle/cashshuffle/message"
	"github.com/nats-io/nuid"
)

const (
	// banTime is the amount of time to ban an IP.
	banTime = 15 * time.Minute

	// banScoreTick is the ban score increment on each pool ban.
	banScoreTick = 1

	// maxBanScore is the score the connection much reach to
	// be banned by IP.
	maxBanScore = 3
)

// Tracker is used to track connections to the server.
type Tracker struct {
	bannedIPs               map[string]*banData
	connections             map[net.Conn]*trackerData
	verificationKeys        map[string]net.Conn
	mutex                   sync.RWMutex
	pools                   map[int]map[uint32]*trackerData
	poolAmounts             map[int]uint64
	poolSizes               map[int]int
	poolVersions            map[int]uint64
	poolTypes               map[int]message.ShuffleType
	poolSize                int
	fullPools               map[int]interface{}
	shufflePort             int
	shuffleWebSocketPort    int
	torShufflePort          int
	torShuffleWebSocketPort int
}

// banData is the data required to track IP bans.
type banData struct {
	score uint32
}

// NewTracker instantiates a new tracker
func NewTracker(poolSize int, shufflePort int, shuffleWebSocketPort int, torShufflePort int, torShuffleWebSocketPort int) *Tracker {
	return &Tracker{
		poolSize:                poolSize,
		bannedIPs:               make(map[string]*banData),
		connections:             make(map[net.Conn]*trackerData),
		verificationKeys:        make(map[string]net.Conn),
		pools:                   make(map[int]map[uint32]*trackerData),
		poolAmounts:             make(map[int]uint64),
		poolSizes:               make(map[int]int),
		poolVersions:            make(map[int]uint64),
		poolTypes:               make(map[int]message.ShuffleType),
		fullPools:               make(map[int]interface{}),
		shufflePort:             shufflePort,
		shuffleWebSocketPort:    shuffleWebSocketPort,
		torShufflePort:          torShufflePort,
		torShuffleWebSocketPort: torShuffleWebSocketPort,
	}
}

// add adds a connection to the tracker.
func (t *Tracker) add(data *trackerData) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.verificationKeys[data.verificationKey] = data.conn

	data.sessionID = t.generateSessionID()

	t.connections[data.conn] = data

	data.pool, data.number = t.assignPool(data)
}

// remove removes the connection.
func (t *Tracker) remove(conn net.Conn) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	td := t.connections[conn]
	if td != nil {
		if td.verificationKey != "" {
			delete(t.verificationKeys, td.verificationKey)
		}

		t.unassignPool(td)

		delete(t.connections, conn)
	}
}

// banned returns true if the player has been banned.
func (t *Tracker) banned(data *trackerData) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return t.poolSizes[data.pool] <= (len(data.bannedBy) + 1)
}

// count returns the number of connections to the server.
func (t *Tracker) count() int {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return len(t.connections)
}

// bannedIP returns true if the player has been banned from the server.
func (t *Tracker) bannedIP(conn net.Conn) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())

	banData := t.bannedIPs[ip]
	if banData != nil && banData.score >= maxBanScore {
		return true
	}

	return false
}

// banIP bans an IP on the server.
func (t *Tracker) banIP(conn net.Conn) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())

	if _, ok := t.bannedIPs[ip]; ok {
		t.bannedIPs[ip].score += banScoreTick
	} else {
		t.bannedIPs[ip] = &banData{
			score: banScoreTick,
		}
	}

	go t.cleanupBan(ip)
}

// cleanupBan is the decrementer on the ban score and
// cleans up IPs that no longer need to be tracked.
func (t *Tracker) cleanupBan(ip string) {
	time.Sleep(banTime)

	t.mutex.Lock()
	defer t.mutex.Unlock()

	if _, ok := t.bannedIPs[ip]; ok {
		t.bannedIPs[ip].score -= banScoreTick
	}

	if t.bannedIPs[ip].score == 0 {
		delete(t.bannedIPs, ip)
	}
}

// getVerifcationKeyConn gets the connection for a verification key.
func (t *Tracker) getVerificationKeyData(key string) *trackerData {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if _, ok := t.verificationKeys[key]; ok {
		return t.connections[t.verificationKeys[key]]
	}

	return nil
}

// getTrackerdData returns trackerdata associated with a connection.
func (t *Tracker) getTrackerData(c net.Conn) *trackerData {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return t.connections[c]
}

// getPoolSize returns the pool size for the connection.
func (t *Tracker) getPoolSize(pool int) int {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return len(t.pools[pool])
}

// generateSessionID generates a unique session id.
// This method assumes the caller is holding the mutex.
func (t *Tracker) generateSessionID() []byte {
	n := nuid.New()

	return []byte(n.Next())
}

// assignPool assigns a user to a pool.
// This method assumes the caller is holding the mutex.
func (t *Tracker) assignPool(data *trackerData) (int, uint32) {
	num := 1

	for {
		if _, ok := t.pools[num]; ok {
			if t.poolAmounts[num] != data.amount {
				num = num + 1
				continue
			}

			if t.poolVersions[num] != data.version {
				num = num + 1
				continue
			}

			if t.poolTypes[num] != data.shuffleType {
				num = num + 1
				continue
			}

			if _, ok := t.fullPools[num]; ok {
				num = num + 1
				continue
			}
		}

		break
	}

	playerNum := uint32(1)
	if _, ok := t.pools[num]; !ok {
		t.pools[num] = make(map[uint32]*trackerData)
		t.pools[num][1] = data
		t.poolAmounts[num] = data.amount
		t.poolSizes[num] = t.poolSize
		t.poolVersions[num] = data.version
		t.poolTypes[num] = data.shuffleType
	} else {
		for {
			if _, ok := t.pools[num][playerNum]; ok {
				playerNum = playerNum + 1
				continue
			}

			break
		}

		t.pools[num][playerNum] = data
	}

	if len(t.pools[num]) == t.poolSize {
		t.fullPools[num] = nil
	}

	return num, playerNum
}

// decreasePoolSize decreases the pool size being
// tracked in trackerData after a blame occurs.
func (t *Tracker) decreasePoolSize(pool int) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	t.poolSizes[pool]--
}

// unassignPool removes a user from a pool.
// This method assumes the caller is holding the mutex.
func (t *Tracker) unassignPool(td *trackerData) {
	delete(t.pools[td.pool], td.number)

	if len(t.pools[td.pool]) == 0 {
		delete(t.pools, td.pool)
		delete(t.fullPools, td.pool)
		delete(t.poolAmounts, td.pool)
		delete(t.poolSizes, td.pool)
		delete(t.poolVersions, td.pool)
		delete(t.poolTypes, td.pool)
	}
}
<commit_msg>Fix find existing pool (#37)<commit_after>package server

import (
	"net"
	"sync"
	"time"

	"github.com/cashshuffle/cashshuffle/message"
	"github.com/nats-io/nuid"
)

const (
	// banTime is the amount of time to ban an IP.
	banTime = 15 * time.Minute

	// banScoreTick is the ban score increment on each pool ban.
	banScoreTick = 1

	// maxBanScore is the score the connection much reach to
	// be banned by IP.
	maxBanScore = 3

	// firstPoolNum is the starting number for pools
	firstPoolNum = 1

	// firstPlayerNum is the starting number for players in a pool
	firstPlayerNum = uint32(1)
)

// Tracker is used to track connections to the server.
type Tracker struct {
	bannedIPs               map[string]*banData
	connections             map[net.Conn]*trackerData
	verificationKeys        map[string]net.Conn
	mutex                   sync.RWMutex
	pools                   map[int]map[uint32]*trackerData
	poolAmounts             map[int]uint64
	poolSizes               map[int]int
	poolVersions            map[int]uint64
	poolTypes               map[int]message.ShuffleType
	poolSize                int
	fullPools               map[int]interface{}
	shufflePort             int
	shuffleWebSocketPort    int
	torShufflePort          int
	torShuffleWebSocketPort int
}

// banData is the data required to track IP bans.
type banData struct {
	score uint32
}

// NewTracker instantiates a new tracker
func NewTracker(poolSize int, shufflePort int, shuffleWebSocketPort int, torShufflePort int, torShuffleWebSocketPort int) *Tracker {
	return &Tracker{
		poolSize:                poolSize,
		bannedIPs:               make(map[string]*banData),
		connections:             make(map[net.Conn]*trackerData),
		verificationKeys:        make(map[string]net.Conn),
		pools:                   make(map[int]map[uint32]*trackerData),
		poolAmounts:             make(map[int]uint64),
		poolSizes:               make(map[int]int),
		poolVersions:            make(map[int]uint64),
		poolTypes:               make(map[int]message.ShuffleType),
		fullPools:               make(map[int]interface{}),
		shufflePort:             shufflePort,
		shuffleWebSocketPort:    shuffleWebSocketPort,
		torShufflePort:          torShufflePort,
		torShuffleWebSocketPort: torShuffleWebSocketPort,
	}
}

// add adds a connection to the tracker.
func (t *Tracker) add(data *trackerData) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.verificationKeys[data.verificationKey] = data.conn

	data.sessionID = t.generateSessionID()

	t.connections[data.conn] = data

	data.pool, data.number = t.assignPool(data)
}

// remove removes the connection.
func (t *Tracker) remove(conn net.Conn) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	td := t.connections[conn]
	if td != nil {
		if td.verificationKey != "" {
			delete(t.verificationKeys, td.verificationKey)
		}

		t.unassignPool(td)

		delete(t.connections, conn)
	}
}

// banned returns true if the player has been banned.
func (t *Tracker) banned(data *trackerData) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return t.poolSizes[data.pool] <= (len(data.bannedBy) + 1)
}

// count returns the number of connections to the server.
func (t *Tracker) count() int {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return len(t.connections)
}

// bannedIP returns true if the player has been banned from the server.
func (t *Tracker) bannedIP(conn net.Conn) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())

	banData := t.bannedIPs[ip]
	if banData != nil && banData.score >= maxBanScore {
		return true
	}

	return false
}

// banIP bans an IP on the server.
func (t *Tracker) banIP(conn net.Conn) {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())

	if _, ok := t.bannedIPs[ip]; ok {
		t.bannedIPs[ip].score += banScoreTick
	} else {
		t.bannedIPs[ip] = &banData{
			score: banScoreTick,
		}
	}

	go t.cleanupBan(ip)
}

// cleanupBan is the decrementer on the ban score and
// cleans up IPs that no longer need to be tracked.
func (t *Tracker) cleanupBan(ip string) {
	time.Sleep(banTime)

	t.mutex.Lock()
	defer t.mutex.Unlock()

	if _, ok := t.bannedIPs[ip]; ok {
		t.bannedIPs[ip].score -= banScoreTick
	}

	if t.bannedIPs[ip].score == 0 {
		delete(t.bannedIPs, ip)
	}
}

// getVerifcationKeyConn gets the connection for a verification key.
func (t *Tracker) getVerificationKeyData(key string) *trackerData {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if _, ok := t.verificationKeys[key]; ok {
		return t.connections[t.verificationKeys[key]]
	}

	return nil
}

// getTrackerdData returns trackerdata associated with a connection.
func (t *Tracker) getTrackerData(c net.Conn) *trackerData {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return t.connections[c]
}

// getPoolSize returns the pool size for the connection.
func (t *Tracker) getPoolSize(pool int) int {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	return len(t.pools[pool])
}

// generateSessionID generates a unique session id.
// This method assumes the caller is holding the mutex.
func (t *Tracker) generateSessionID() []byte {
	n := nuid.New()

	return []byte(n.Next())
}

// assignPool assigns a user to a pool.
// This method assumes the caller is holding the mutex.
func (t *Tracker) assignPool(data *trackerData) (int, uint32) {
	num, playerNum, found := t.getAvailableSlot(data)
	if !found {
		num = t.getEmptyPool()
		playerNum = firstPlayerNum
		t.pools[num] = make(map[uint32]*trackerData)
		t.poolAmounts[num] = data.amount
		t.poolSizes[num] = t.poolSize
		t.poolVersions[num] = data.version
		t.poolTypes[num] = data.shuffleType
	}
	t.pools[num][playerNum] = data

	if len(t.pools[num]) == t.poolSize {
		t.fullPools[num] = nil
	}

	return num, playerNum
}

// getAvailableSlot finds an existing pool and open player slot for player data
func (t *Tracker) getAvailableSlot(data *trackerData) (int, uint32, bool) {
	for num := range t.pools {
		if t.poolAmounts[num] != data.amount {
			continue
		}

		if t.poolVersions[num] != data.version {
			continue
		}

		if t.poolTypes[num] != data.shuffleType {
			continue
		}

		if _, ok := t.fullPools[num]; ok {
			continue
		}

		// find a slot in the available pool
		playerNum := firstPlayerNum
		for {
			if _, ok := t.pools[num][playerNum]; ok {
				playerNum = playerNum + 1
				continue
			}
			break
		}

		return num, playerNum, true
	}
	return 0, 0, false
}

// getEmptyPool finds the lowest empty pool number >=1
func (t *Tracker) getEmptyPool() int {
	num := firstPoolNum
	for {
		if _, ok := t.pools[num]; !ok {
			return num
		}
		num++
	}
}

// decreasePoolSize decreases the pool size being
// tracked in trackerData after a blame occurs.
func (t *Tracker) decreasePoolSize(pool int) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	t.poolSizes[pool]--
}

// unassignPool removes a user from a pool.
// This method assumes the caller is holding the mutex.
func (t *Tracker) unassignPool(td *trackerData) {
	delete(t.pools[td.pool], td.number)

	if len(t.pools[td.pool]) == 0 {
		delete(t.pools, td.pool)
		delete(t.fullPools, td.pool)
		delete(t.poolAmounts, td.pool)
		delete(t.poolSizes, td.pool)
		delete(t.poolVersions, td.pool)
		delete(t.poolTypes, td.pool)
	}
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"fmt"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/aandryashin/selenoid/config"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

type Docker struct {
	Ip      string
	Client  *client.Client
	Service *config.Browser
}

func (docker *Docker) StartWithCancel() (*url.URL, func(), error) {
	port, err := nat.NewPort("tcp", docker.Service.Port)
	if err != nil {
		return nil, nil, err
	}
	ctx := context.Background()
	log.Println("Creating Docker container", docker.Service.Image, "...")
	resp, err := docker.Client.ContainerCreate(ctx,
		&container.Config{
			Hostname:     "localhost",
			Image:        docker.Service.Image.(string),
			ExposedPorts: map[nat.Port]struct{}{port: struct{}{}},
		},
		&container.HostConfig{
			AutoRemove: true,
			PortBindings: nat.PortMap{
				port: []nat.PortBinding{nat.PortBinding{HostIP: "0.0.0.0"}},
			},
			ShmSize:    268435456,
			Privileged: true,
		},
		&network.NetworkingConfig{}, "")
	if err != nil {
		return nil, nil, fmt.Errorf("error creating container: %v", err)
	}
	log.Println("Starting container...")
	err = docker.Client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("error starting container: %v", err)
	}
	log.Printf("Container %s started\n", resp.ID)
	stat, err := docker.Client.ContainerInspect(ctx, resp.ID)
	if err != nil {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("unable to inspect container %s: %s\n", resp.ID, err)
	}
	_, ok := stat.NetworkSettings.Ports[port]
	if !ok {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("no bingings available for %v...\n", port)
	}
	if len(stat.NetworkSettings.Ports[port]) != 1 {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("error: wrong number of port bindings")
	}
	addr := stat.NetworkSettings.Ports[port][0]
	if docker.Ip == "" {
		_, err = os.Stat("/.dockerenv")
		if err != nil {
			addr.HostIP = "127.0.0.1"
		} else {
			addr.HostIP = "172.17.0.1"
		}
	} else {
		addr.HostIP = docker.Ip
	}
	host := fmt.Sprintf("http://%s:%s%s", addr.HostIP, addr.HostPort, docker.Service.Path)
	s := time.Now()
	err = wait(host, 10*time.Second)
	if err != nil {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, err
	}
	log.Println(time.Since(s))
	u, _ := url.Parse(host)
	log.Println("proxying requests to:", host)
	return u, func() { stopAndRemoveContainer(ctx, docker.Client, resp.ID) }, nil
}

func stopAndRemoveContainer(ctx context.Context, cli *client.Client, id string) {
	stopContainer(ctx, cli, id)
	removeContainer(ctx, cli, id)
}

func removeContainer(ctx context.Context, cli *client.Client, id string) {
	fmt.Println("Removing container", id)
	err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
	if err != nil {
		fmt.Println("error: unable to remove container", id, err)
		return
	}
	fmt.Printf("Container %s removed\n", id)
}

func stopContainer(ctx context.Context, cli *client.Client, id string) {
	fmt.Println("Stopping container", id)
	err := cli.ContainerStop(ctx, id, nil)
	if err != nil {
		log.Println("error: unable to stop container", id, err)
		return
	}
	cli.ContainerWait(ctx, id)
	fmt.Printf("Container %s stopped\n", id)
	removeContainer(ctx, cli, id)
}
<commit_msg>Fixed duplicated remove.<commit_after>package service

import (
	"context"
	"fmt"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/aandryashin/selenoid/config"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

type Docker struct {
	Ip      string
	Client  *client.Client
	Service *config.Browser
}

func (docker *Docker) StartWithCancel() (*url.URL, func(), error) {
	port, err := nat.NewPort("tcp", docker.Service.Port)
	if err != nil {
		return nil, nil, err
	}
	ctx := context.Background()
	log.Println("Creating Docker container", docker.Service.Image, "...")
	resp, err := docker.Client.ContainerCreate(ctx,
		&container.Config{
			Hostname:     "localhost",
			Image:        docker.Service.Image.(string),
			ExposedPorts: map[nat.Port]struct{}{port: struct{}{}},
		},
		&container.HostConfig{
			AutoRemove: true,
			PortBindings: nat.PortMap{
				port: []nat.PortBinding{nat.PortBinding{HostIP: "0.0.0.0"}},
			},
			ShmSize:    268435456,
			Privileged: true,
		},
		&network.NetworkingConfig{}, "")
	if err != nil {
		return nil, nil, fmt.Errorf("error creating container: %v", err)
	}
	log.Println("Starting container...")
	err = docker.Client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("error starting container: %v", err)
	}
	log.Printf("Container %s started\n", resp.ID)
	stat, err := docker.Client.ContainerInspect(ctx, resp.ID)
	if err != nil {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("unable to inspect container %s: %s\n", resp.ID, err)
	}
	_, ok := stat.NetworkSettings.Ports[port]
	if !ok {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("no bingings available for %v...\n", port)
	}
	if len(stat.NetworkSettings.Ports[port]) != 1 {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, fmt.Errorf("error: wrong number of port bindings")
	}
	addr := stat.NetworkSettings.Ports[port][0]
	if docker.Ip == "" {
		_, err = os.Stat("/.dockerenv")
		if err != nil {
			addr.HostIP = "127.0.0.1"
		} else {
			addr.HostIP = "172.17.0.1"
		}
	} else {
		addr.HostIP = docker.Ip
	}
	host := fmt.Sprintf("http://%s:%s%s", addr.HostIP, addr.HostPort, docker.Service.Path)
	s := time.Now()
	err = wait(host, 10*time.Second)
	if err != nil {
		stopAndRemoveContainer(ctx, docker.Client, resp.ID)
		return nil, nil, err
	}
	log.Println(time.Since(s))
	u, _ := url.Parse(host)
	log.Println("proxying requests to:", host)
	return u, func() { stopAndRemoveContainer(ctx, docker.Client, resp.ID) }, nil
}

func stopAndRemoveContainer(ctx context.Context, cli *client.Client, id string) {
	stopContainer(ctx, cli, id)
	removeContainer(ctx, cli, id)
}

func removeContainer(ctx context.Context, cli *client.Client, id string) {
	fmt.Println("Removing container", id)
	err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
	if err != nil {
		fmt.Println("error: unable to remove container", id, err)
		return
	}
	fmt.Printf("Container %s removed\n", id)
}

func stopContainer(ctx context.Context, cli *client.Client, id string) {
	fmt.Println("Stopping container", id)
	err := cli.ContainerStop(ctx, id, nil)
	if err != nil {
		log.Println("error: unable to stop container", id, err)
		return
	}
	cli.ContainerWait(ctx, id)
	fmt.Printf("Container %s stopped\n", id)
}
<|endoftext|>


<commit_before>package apigee

import (
	"fmt"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/zambien/go-apigee-edge"
	"log"
	"strings"
	"testing"
)

func TestAccProduct_Updated(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckProductDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckProductConfigRequired,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckProductExists("apigee_product.foo_product", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "name", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "display_name", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "approval_type", "manual"),
				),
			},

			resource.TestStep{
				Config: testAccCheckProductConfigUpdated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckProductExists("apigee_product.foo_product", "foo_product_updated"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "name", "foo_product_updated"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "display_name", "foo_product_updated_different"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "description", "no one ever fills this out"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "approval_type", "auto"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "api_resources.0", "/**"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "proxies.0", "tf_helloworld"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota", "1000"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota_interval", "2"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "scopes.0", "READ"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota_time_unit", "minute"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.access", "public"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.custom1", "customval1"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.custom2", "customval2"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "environments.0", "test"),
				),
			},
		},
	})
}

func testAccCheckProductDestroy(s *terraform.State) error {

	client := testAccProvider.Meta().(*apigee.EdgeClient)

	if err := productDestroyHelper(s, client); err != nil {
		return err
	}
	return nil
}

func testAccCheckProductExists(n string, name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		client := testAccProvider.Meta().(*apigee.EdgeClient)
		if err := productExistsHelper(s, client, name); err != nil {
			log.Printf("Error in testAccCheckProductExists: %s", err)
			return err
		}
		return nil
	}
}

const testAccCheckProductConfigRequired = `
resource "apigee_api_proxy" "tf_helloworld" {
   name  		= "tf_helloworld"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
}

resource "apigee_product" "foo_product" {
   name = "foo_product"
   approval_type = "manual"
}
`

const testAccCheckProductConfigUpdated = `
resource "apigee_api_proxy" "tf_helloworld" {
   name  		= "tf_helloworld"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
}

resource "apigee_product" "foo_product" {
   name = "foo_product_updated"
   display_name = "foo_product_updated_different"
   description = "no one ever fills this out"
   approval_type = "auto"

   api_resources = ["/**"]
   proxies = ["${apigee_api_proxy.tf_helloworld.name}"]

   quota = "1000"
   quota_interval = "2"
   quota_time_unit = "minute"

   scopes = ["READ"]

   attributes = {
      access = "public"

      custom1 = "customval1"
      custom2 = "customval2"
   }
   environments = ["test"]
}
`

func productDestroyHelper(s *terraform.State, client *apigee.EdgeClient) error {

	for _, r := range s.RootModule().Resources {
		id := r.Primary.ID

		if id == "" {
			return fmt.Errorf("No product ID is set")
		}

		_, _, err := client.Products.Get("foo_product")

		if err != nil {
			if strings.Contains(err.Error(), "404 ") {
				return nil
			}
			return fmt.Errorf("Received an error retrieving product  %+v\n", err)
		}
	}

	return fmt.Errorf("Product still exists")
}

func productExistsHelper(s *terraform.State, client *apigee.EdgeClient, name string) error {

	for _, r := range s.RootModule().Resources {
		id := r.Primary.ID

		if id == "" {
			return fmt.Errorf("No product ID is set")
		}

		if productData, _, err := client.Products.Get(name); err != nil {
			return fmt.Errorf("Received an error retrieving product  %+v\n", productData)
		} else {
			log.Printf("Created product name: %s", productData.Name)
		}

	}
	return nil
}
<commit_msg>reproduce proxy order issue in product - failing test case<commit_after>package apigee

import (
	"fmt"
	"log"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/zambien/go-apigee-edge"
)

func TestAccProduct_Updated(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckProductDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckProductConfigRequired,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckProductExists("apigee_product.foo_product", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "name", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "display_name", "foo_product"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "approval_type", "manual"),
				),
			},

			resource.TestStep{
				Config: testAccCheckProductConfigUpdated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckProductExists("apigee_product.foo_product", "foo_product_updated"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "name", "foo_product_updated"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "display_name", "foo_product_updated_different"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "description", "no one ever fills this out"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "approval_type", "auto"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "api_resources.0", "/**"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "proxies.0", "tf_helloworld"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota", "1000"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota_interval", "2"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "scopes.0", "READ"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "quota_time_unit", "minute"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.access", "public"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.custom1", "customval1"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "attributes.custom2", "customval2"),
					resource.TestCheckResourceAttr(
						"apigee_product.foo_product", "environments.0", "test"),
				),
			},
		},
	})
}

func testAccCheckProductDestroy(s *terraform.State) error {

	client := testAccProvider.Meta().(*apigee.EdgeClient)

	if err := productDestroyHelper(s, client); err != nil {
		return err
	}
	return nil
}

func testAccCheckProductExists(n string, name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		client := testAccProvider.Meta().(*apigee.EdgeClient)
		if err := productExistsHelper(s, client, name); err != nil {
			log.Printf("Error in testAccCheckProductExists: %s", err)
			return err
		}
		return nil
	}
}

const testAccCheckProductConfigRequired = `
resource "apigee_api_proxy" "tf_helloworld" {
   name  		= "tf_helloworld"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
}

resource "apigee_product" "foo_product" {
   name = "foo_product"
   approval_type = "manual"
}
`

const testAccCheckProductConfigUpdated = `
resource "apigee_api_proxy" "tf_helloworld" {
   name  		= "tf_helloworld"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
}

resource "apigee_api_proxy" "tf_helloworld_2" {
   name  		= "tf_helloworld_2"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
 }

resource "apigee_api_proxy" "tf_helloworld_3" {
   name  		= "tf_helloworld_3"
   bundle       = "test-fixtures/helloworld_proxy.zip"
   bundle_sha   = "${filebase64sha256("test-fixtures/helloworld_proxy.zip")}"
}

resource "apigee_product" "foo_product" {
   name = "foo_product_updated"
   display_name = "foo_product_updated_different"
   description = "no one ever fills this out"
   approval_type = "auto"

   api_resources = ["/**"]
   proxies = ["${apigee_api_proxy.tf_helloworld.name}", "${apigee_api_proxy.tf_helloworld_2.name}", "${apigee_api_proxy.tf_helloworld_3.name}"]

   quota = "1000"
   quota_interval = "2"
   quota_time_unit = "minute"

   scopes = ["READ"]

   attributes = {
      access = "public"

      custom1 = "customval1"
      custom2 = "customval2"
   }
   environments = ["test"]
}
`

func productDestroyHelper(s *terraform.State, client *apigee.EdgeClient) error {

	for _, r := range s.RootModule().Resources {
		id := r.Primary.ID

		if id == "" {
			return fmt.Errorf("No product ID is set")
		}

		_, _, err := client.Products.Get("foo_product")

		if err != nil {
			if strings.Contains(err.Error(), "404 ") {
				return nil
			}
			return fmt.Errorf("Received an error retrieving product  %+v\n", err)
		}
	}

	return fmt.Errorf("Product still exists")
}

func productExistsHelper(s *terraform.State, client *apigee.EdgeClient, name string) error {

	for _, r := range s.RootModule().Resources {
		id := r.Primary.ID

		if id == "" {
			return fmt.Errorf("No product ID is set")
		}

		if productData, _, err := client.Products.Get(name); err != nil {
			return fmt.Errorf("Received an error retrieving product  %+v\n", productData)
		} else {
			log.Printf("Created product name: %s", productData.Name)
		}

	}
	return nil
}
<|endoftext|>


<commit_before>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"path"
	"regexp"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

const (
	// ManifestDocType is manifest type
	ManifestDocType = "io.cozy.manifests"
	// ManifestMaxSize is the manifest maximum size
	ManifestMaxSize = 2 << (2 * 10) // 2MB
)

// AppsDirectory is the name of the directory in which apps are stored
const AppsDirectory = "/_cozyapps"

// ManifestFilename is the name of the manifest at the root of the
// application directory
const ManifestFilename = "manifest.webapp"

// State is the state of the application
type State string

const (
	// Available state
	Available State = "available"
	// Installing state
	Installing = "installing"
	// Upgrading state
	Upgrading = "upgrading"
	// Uninstalling state
	Uninstalling = "uninstalling"
	// Errored state
	Errored = "errored"
	// Ready state
	Ready = "ready"
)

var slugReg = regexp.MustCompile(`^[A-Za-z0-9\-]+$`)

// Access is a string representing the access permission level. It can
// either be read, write or readwrite.
type Access string

// Permissions is a map of key, a description and an access level.
type Permissions map[string]*struct {
	Description string `json:"description"`
	Access      Access `json:"access"`
}

// Developer is the name and url of a developer.
type Developer struct {
	Name string `json:"name"`
	URL  string `json:"url,omitempty"`
}

// Manifest contains all the informations about an application.
type Manifest struct {
	ManRev string `json:"_rev,omitempty"` // Manifest revision

	Name        string     `json:"name"`
	Slug        string     `json:"slug"`
	Source      string     `json:"source"`
	State       State      `json:"state"`
	Icon        string     `json:"icon"`
	Description string     `json:"description"`
	Developer   *Developer `json:"developer"`

	DefaultLocal string `json:"default_locale"`
	Locales      map[string]*struct {
		Description string `json:"description"`
	} `json:"locales"`

	Version     string       `json:"version"`
	License     string       `json:"license"`
	Permissions *Permissions `json:"permissions"`
}

// ID returns the manifest identifier - see couchdb.Doc interface
func (m *Manifest) ID() string {
	return ManifestDocType + "/" + m.Slug
}

// Rev return the manifest revision - see couchdb.Doc interface
func (m *Manifest) Rev() string { return m.ManRev }

// DocType returns the manifest doctype - see couchdb.Doc interfaces
func (m *Manifest) DocType() string { return ManifestDocType }

// SetID is used to change the file identifier - see couchdb.Doc
// interface
func (m *Manifest) SetID(id string) {}

// SetRev is used to change the file revision - see couchdb.Doc
// interface
func (m *Manifest) SetRev(rev string) { m.ManRev = rev }

// SelfLink is used to generate a JSON-API link for the file - see
// jsonapi.Object interface
func (m *Manifest) SelfLink() string { return "/apps/" + m.Slug }

// Relationships is used to generate the parent relationship in JSON-API format
// - see jsonapi.Object interface
func (m *Manifest) Relationships() jsonapi.RelationshipMap {
	return jsonapi.RelationshipMap{}
}

// Included is part of the jsonapi.Object interface
func (m *Manifest) Included() []jsonapi.Object {
	return []jsonapi.Object{}
}

// Client interface should be implemented by the underlying transport
// used to fetch the application data.
type Client interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest() (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(vfsC vfs.Context, appdir string) error
}

// List returns the list of installed applications.
//
// TODO: pagination
func List(db couchdb.Database) ([]*Manifest, error) {
	var docs []*Manifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, ManifestDocType, req, &docs)
	if err != nil {
		return nil, err
	}
	return docs, nil
}

// GetBySlug returns an app identified by its slug
func GetBySlug(db couchdb.Database, slug string) (*Manifest, error) {
	man := &Manifest{}
	err := couchdb.GetDoc(db, ManifestDocType, slug, man)
	if err != nil {
		return nil, err
	}
	return man, nil
}

// Installer is used to install or update applications.
type Installer struct {
	cli Client

	vfsC vfs.Context

	slug string
	src  string
	man  *Manifest

	err  error
	errc chan error
	manc chan *Manifest
}

// NewInstaller creates a new Installer
func NewInstaller(vfsC vfs.Context, slug, src string) (*Installer, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	parsedSrc, err := url.Parse(src)
	if err != nil {
		return nil, err
	}

	var cli Client
	switch parsedSrc.Scheme {
	case "git":
		cli = newGitClient(vfsC, src)
	default:
		err = ErrNotSupportedSource
	}

	if err != nil {
		return nil, err
	}

	inst := &Installer{
		cli:  cli,
		vfsC: vfsC,

		slug: slug,
		src:  src,

		errc: make(chan error),
		manc: make(chan *Manifest),
	}

	return inst, err
}

// Install will install the application linked to the installer. It
// will report its progress or error using the WaitManifest method.
func (i *Installer) Install() (newman *Manifest, err error) {
	if i.err != nil {
		return nil, i.err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		}
	}()

	_, err = i.getOrCreateManifest(i.src, i.slug)
	if err != nil {
		return
	}

	oldman := i.man
	if s := oldman.State; s != Available && s != Errored {
		return nil, ErrBadState
	}

	newman = &(*oldman)
	newman.State = Installing

	defer func() {
		if err != nil {
			newman.State = Errored
			i.updateManifest(newman)
		}
	}()

	err = i.updateManifest(newman)
	if err != nil {
		return
	}

	appdir := path.Join(AppsDirectory, newman.Slug)
	err = vfs.MkdirAll(i.vfsC, appdir)
	if err != nil {
		return
	}

	err = i.cli.Fetch(i.vfsC, appdir)
	if err != nil {
		return
	}

	newman.State = Ready
	err = i.updateManifest(newman)
	if err != nil {
		return
	}

	return
}

func (i *Installer) handleErr(err error) error {
	if i.err == nil {
		i.err = err
		i.errc <- err
	}
	return i.err
}

func (i *Installer) getOrCreateManifest(src, slug string) (man *Manifest, err error) {
	if i.err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		} else {
			i.man = man
		}
	}()

	if i.man != nil {
		panic("Manifest is already defined")
	}

	man, err = GetBySlug(i.vfsC, slug)
	if err != nil && !couchdb.IsNotFoundError(err) {
		return nil, err
	}
	if err == nil {
		return man, nil
	}

	r, err := i.cli.FetchManifest()
	if err != nil {
		return nil, err
	}

	defer r.Close()
	man = &Manifest{}
	err = json.NewDecoder(io.LimitReader(r, ManifestMaxSize)).Decode(&man)
	if err != nil {
		return nil, ErrBadManifest
	}

	man.Slug = slug
	man.Source = src
	man.State = Available

	err = couchdb.CreateNamedDoc(i.vfsC, man)
	return
}

func (i *Installer) updateManifest(newman *Manifest) (err error) {
	if i.err != nil {
		return err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		} else {
			i.man = newman
			i.manc <- newman
		}
	}()

	oldman := i.man
	if oldman == nil {
		panic("Manifest not defined")
	}

	newman.SetID(oldman.ID())
	newman.SetRev(oldman.Rev())

	return couchdb.UpdateDoc(i.vfsC, newman)
}

// WaitManifest should be used to monitor the progress of the
// Installer.
func (i *Installer) WaitManifest() (man *Manifest, done bool, err error) {
	select {
	case man = <-i.manc:
		done = man.State == Ready
		return
	case err = <-i.errc:
		return
	}
}
<commit_msg>Fix apps.GetBySlug<commit_after>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"path"
	"regexp"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

const (
	// ManifestDocType is manifest type
	ManifestDocType = "io.cozy.manifests"
	// ManifestMaxSize is the manifest maximum size
	ManifestMaxSize = 2 << (2 * 10) // 2MB
)

// AppsDirectory is the name of the directory in which apps are stored
const AppsDirectory = "/_cozyapps"

// ManifestFilename is the name of the manifest at the root of the
// application directory
const ManifestFilename = "manifest.webapp"

// State is the state of the application
type State string

const (
	// Available state
	Available State = "available"
	// Installing state
	Installing = "installing"
	// Upgrading state
	Upgrading = "upgrading"
	// Uninstalling state
	Uninstalling = "uninstalling"
	// Errored state
	Errored = "errored"
	// Ready state
	Ready = "ready"
)

var slugReg = regexp.MustCompile(`^[A-Za-z0-9\-]+$`)

// Access is a string representing the access permission level. It can
// either be read, write or readwrite.
type Access string

// Permissions is a map of key, a description and an access level.
type Permissions map[string]*struct {
	Description string `json:"description"`
	Access      Access `json:"access"`
}

// Developer is the name and url of a developer.
type Developer struct {
	Name string `json:"name"`
	URL  string `json:"url,omitempty"`
}

// Manifest contains all the informations about an application.
type Manifest struct {
	ManRev string `json:"_rev,omitempty"` // Manifest revision

	Name        string     `json:"name"`
	Slug        string     `json:"slug"`
	Source      string     `json:"source"`
	State       State      `json:"state"`
	Icon        string     `json:"icon"`
	Description string     `json:"description"`
	Developer   *Developer `json:"developer"`

	DefaultLocal string `json:"default_locale"`
	Locales      map[string]*struct {
		Description string `json:"description"`
	} `json:"locales"`

	Version     string       `json:"version"`
	License     string       `json:"license"`
	Permissions *Permissions `json:"permissions"`
}

// ID returns the manifest identifier - see couchdb.Doc interface
func (m *Manifest) ID() string {
	return ManifestDocType + "/" + m.Slug
}

// Rev return the manifest revision - see couchdb.Doc interface
func (m *Manifest) Rev() string { return m.ManRev }

// DocType returns the manifest doctype - see couchdb.Doc interfaces
func (m *Manifest) DocType() string { return ManifestDocType }

// SetID is used to change the file identifier - see couchdb.Doc
// interface
func (m *Manifest) SetID(id string) {}

// SetRev is used to change the file revision - see couchdb.Doc
// interface
func (m *Manifest) SetRev(rev string) { m.ManRev = rev }

// SelfLink is used to generate a JSON-API link for the file - see
// jsonapi.Object interface
func (m *Manifest) SelfLink() string { return "/apps/" + m.Slug }

// Relationships is used to generate the parent relationship in JSON-API format
// - see jsonapi.Object interface
func (m *Manifest) Relationships() jsonapi.RelationshipMap {
	return jsonapi.RelationshipMap{}
}

// Included is part of the jsonapi.Object interface
func (m *Manifest) Included() []jsonapi.Object {
	return []jsonapi.Object{}
}

// Client interface should be implemented by the underlying transport
// used to fetch the application data.
type Client interface {
	// FetchManifest should returns an io.ReadCloser to read the
	// manifest data
	FetchManifest() (io.ReadCloser, error)
	// Fetch should download the application and install it in the given
	// directory.
	Fetch(vfsC vfs.Context, appdir string) error
}

// List returns the list of installed applications.
//
// TODO: pagination
func List(db couchdb.Database) ([]*Manifest, error) {
	var docs []*Manifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, ManifestDocType, req, &docs)
	if err != nil {
		return nil, err
	}
	return docs, nil
}

// GetBySlug returns an app identified by its slug
func GetBySlug(db couchdb.Database, slug string) (*Manifest, error) {
	man := &Manifest{}
	err := couchdb.GetDoc(db, ManifestDocType, ManifestDocType+"/"+slug, man)
	if err != nil {
		return nil, err
	}
	return man, nil
}

// Installer is used to install or update applications.
type Installer struct {
	cli Client

	vfsC vfs.Context

	slug string
	src  string
	man  *Manifest

	err  error
	errc chan error
	manc chan *Manifest
}

// NewInstaller creates a new Installer
func NewInstaller(vfsC vfs.Context, slug, src string) (*Installer, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}

	parsedSrc, err := url.Parse(src)
	if err != nil {
		return nil, err
	}

	var cli Client
	switch parsedSrc.Scheme {
	case "git":
		cli = newGitClient(vfsC, src)
	default:
		err = ErrNotSupportedSource
	}

	if err != nil {
		return nil, err
	}

	inst := &Installer{
		cli:  cli,
		vfsC: vfsC,

		slug: slug,
		src:  src,

		errc: make(chan error),
		manc: make(chan *Manifest),
	}

	return inst, err
}

// Install will install the application linked to the installer. It
// will report its progress or error using the WaitManifest method.
func (i *Installer) Install() (newman *Manifest, err error) {
	if i.err != nil {
		return nil, i.err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		}
	}()

	_, err = i.getOrCreateManifest(i.src, i.slug)
	if err != nil {
		return
	}

	oldman := i.man
	if s := oldman.State; s != Available && s != Errored {
		return nil, ErrBadState
	}

	newman = &(*oldman)
	newman.State = Installing

	defer func() {
		if err != nil {
			newman.State = Errored
			i.updateManifest(newman)
		}
	}()

	err = i.updateManifest(newman)
	if err != nil {
		return
	}

	appdir := path.Join(AppsDirectory, newman.Slug)
	err = vfs.MkdirAll(i.vfsC, appdir)
	if err != nil {
		return
	}

	err = i.cli.Fetch(i.vfsC, appdir)
	if err != nil {
		return
	}

	newman.State = Ready
	err = i.updateManifest(newman)
	if err != nil {
		return
	}

	return
}

func (i *Installer) handleErr(err error) error {
	if i.err == nil {
		i.err = err
		i.errc <- err
	}
	return i.err
}

func (i *Installer) getOrCreateManifest(src, slug string) (man *Manifest, err error) {
	if i.err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		} else {
			i.man = man
		}
	}()

	if i.man != nil {
		panic("Manifest is already defined")
	}

	man, err = GetBySlug(i.vfsC, slug)
	if err != nil && !couchdb.IsNotFoundError(err) {
		return nil, err
	}
	if err == nil {
		return man, nil
	}

	r, err := i.cli.FetchManifest()
	if err != nil {
		return nil, err
	}

	defer r.Close()
	man = &Manifest{}
	err = json.NewDecoder(io.LimitReader(r, ManifestMaxSize)).Decode(&man)
	if err != nil {
		return nil, ErrBadManifest
	}

	man.Slug = slug
	man.Source = src
	man.State = Available

	err = couchdb.CreateNamedDoc(i.vfsC, man)
	return
}

func (i *Installer) updateManifest(newman *Manifest) (err error) {
	if i.err != nil {
		return err
	}

	defer func() {
		if err != nil {
			err = i.handleErr(err)
		} else {
			i.man = newman
			i.manc <- newman
		}
	}()

	oldman := i.man
	if oldman == nil {
		panic("Manifest not defined")
	}

	newman.SetID(oldman.ID())
	newman.SetRev(oldman.Rev())

	return couchdb.UpdateDoc(i.vfsC, newman)
}

// WaitManifest should be used to monitor the progress of the
// Installer.
func (i *Installer) WaitManifest() (man *Manifest, done bool, err error) {
	select {
	case man = <-i.manc:
		done = man.State == Ready
		return
	case err = <-i.errc:
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	"github.com/ecc1/cc1100"
	"github.com/ecc1/spi"
)

func main() {
	dev, err := spi.Open(cc1100.SpiSpeed, 0)
	if err != nil {
		log.Fatal(err)
	}
	err = dev.SetMaxSpeed(cc1100.SpiSpeed)
	if err != nil {
		log.Fatal(err)
	}
	speed, err := dev.MaxSpeed()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Max speed: %d Hz\n", speed)

	cc1100.Reset(dev)
	fmt.Printf("\nDefault RF settings:\n")
	cc1100.DumpRF(dev)

	cc1100.InitRF(dev)
	fmt.Printf("\nRF settings after initialization:\n")
	cc1100.DumpRF(dev)
}
<commit_msg>Remove custom CSn<commit_after>package main

import (
	"fmt"
	"log"

	"github.com/ecc1/cc1100"
	"github.com/ecc1/spi"
)

func main() {
	dev, err := spi.Open(cc1100.SpiSpeed)
	if err != nil {
		log.Fatal(err)
	}
	err = dev.SetMaxSpeed(cc1100.SpiSpeed)
	if err != nil {
		log.Fatal(err)
	}
	speed, err := dev.MaxSpeed()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Max speed: %d Hz\n", speed)

	cc1100.Reset(dev)
	fmt.Printf("\nDefault RF settings:\n")
	cc1100.DumpRF(dev)

	cc1100.InitRF(dev)
	fmt.Printf("\nRF settings after initialization:\n")
	cc1100.DumpRF(dev)
}
<|endoftext|>


<commit_before><commit_msg>Fix bug with double-replacement of backslashes in build commands<commit_after><|endoftext|>


<commit_before><commit_msg>add buffer size<commit_after><|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"

	"github.com/git-lfs/git-lfs/git"
)

// runCatFileBatch uses 'git cat-file --batch' to get the object contents of a
// git object, given its sha1. The contents will be decoded into a Git LFS
// pointer. Git Blob SHA1s are read from the sha1Ch channel and fed to STDIN.
// Results are parsed from STDOUT, and any eligible LFS pointers are sent to
// pointerCh. If a Git Blob is not an LFS pointer, check the lockableSet to see
// if that blob is for a locked file. Any errors are sent to errCh. An error is
// returned if the 'git cat-file' command fails to start.
func runCatFileBatch(pointerCh chan *WrappedPointer, lockableCh chan string, lockableSet *lockableNameSet, revs *StringChannelWrapper, errCh chan error) error {
	scanner, err := NewCatFileBatchScanner()
	if err != nil {
		return err
	}

	go func() {
		for r := range revs.Results {
			canScan := scanner.Scan(r)

			if err := scanner.Err(); err != nil {
				errCh <- err
			} else if p := scanner.Pointer(); p != nil {
				pointerCh <- p
			} else if b := scanner.BlobSHA(); len(b) == 40 {
				if name, ok := lockableSet.Check(b); ok {
					lockableCh <- name
				}
			}

			if !canScan {
				break
			}
		}

		if err := revs.Wait(); err != nil {
			errCh <- err
		}

		if err := scanner.Close(); err != nil {
			errCh <- err
		}

		close(pointerCh)
		close(errCh)
		close(lockableCh)
	}()

	return nil
}

type CatFileBatchScanner struct {
	scanner *git.ObjectScanner

	blobSha     string
	contentsSha string
	pointer     *WrappedPointer
	err         error
}

func NewCatFileBatchScanner() (*CatFileBatchScanner, error) {
	scanner, err := git.NewObjectScanner()
	if err != nil {
		return nil, err
	}

	return &CatFileBatchScanner{scanner: scanner}, nil
}

func (s *CatFileBatchScanner) BlobSHA() string {
	return s.blobSha
}

func (s *CatFileBatchScanner) ContentsSha() string {
	return s.contentsSha
}

func (s *CatFileBatchScanner) Pointer() *WrappedPointer {
	return s.pointer
}

func (s *CatFileBatchScanner) Err() error {
	return s.err
}

func (s *CatFileBatchScanner) Scan(sha string) bool {
	s.pointer, s.err = nil, nil
	s.blobSha, s.contentsSha = "", ""

	b, c, p, err := s.next(sha)
	s.blobSha = b
	s.contentsSha = c
	s.pointer = p

	if err != nil {
		if err != io.EOF {
			s.err = err
		}
		return false
	}

	fmt.Println("returning true with blobsha:", b, "contents:", c, err)
	return true
}

func (s *CatFileBatchScanner) Close() error {
	return s.scanner.Close()
}

func (s *CatFileBatchScanner) next(blob string) (string, string, *WrappedPointer, error) {
	if !s.scanner.Scan(blob) {
		if err := s.scanner.Err(); err != nil {
			return "", "", nil, err
		}
		return "", "", nil, io.EOF
	}

	blobSha := s.scanner.Sha1()
	size := s.scanner.Size()

	sha := sha256.New()

	var buf *bytes.Buffer
	var to io.Writer = sha
	if size <= blobSizeCutoff {
		buf = bytes.NewBuffer(make([]byte, 0, size))
		to = io.MultiWriter(to, buf)
	}

	read, err := io.CopyN(to, s.scanner.Contents(), int64(size))
	if err != nil {
		return blobSha, "", nil, err
	}

	if int64(size) != read {
		return blobSha, "", nil, fmt.Errorf("expected %d bytes, read %d bytes", size, read)
	}

	var pointer *WrappedPointer
	var contentsSha string

	if size <= blobSizeCutoff {
		if p, err := DecodePointer(bytes.NewReader(buf.Bytes())); err != nil {
			contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
		} else {
			pointer = &WrappedPointer{
				Sha1:    blobSha,
				Pointer: p,
			}
			contentsSha = p.Oid
		}
	} else {
		contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
	}

	return blobSha, contentsSha, pointer, err
}
<commit_msg>lfs: remove some unneeded debugging information<commit_after>package lfs

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"

	"github.com/git-lfs/git-lfs/git"
)

// runCatFileBatch uses 'git cat-file --batch' to get the object contents of a
// git object, given its sha1. The contents will be decoded into a Git LFS
// pointer. Git Blob SHA1s are read from the sha1Ch channel and fed to STDIN.
// Results are parsed from STDOUT, and any eligible LFS pointers are sent to
// pointerCh. If a Git Blob is not an LFS pointer, check the lockableSet to see
// if that blob is for a locked file. Any errors are sent to errCh. An error is
// returned if the 'git cat-file' command fails to start.
func runCatFileBatch(pointerCh chan *WrappedPointer, lockableCh chan string, lockableSet *lockableNameSet, revs *StringChannelWrapper, errCh chan error) error {
	scanner, err := NewCatFileBatchScanner()
	if err != nil {
		return err
	}

	go func() {
		for r := range revs.Results {
			canScan := scanner.Scan(r)

			if err := scanner.Err(); err != nil {
				errCh <- err
			} else if p := scanner.Pointer(); p != nil {
				pointerCh <- p
			} else if b := scanner.BlobSHA(); len(b) == 40 {
				if name, ok := lockableSet.Check(b); ok {
					lockableCh <- name
				}
			}

			if !canScan {
				break
			}
		}

		if err := revs.Wait(); err != nil {
			errCh <- err
		}

		if err := scanner.Close(); err != nil {
			errCh <- err
		}

		close(pointerCh)
		close(errCh)
		close(lockableCh)
	}()

	return nil
}

type CatFileBatchScanner struct {
	scanner *git.ObjectScanner

	blobSha     string
	contentsSha string
	pointer     *WrappedPointer
	err         error
}

func NewCatFileBatchScanner() (*CatFileBatchScanner, error) {
	scanner, err := git.NewObjectScanner()
	if err != nil {
		return nil, err
	}

	return &CatFileBatchScanner{scanner: scanner}, nil
}

func (s *CatFileBatchScanner) BlobSHA() string {
	return s.blobSha
}

func (s *CatFileBatchScanner) ContentsSha() string {
	return s.contentsSha
}

func (s *CatFileBatchScanner) Pointer() *WrappedPointer {
	return s.pointer
}

func (s *CatFileBatchScanner) Err() error {
	return s.err
}

func (s *CatFileBatchScanner) Scan(sha string) bool {
	s.pointer, s.err = nil, nil
	s.blobSha, s.contentsSha = "", ""

	b, c, p, err := s.next(sha)
	s.blobSha = b
	s.contentsSha = c
	s.pointer = p

	if err != nil {
		if err != io.EOF {
			s.err = err
		}
		return false
	}

	return true
}

func (s *CatFileBatchScanner) Close() error {
	return s.scanner.Close()
}

func (s *CatFileBatchScanner) next(blob string) (string, string, *WrappedPointer, error) {
	if !s.scanner.Scan(blob) {
		if err := s.scanner.Err(); err != nil {
			return "", "", nil, err
		}
		return "", "", nil, io.EOF
	}

	blobSha := s.scanner.Sha1()
	size := s.scanner.Size()

	sha := sha256.New()

	var buf *bytes.Buffer
	var to io.Writer = sha
	if size <= blobSizeCutoff {
		buf = bytes.NewBuffer(make([]byte, 0, size))
		to = io.MultiWriter(to, buf)
	}

	read, err := io.CopyN(to, s.scanner.Contents(), int64(size))
	if err != nil {
		return blobSha, "", nil, err
	}

	if int64(size) != read {
		return blobSha, "", nil, fmt.Errorf("expected %d bytes, read %d bytes", size, read)
	}

	var pointer *WrappedPointer
	var contentsSha string

	if size <= blobSizeCutoff {
		if p, err := DecodePointer(bytes.NewReader(buf.Bytes())); err != nil {
			contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
		} else {
			pointer = &WrappedPointer{
				Sha1:    blobSha,
				Pointer: p,
			}
			contentsSha = p.Oid
		}
	} else {
		contentsSha = fmt.Sprintf("%x", sha.Sum(nil))
	}

	return blobSha, contentsSha, pointer, err
}
<|endoftext|>


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

package control

import (
	"github.com/cloudawan/cloudone_utility/logger"
	"github.com/cloudawan/cloudone_utility/restclient"
	"strconv"
)

func GetAllNamespaceName(kubeapiHost string, kubeapiPort int) (returnedNameSlice []string, returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("GetAllNamespaceName Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedNameSlice = nil
			returnedError = err.(error)
		}
	}()

	result, err := restclient.RequestGet("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/", nil, true)
	if err != nil {
		log.Error("Fail to get all namespace name with host: %s, port: %d, error: %s", kubeapiHost, kubeapiPort, err.Error())
		return nil, err
	}
	jsonMap, _ := result.(map[string]interface{})

	nameSlice := make([]string, 0)
	for _, data := range jsonMap["items"].([]interface{}) {
		name, ok := data.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
		if ok {
			nameSlice = append(nameSlice, name)
		}
	}

	return nameSlice, nil
}

func CreateNamespace(kubeapiHost string, kubeapiPort int, name string) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("CreateNamespace Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedError = err.(error)
		}
	}()

	jsonMap := make(map[string]interface{})
	jsonMap["metadata"] = make(map[string]interface{})
	jsonMap["metadata"].(map[string]interface{})["name"] = name

	_, err := restclient.RequestPost("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/", jsonMap, nil, true)
	if err != nil {
		log.Error("Fail to create namespace with host: %s, port: %d, name: %s, error: %s", kubeapiHost, kubeapiPort, name, err.Error())
		return err
	}

	return nil
}

func DeleteNamespace(kubeapiHost string, kubeapiPort int, name string) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("DeleteNamespace Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedError = err.(error)
		}
	}()

	_, err := restclient.RequestDelete("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/"+name, nil, nil, true)
	if err != nil {
		log.Error("Fail to delete namespace with host: %s, port: %d, name: %s, error: %s", kubeapiHost, kubeapiPort, name, err.Error())
		return err
	}

	return nil
}
<commit_msg>Add phase check<commit_after>// Copyright 2015 CloudAwan LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"github.com/cloudawan/cloudone_utility/logger"
	"github.com/cloudawan/cloudone_utility/restclient"
	"strconv"
)

func GetAllNamespaceName(kubeapiHost string, kubeapiPort int) (returnedNameSlice []string, returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("GetAllNamespaceName Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedNameSlice = nil
			returnedError = err.(error)
		}
	}()

	result, err := restclient.RequestGet("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/", nil, true)
	if err != nil {
		log.Error("Fail to get all namespace name with host: %s, port: %d, error: %s", kubeapiHost, kubeapiPort, err.Error())
		return nil, err
	}
	jsonMap, _ := result.(map[string]interface{})

	nameSlice := make([]string, 0)
	for _, data := range jsonMap["items"].([]interface{}) {
		name, ok := data.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
		if ok {
			statusJsonMap, _ := data.(map[string]interface{})["status"].(map[string]interface{})
			phase, _ := statusJsonMap["phase"].(string)
			if phase == "Active" {
				nameSlice = append(nameSlice, name)
			}
		}

	}

	return nameSlice, nil
}

func CreateNamespace(kubeapiHost string, kubeapiPort int, name string) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("CreateNamespace Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedError = err.(error)
		}
	}()

	jsonMap := make(map[string]interface{})
	jsonMap["metadata"] = make(map[string]interface{})
	jsonMap["metadata"].(map[string]interface{})["name"] = name

	_, err := restclient.RequestPost("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/", jsonMap, nil, true)
	if err != nil {
		log.Error("Fail to create namespace with host: %s, port: %d, name: %s, error: %s", kubeapiHost, kubeapiPort, name, err.Error())
		return err
	}

	return nil
}

func DeleteNamespace(kubeapiHost string, kubeapiPort int, name string) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			log.Error("DeleteNamespace Error: %s", err)
			log.Error(logger.GetStackTrace(4096, false))
			returnedError = err.(error)
		}
	}()

	_, err := restclient.RequestDelete("http://"+kubeapiHost+":"+strconv.Itoa(kubeapiPort)+"/api/v1/namespaces/"+name, nil, nil, true)
	if err != nil {
		log.Error("Fail to delete namespace with host: %s, port: %d, name: %s, error: %s", kubeapiHost, kubeapiPort, name, err.Error())
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"
	"../log"
	"../dbmanager"
)

func apiHandler(resp http.ResponseWriter, req *http.Request) {
	uri := req.URL.Path[5:]

	switch uri {
	case "list":
		if checkAccess(resp, req) {
			HandlerList(resp, req)
		}
		break
	default:
		HandlerApiHome(resp, req)
	}
}

func checkAccess(resp http.ResponseWriter, req *http.Request) bool {
	return true
}

func HandlerApiHome(resp http.ResponseWriter, req *http.Request) {

}

func HandlerList(resp http.ResponseWriter, req *http.Request) {
	db := ReInitDb()
	r, err := dbmanager.PatchData(db, "credit")
	if err != nil {
		return
	}
	var cl = &[]Credit{}
	ParseRowsToCreditList(r, cl)
	js := ParserCreditsToJson(cl)
	respData, err := js.MarshalJSON()
	if err != nil {
		log.Error("convert json to bytes error")
		resp.Write([]byte(`500 SERVER INTERNAL ERROR`))
	}
	resp.Write(respData)
}
<commit_msg>fix resp data type as json/application<commit_after>package server

import (
	"net/http"
	"../log"
	"../dbmanager"
)

func apiHandler(resp http.ResponseWriter, req *http.Request) {
	uri := req.URL.Path[5:]

	switch uri {
	case "list":
		if checkAccess(resp, req) {
			HandlerList(resp, req)
		}
		break
	default:
		HandlerApiHome(resp, req)
	}
}

func checkAccess(resp http.ResponseWriter, req *http.Request) bool {
	return true
}

func HandlerApiHome(resp http.ResponseWriter, req *http.Request) {

}

func HandlerList(resp http.ResponseWriter, req *http.Request) {
	db := ReInitDb()
	r, err := dbmanager.PatchData(db, "credit")
	if err != nil {
		return
	}
	var cl = &[]Credit{}
	ParseRowsToCreditList(r, cl)
	js := ParserCreditsToJson(cl)
	respData, err := js.MarshalJSON()
	if err != nil {
		log.Error("convert json to bytes error")
		resp.Write([]byte(`500 SERVER INTERNAL ERROR`))
	}
	resp.Header().Set("content-type","application/json")
	resp.Write(respData)
}
<|endoftext|>


<commit_before>package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/solderapp/solder/model"
	"github.com/solderapp/solder/router/middleware/context"
)

// GetClients retrieves all available clients.
func GetClients(c *gin.Context) {
	records := &model.Clients{}

	err := context.Store(c).Scopes(
		model.ClientDefaultOrder,
	).Find(
		&records,
	).Error

	if err != nil {
		c.IndentedJSON(
			http.StatusInternalServerError,
			gin.H{
				"status":  http.StatusInternalServerError,
				"message": "Failed to fetch clients",
			},
		)

		c.Abort()
		return
	}

	c.IndentedJSON(
		http.StatusOK,
		records,
	)
}

// GetClient retrieves a specific client.
func GetClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}

// DeleteClient removes a specific client.
func DeleteClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}

// PatchClient creates a new client.
func PatchClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}

// PostClient updates an existing client.
func PostClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}
<commit_msg>Added client show controller action<commit_after>package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/solderapp/solder/model"
	"github.com/solderapp/solder/router/middleware/context"
)

// GetClients retrieves all available clients.
func GetClients(c *gin.Context) {
	records := &model.Clients{}

	err := context.Store(c).Scopes(
		model.ClientDefaultOrder,
	).Find(
		&records,
	).Error

	if err != nil {
		c.IndentedJSON(
			http.StatusInternalServerError,
			gin.H{
				"status":  http.StatusInternalServerError,
				"message": "Failed to fetch clients",
			},
		)

		c.Abort()
		return
	}

	c.IndentedJSON(
		http.StatusOK,
		records,
	)
}

// GetClient retrieves a specific client.
func GetClient(c *gin.Context) {
	record := &model.Client{
		Slug: c.Param("client"),
	}

	err := context.Store(c).Find(
		&record,
	).Error

	if err != nil {
		c.IndentedJSON(
			http.StatusNotFound,
			gin.H{
				"status":  http.StatusNotFound,
				"message": "Failed to find client",
			},
		)

		c.Abort()
		return
	}

	c.IndentedJSON(
		http.StatusOK,
		record,
	)
}

// DeleteClient removes a specific client.
func DeleteClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}

// PatchClient creates a new client.
func PatchClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}

// PostClient updates an existing client.
func PostClient(c *gin.Context) {
	c.IndentedJSON(
		http.StatusOK,
		gin.H{},
	)
}
<|endoftext|>


<commit_before>package secstring

import "testing"

func TestNew(t *testing.T) {
	b := []byte("test")
	s, err := NewSecString(b)
	if s == nil {
		t.Error("New: Expected non-nil SecString. Got nil")
	}

	if err != nil {
		t.Errorf("New: Expected nil err. Got %v", err)
	}

	for i := 0; i < len(b); i++ {
		if b[i] != 0 {
			t.Error("New: input should have been cleared")
			t.Errorf("offending byte at offset %v: %v", i, b[i])
		}
	}

	if s.Length != 4 {
		t.Errorf("New: Expected length 4. Got %v", s.Length)
	}
}

func TestDestroy(t *testing.T) {
	s, err := NewSecString([]byte("test"))
	err = s.Destroy()

	if err != nil {
		t.Errorf("Destroy: Expected nil err. Got %v", err)
	}

	if s.String != nil {
		t.Errorf("Destroy: Expected nil s.String. got %v", s.String)
	}
}

func TestFromString(t *testing.T) {
	b := "test"
	s, err := FromString(&b)
	if s == nil {
		t.Error("New: Expected non-nil SecString. Got nil")
	}

	if err != nil {
		t.Errorf("New: Expected nil err. Got %v", err)
	}

	for i := 0; i < s.Length; i++ {
		if b[i] != uint8('x') {
			t.Error("New: input should have been cleared")
			t.Errorf("offending byte at offset %v: %v", i, b[i])
		}
	}

	if s.Length != 4 {
		t.Errorf("New: Expected length 4. Got %v", s.Length)
	}
}
<commit_msg>Clean up after my tests<commit_after>package secstring

import "testing"

func TestNew(t *testing.T) {
	b := []byte("test")
	s, err := NewSecString(b)
	defer s.Destroy()
	if s == nil {
		t.Error("New: Expected non-nil SecString. Got nil")
	}

	if err != nil {
		t.Errorf("New: Expected nil err. Got %v", err)
	}

	for i := 0; i < len(b); i++ {
		if b[i] != 0 {
			t.Error("New: input should have been cleared")
			t.Errorf("offending byte at offset %v: %v", i, b[i])
		}
	}

	if s.Length != 4 {
		t.Errorf("New: Expected length 4. Got %v", s.Length)
	}
}

func TestDestroy(t *testing.T) {
	s, err := NewSecString([]byte("test"))
	err = s.Destroy()

	if err != nil {
		t.Errorf("Destroy: Expected nil err. Got %v", err)
	}

	if s.String != nil {
		t.Errorf("Destroy: Expected nil s.String. got %v", s.String)
	}
}

func TestFromString(t *testing.T) {
	b := "test"
	s, err := FromString(&b)
	defer s.Destroy()
	if s == nil {
		t.Error("New: Expected non-nil SecString. Got nil")
	}

	if err != nil {
		t.Errorf("New: Expected nil err. Got %v", err)
	}

	for i := 0; i < s.Length; i++ {
		if b[i] != uint8('x') {
			t.Error("New: input should have been cleared")
			t.Errorf("offending byte at offset %v: %v", i, b[i])
		}
	}

	if s.Length != 4 {
		t.Errorf("New: Expected length 4. Got %v", s.Length)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

const (
	writeWait      = 3 * time.Second
	pongWait       = 5 * time.Second
	pingPeriod     = (pongWait * 9) / 10
	maxMessageSize = 512
)

type PlayerReader interface {
	Channel() chan<- PlayerMessage
	Register(*Player)
	Unregister(*Player, int, int)
	Group() int
}

type Player struct {
	writech chan []byte
	readers []PlayerReader
	readerl *sync.RWMutex
	ws      *websocket.Conn
	Name    string
}

type PlayerMessage struct {
	Player  *Player
	Message []byte
}

func NewPlayer(ws *websocket.Conn, pn string) *Player {
	p := &Player{
		writech: make(chan []byte, 512),
		readers: make([]PlayerReader, 0, 64),
		ws:      ws,
		Name:    pn,
		readerl: new(sync.RWMutex),
	}
	return p
}

func (p *Player) Listen() {
	go p.writer()
	p.reader()
}

func (p *Player) Unregister(group, code int) {
	p.readerl.RLock()
	cpy := make([]PlayerReader, len(p.readers))
	for i, reader := range p.readers {
		cpy[i] = reader
	}
	p.readerl.RUnlock()
	for _, reader := range cpy {
		reader.Unregister(p, group, code)
	}
}

func (p *Player) reader() {
	defer func() {
		p.Unregister(GROUP_ALL, PLAYER_DISCONNECT)
		p.ws.Close()
	}()
	p.ws.SetReadDeadline(time.Now().Add(pongWait))
	p.ws.SetPongHandler(func(string) error { p.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := p.ws.ReadMessage()
		if err != nil {
			break
		}
		p.dispatchMessage(message)
	}
}

func (p *Player) AddReader(pl PlayerReader) {
	p.readerl.Lock()
	p.readers = append(p.readers, pl)
	p.readerl.Unlock()
}

func (p *Player) RemoveReader(pl PlayerReader) {
	p.readerl.Lock()
	for i := range p.readers {
		if p.readers[i] == pl {
			p.readers = append(p.readers[:i], p.readers[i+1:]...)
			break
		}
	}
	p.readerl.Unlock()
}

func (p *Player) dispatchMessage(message []byte) {
	p.readerl.RLock()
	pm := PlayerMessage{p, message}
	for _, reader := range p.readers {
		reader.Channel() <- pm
	}
	p.readerl.RUnlock()
}

func (p *Player) Write(message []byte) {
	select {
	case p.writech <- message:
	default:
		close(p.writech)
	}
}

func (p *Player) write(mt int, payload []byte) error {
	p.ws.SetWriteDeadline(time.Now().Add(writeWait))
	return p.ws.WriteMessage(mt, payload)
}

func (p *Player) writer() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		p.ws.Close()
	}()
	for {
		select {
		case message, ok := <-p.writech:
			if !ok {
				p.write(websocket.CloseMessage, []byte{})
				return
			}
			if err := p.write(websocket.BinaryMessage, message); err != nil {
				return
			}
		case <-ticker.C:
			if err := p.write(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		}
	}
}
<commit_msg>Changed unregister structure<commit_after>package server

import (
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

const (
	writeWait      = 3 * time.Second
	pongWait       = 5 * time.Second
	pingPeriod     = (pongWait * 9) / 10
	maxMessageSize = 512
)

type PlayerReader interface {
	Channel() chan<- PlayerMessage
	Register(*Player)
	Unregister(*Player, int, int)
	Group() int
}

type Player struct {
	writech chan []byte
	readers []PlayerReader
	readerl *sync.RWMutex
	ws      *websocket.Conn
	Name    string
}

type PlayerMessage struct {
	Player  *Player
	Message []byte
}

func NewPlayer(ws *websocket.Conn, pn string) *Player {
	p := &Player{
		writech: make(chan []byte, 512),
		readers: make([]PlayerReader, 0, 64),
		ws:      ws,
		Name:    pn,
		readerl: new(sync.RWMutex),
	}
	return p
}

func (p *Player) Listen() {
	go p.writer()
	p.reader()
}

func (p *Player) Unregister(group, code int) {
	p.readerl.RLock()
	for i, reader := range p.readers {
		defer reader.Unregister(p, group, code)
	}
	p.readerl.RUnlock()
}

func (p *Player) reader() {
	defer func() {
		p.Unregister(GROUP_ALL, PLAYER_DISCONNECT)
		p.ws.Close()
	}()
	p.ws.SetReadDeadline(time.Now().Add(pongWait))
	p.ws.SetPongHandler(func(string) error { p.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := p.ws.ReadMessage()
		if err != nil {
			break
		}
		p.dispatchMessage(message)
	}
}

func (p *Player) AddReader(pl PlayerReader) {
	p.readerl.Lock()
	p.readers = append(p.readers, pl)
	p.readerl.Unlock()
}

func (p *Player) RemoveReader(pl PlayerReader) {
	p.readerl.Lock()
	for i := range p.readers {
		if p.readers[i] == pl {
			p.readers = append(p.readers[:i], p.readers[i+1:]...)
			break
		}
	}
	p.readerl.Unlock()
}

func (p *Player) dispatchMessage(message []byte) {
	p.readerl.RLock()
	pm := PlayerMessage{p, message}
	for _, reader := range p.readers {
		reader.Channel() <- pm
	}
	p.readerl.RUnlock()
}

func (p *Player) Write(message []byte) {
	select {
	case p.writech <- message:
	default:
		close(p.writech)
	}
}

func (p *Player) write(mt int, payload []byte) error {
	p.ws.SetWriteDeadline(time.Now().Add(writeWait))
	return p.ws.WriteMessage(mt, payload)
}

func (p *Player) writer() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		p.ws.Close()
	}()
	for {
		select {
		case message, ok := <-p.writech:
			if !ok {
				p.write(websocket.CloseMessage, []byte{})
				return
			}
			if err := p.write(websocket.BinaryMessage, message); err != nil {
				return
			}
		case <-ticker.C:
			if err := p.write(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"html/template"
	"net/http"

	ctx "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/jordan-wright/gophish/auth"
	"github.com/jordan-wright/gophish/db"
	mid "github.com/jordan-wright/gophish/middleware"
	"github.com/jordan-wright/gophish/models"
	"github.com/justinas/nosurf"
)

var templateDelims = []string{"{{%", "%}}"}

func CreateRouter() *nosurf.CSRFHandler {
	router := mux.NewRouter()
	// Base Front-end routes
	router.HandleFunc("/login", Login)
	router.HandleFunc("/logout", Use(Logout, mid.RequireLogin))
	router.HandleFunc("/register", Register)
	router.HandleFunc("/", Use(Base, mid.RequireLogin))
	router.HandleFunc("/campaigns/{id:[0-9]+}", Use(Campaigns_Id, mid.RequireLogin))
	router.HandleFunc("/users", Use(Users, mid.RequireLogin))
	router.HandleFunc("/settings", Use(Settings, mid.RequireLogin))

	// Create the API routes
	api := router.PathPrefix("/api").Subrouter()
	api.HandleFunc("/", Use(API, mid.RequireLogin))
	api.HandleFunc("/reset", Use(API_Reset, mid.RequireLogin))
	api.HandleFunc("/campaigns/", Use(API_Campaigns, mid.RequireAPIKey))
	api.HandleFunc("/campaigns/{id:[0-9]+}", Use(API_Campaigns_Id, mid.RequireAPIKey))
	api.HandleFunc("/groups/", Use(API_Groups, mid.RequireAPIKey))
	api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey))
	api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey))
	api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))

	// Setup static file serving
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

	// Setup CSRF Protection
	csrfHandler := nosurf.New(router)
	// Exempt API routes and Static files
	csrfHandler.ExemptGlob("/api/campaigns/*")
	csrfHandler.ExemptGlob("/api/groups/*")
	csrfHandler.ExemptGlob("/api/templates/*")
	csrfHandler.ExemptGlob("/static/*")
	return csrfHandler
}

// Use allows us to stack middleware to process the request
// Example taken from https://github.com/gorilla/mux/pull/36#issuecomment-25849172
func Use(handler http.HandlerFunc, mid ...func(http.Handler) http.HandlerFunc) http.HandlerFunc {
	for _, m := range mid {
		handler = m(handler)
	}
	return handler
}

func Register(w http.ResponseWriter, r *http.Request) {
	// If it is a post request, attempt to register the account
	// Now that we are all registered, we can log the user in
	params := struct {
		Title   string
		Flashes []interface{}
		User    models.User
		Token   string
	}{Title: "Register", Token: nosurf.Token(r)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "register").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		//Attempt to register
		succ, err := auth.Register(r)
		//If we've registered, redirect to the login page
		if succ {
			session.AddFlash(models.Flash{
				Type:    "success",
				Message: "Registration successful!.",
			})
			session.Save(r, w)
			http.Redirect(w, r, "/login", 302)
		} else {
			// Check the error
			m := ""
			if err == db.ErrUsernameTaken {
				m = "Username already taken"
			} else {
				m = "Unknown error - please try again"
				fmt.Println(err)
			}
			fmt.Println(m)
			session.AddFlash(models.Flash{
				Type:    "danger",
				Message: m,
			})
			session.Save(r, w)
			http.Redirect(w, r, "/register", 302)
		}

	}
}

func Logout(w http.ResponseWriter, r *http.Request) {
	// If it is a post request, attempt to register the account
	// Now that we are all registered, we can log the user in
	session := ctx.Get(r, "session").(*sessions.Session)
	delete(session.Values, "id")
	Flash(w, r, "success", "You have successfully logged out")
	http.Redirect(w, r, "login", 302)
}

func Base(w http.ResponseWriter, r *http.Request) {
	// Example of using session - will be removed.
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params)
}

func Users(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Users & Groups", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "users").ExecuteTemplate(w, "base", params)
}

func Settings(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
		Token   string
	}{Title: "Settings", User: ctx.Get(r, "user").(models.User)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Token = nosurf.Token(r)
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		err := auth.ChangePassword(r)
		if err == auth.ErrInvalidPassword {
			Flash(w, r, "danger", "Invalid Password")
		} else if err != nil {
			Flash(w, r, "danger", "Unknown Error")
		} else {
			Flash(w, r, "success", "Password successfully reset")
		}
		http.Redirect(w, r, "/settings", 302)
	}
}

func Campaigns_Id(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Results", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
}

func Login(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
		Token   string
	}{Title: "Login", Token: nosurf.Token(r)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "login").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		//Attempt to login
		succ, err := auth.Login(r)
		if err != nil {
			fmt.Println(err)
		}
		//If we've logged in, save the session and redirect to the dashboard
		if succ {
			session.Save(r, w)
			http.Redirect(w, r, "/", 302)
		} else {
			Flash(w, r, "danger", "Invalid Username/Password")
			http.Redirect(w, r, "/login", 302)
		}
	}
}

func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
	templates := template.New("template")
	templates.Delims(templateDelims[0], templateDelims[1])
	_, err := templates.ParseFiles("templates/base.html", "templates/nav.html", "templates/"+tmpl+".html", "templates/flashes.html")
	if err != nil {
		fmt.Println(err)
	}
	return template.Must(templates, err)
}

func checkError(e error, w http.ResponseWriter, m string, c int) bool {
	if e != nil {
		fmt.Println(e)
		http.Error(w, "Error: "+m, c)
		return true
	}
	return false
}

func Flash(w http.ResponseWriter, r *http.Request, t string, m string) {
	session := ctx.Get(r, "session").(*sessions.Session)
	session.AddFlash(models.Flash{
		Type:    t,
		Message: m,
	})
	session.Save(r, w)
}
<commit_msg>Set StrictSlash on API router to automatically redirect to correct url if slash is not provided<commit_after>package controllers

import (
	"fmt"
	"html/template"
	"net/http"

	ctx "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/jordan-wright/gophish/auth"
	"github.com/jordan-wright/gophish/db"
	mid "github.com/jordan-wright/gophish/middleware"
	"github.com/jordan-wright/gophish/models"
	"github.com/justinas/nosurf"
)

var templateDelims = []string{"{{%", "%}}"}

func CreateRouter() *nosurf.CSRFHandler {
	router := mux.NewRouter()
	// Base Front-end routes
	router.HandleFunc("/login", Login)
	router.HandleFunc("/logout", Use(Logout, mid.RequireLogin))
	router.HandleFunc("/register", Register)
	router.HandleFunc("/", Use(Base, mid.RequireLogin))
	router.HandleFunc("/campaigns/{id:[0-9]+}", Use(Campaigns_Id, mid.RequireLogin))
	router.HandleFunc("/users", Use(Users, mid.RequireLogin))
	router.HandleFunc("/settings", Use(Settings, mid.RequireLogin))

	// Create the API routes
	api := router.PathPrefix("/api").Subrouter()
	api = api.StrictSlash(true)
	api.HandleFunc("/", Use(API, mid.RequireLogin))
	api.HandleFunc("/reset", Use(API_Reset, mid.RequireLogin))
	api.HandleFunc("/campaigns/", Use(API_Campaigns, mid.RequireAPIKey))
	api.HandleFunc("/campaigns/{id:[0-9]+}", Use(API_Campaigns_Id, mid.RequireAPIKey))
	api.HandleFunc("/groups/", Use(API_Groups, mid.RequireAPIKey))
	api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey))
	api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey))
	api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))

	// Setup static file serving
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

	// Setup CSRF Protection
	csrfHandler := nosurf.New(router)
	// Exempt API routes and Static files
	csrfHandler.ExemptGlob("/api/campaigns/*")
	csrfHandler.ExemptGlob("/api/groups/*")
	csrfHandler.ExemptGlob("/api/templates/*")
	csrfHandler.ExemptGlob("/static/*")
	return csrfHandler
}

// Use allows us to stack middleware to process the request
// Example taken from https://github.com/gorilla/mux/pull/36#issuecomment-25849172
func Use(handler http.HandlerFunc, mid ...func(http.Handler) http.HandlerFunc) http.HandlerFunc {
	for _, m := range mid {
		handler = m(handler)
	}
	return handler
}

func Register(w http.ResponseWriter, r *http.Request) {
	// If it is a post request, attempt to register the account
	// Now that we are all registered, we can log the user in
	params := struct {
		Title   string
		Flashes []interface{}
		User    models.User
		Token   string
	}{Title: "Register", Token: nosurf.Token(r)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "register").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		//Attempt to register
		succ, err := auth.Register(r)
		//If we've registered, redirect to the login page
		if succ {
			session.AddFlash(models.Flash{
				Type:    "success",
				Message: "Registration successful!.",
			})
			session.Save(r, w)
			http.Redirect(w, r, "/login", 302)
		} else {
			// Check the error
			m := ""
			if err == db.ErrUsernameTaken {
				m = "Username already taken"
			} else {
				m = "Unknown error - please try again"
				fmt.Println(err)
			}
			fmt.Println(m)
			session.AddFlash(models.Flash{
				Type:    "danger",
				Message: m,
			})
			session.Save(r, w)
			http.Redirect(w, r, "/register", 302)
		}

	}
}

func Logout(w http.ResponseWriter, r *http.Request) {
	// If it is a post request, attempt to register the account
	// Now that we are all registered, we can log the user in
	session := ctx.Get(r, "session").(*sessions.Session)
	delete(session.Values, "id")
	Flash(w, r, "success", "You have successfully logged out")
	http.Redirect(w, r, "login", 302)
}

func Base(w http.ResponseWriter, r *http.Request) {
	// Example of using session - will be removed.
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params)
}

func Users(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Users & Groups", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "users").ExecuteTemplate(w, "base", params)
}

func Settings(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
		Token   string
	}{Title: "Settings", User: ctx.Get(r, "user").(models.User)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Token = nosurf.Token(r)
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		err := auth.ChangePassword(r)
		if err == auth.ErrInvalidPassword {
			Flash(w, r, "danger", "Invalid Password")
		} else if err != nil {
			Flash(w, r, "danger", "Unknown Error")
		} else {
			Flash(w, r, "success", "Password successfully reset")
		}
		http.Redirect(w, r, "/settings", 302)
	}
}

func Campaigns_Id(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
	}{Title: "Results", User: ctx.Get(r, "user").(models.User)}
	getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
}

func Login(w http.ResponseWriter, r *http.Request) {
	params := struct {
		User    models.User
		Title   string
		Flashes []interface{}
		Token   string
	}{Title: "Login", Token: nosurf.Token(r)}
	session := ctx.Get(r, "session").(*sessions.Session)
	switch {
	case r.Method == "GET":
		params.Flashes = session.Flashes()
		session.Save(r, w)
		getTemplate(w, "login").ExecuteTemplate(w, "base", params)
	case r.Method == "POST":
		//Attempt to login
		succ, err := auth.Login(r)
		if err != nil {
			fmt.Println(err)
		}
		//If we've logged in, save the session and redirect to the dashboard
		if succ {
			session.Save(r, w)
			http.Redirect(w, r, "/", 302)
		} else {
			Flash(w, r, "danger", "Invalid Username/Password")
			http.Redirect(w, r, "/login", 302)
		}
	}
}

func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
	templates := template.New("template")
	templates.Delims(templateDelims[0], templateDelims[1])
	_, err := templates.ParseFiles("templates/base.html", "templates/nav.html", "templates/"+tmpl+".html", "templates/flashes.html")
	if err != nil {
		fmt.Println(err)
	}
	return template.Must(templates, err)
}

func checkError(e error, w http.ResponseWriter, m string, c int) bool {
	if e != nil {
		fmt.Println(e)
		http.Error(w, "Error: "+m, c)
		return true
	}
	return false
}

func Flash(w http.ResponseWriter, r *http.Request, t string, m string) {
	session := ctx.Get(r, "session").(*sessions.Session)
	session.AddFlash(models.Flash{
		Type:    t,
		Message: m,
	})
	session.Save(r, w)
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/jessemillar/stalks/helpers"
	"github.com/zenazn/goji/web"
)

func (cg *ControllerGroup) Slack(c web.C, w http.ResponseWriter, r *http.Request) {
	params := strings.Fields(r.PostFormValue("text"))

	if len(params) == 0 {
		fmt.Fprintf(w, "%s\n", "Please enter a command. Usage: `/stalks [play, portfolio, check, buy, sell]`")
		return
	}

	if params[0] == "play" {
		fmt.Fprintf(w, "%s\n", helpers.MakeUser(r.PostFormValue("user_id"), r.PostFormValue("user_name")))
	} else if params[0] == "check" || params[0] == "c" {
		if len(params) < 2 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks check [symbol]`")
			return
		}

		fmt.Fprintf(w, "%s\n", helpers.Check(params[1]))
	} else if params[0] == "portfolio" || params[0] == "p" {
		fmt.Fprintf(w, "%s\n", helpers.Portfolio(r.PostFormValue("user_id")))
	} else if params[0] == "buy" || params[0] == "b" {
		if len(params) < 3 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks buy [quantity] [symbol]`")
			return
		}

		quantity, _ := strconv.Atoi(params[1])
		fmt.Fprintf(w, "%s\n", helpers.Buy(r.PostFormValue("user_id"), quantity, params[2]))
	} else if params[0] == "sell" || params[0] == "s" {
		if len(params) < 2 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks sell [quantity] [symbol]`")
			return
		}

		quantity, _ := strconv.Atoi(params[1])
		fmt.Fprintf(w, "%s\n", helpers.Sell(r.PostFormValue("user_id"), quantity, params[2]))
	}
}
<commit_msg>Adding a Slack error message if a user tries to play without creating an account<commit_after>package controllers

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/jessemillar/stalks/helpers"
	"github.com/jessemillar/stalks/models"
	"github.com/zenazn/goji/web"
)

func (cg *ControllerGroup) Slack(c web.C, w http.ResponseWriter, r *http.Request) {
	params := strings.Fields(r.PostFormValue("text"))

	if len(params) == 0 {
		fmt.Fprintf(w, "%s\n", "Please enter a command. Usage: `/stalks [play, portfolio, check, buy, sell]`")
		return
	}

	if params[0] == "play" {
		fmt.Fprintf(w, "%s\n", helpers.MakeUser(r.PostFormValue("user_id"), r.PostFormValue("user_name")))
	}

	user := models.GetUser(r.PostFormValue("user_id"))
	if len(user.Username) == 0 { // If we get a blank user returned
		fmt.Fprintf(w, "Your account does not exist yet. Please run `/stalks play` to start.")
		return
	}

	if params[0] == "check" || params[0] == "c" {
		if len(params) < 2 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks check [symbol]`")
			return
		}

		fmt.Fprintf(w, "%s\n", helpers.Check(params[1]))
	} else if params[0] == "portfolio" || params[0] == "p" {
		fmt.Fprintf(w, "%s\n", helpers.Portfolio(r.PostFormValue("user_id")))
	} else if params[0] == "buy" || params[0] == "b" {
		if len(params) < 3 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks buy [quantity] [symbol]`")
			return
		}

		quantity, _ := strconv.Atoi(params[1])
		fmt.Fprintf(w, "%s\n", helpers.Buy(r.PostFormValue("user_id"), quantity, params[2]))
	} else if params[0] == "sell" || params[0] == "s" {
		if len(params) < 2 {
			fmt.Fprintf(w, "Not enough parameters. Usage: `/stalks sell [quantity] [symbol]`")
			return
		}

		quantity, _ := strconv.Atoi(params[1])
		fmt.Fprintf(w, "%s\n", helpers.Sell(r.PostFormValue("user_id"), quantity, params[2]))
	}
}
<|endoftext|>