<commit_before>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/muesli/polly/api/config"

	"github.com/lib/pq"
	"github.com/muesli/cache2go"
	uuid "github.com/nu7hatch/gouuid"
)

var (
	pgDB   *sql.DB
	pgConn config.PostgreSQLConnection

	proposalsCache = cache2go.Cache("track")
	usersCache     = cache2go.Cache("user")

	// ErrInvalidID is the error returned when encountering an invalid database ID
	ErrInvalidID = errors.New("Invalid id")
)

// SetupPostgres sets the db configuration
func SetupPostgres(pc config.PostgreSQLConnection) {
	pgConn = pc
}

// GetDatabase connects to the database on first run and returns the existing
// connection on further calls
func GetDatabase() *sql.DB {
	if pgDB == nil {
		var err error
		pgDB, err = sql.Open("postgres", pgConn.Marshal())
		if err != nil {
			panic(err)
		}

		tables := []string{
			`CREATE TABLE IF NOT EXISTS users
				(
				  id          	bigserial 	PRIMARY KEY,
				  username    	text      	NOT NULL,
				  password		text		NOT NULL,
				  about       	text,
				  email       	text		NOT NULL,
				  activated   	bool		DEFAULT false,
				  authtoken   	text[]     	NOT NULL,
				  CONSTRAINT  	uk_username	UNIQUE (username),
				  CONSTRAINT  	uk_email 	UNIQUE (email)
				)`,
			`CREATE TABLE IF NOT EXISTS proposals
				(
				  id          	bigserial 	PRIMARY KEY,
				  userid      	bigserial 	NOT NULL,
				  title       	text      	NOT NULL,
				  description	text      	NOT NULL,
				  recipient		text		NOT NULL,
				  value			int			NOT NULL,
				  starts		timestamp	NOT NULL,
				  votes	      	int       	DEFAULT 0,
				  moderated     bool        DEFAULT false,
				  CONSTRAINT  	fk_user		FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
			`CREATE TABLE IF NOT EXISTS votes
				(
				  id          	bigserial			PRIMARY KEY,
				  userid    	bigserial			NOT NULL,
				  proposalid   	bigserial			NOT NULL,
				  vote			bool				NOT NULL,
				  CONSTRAINT  	uk_user_proposal	UNIQUE (userid, proposalid),
				  CONSTRAINT  	fk_user				FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
				  CONSTRAINT  	fk_proposal			FOREIGN KEY (proposalid) REFERENCES proposals (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
		}

		// FIXME: add IF NOT EXISTS to CREATE INDEX statements (coming in v9.5)
		// See: http://www.postgresql.org/docs/devel/static/sql-createindex.html
		indexes := []string{
			`CREATE INDEX idx_users_email ON users(email)`,
			`CREATE INDEX idx_proposals_moderated ON proposals(moderated)`,
			`CREATE INDEX idx_proposals_value ON proposals(value)`,
			`CREATE INDEX idx_proposals_userid ON proposals(userid)`,
			`CREATE INDEX idx_proposals_starts ON proposals(starts)`,
			`CREATE INDEX idx_votes_userid ON votes(userid)`,
			`CREATE INDEX idx_votes_proposalid ON votes(proposalid)`,
		}

		for _, v := range tables {
			fmt.Println("Creating table:", v)
			_, err = pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
		for _, v := range indexes {
			fmt.Println("Creating index:", v)
			_, err = pgDB.Exec(v)
			if err != nil && strings.Index(err.Error(), "already exists") < 0 {
				fmt.Println("Error:", err)
			}
		}

	}

	return pgDB
}

// WipeDatabase drops all database tables - use carefully!
func WipeDatabase() {
	// Commented out to prevent accidental usage

	/*
		drops := []string{
			`DROP TABLE votes`,
			`DROP TABLE proposals`,
			`DROP TABLE users`,
		}

		for _, v := range drops {
			fmt.Println("Dropping table:", v)
			_, err := pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
	*/
}

func init() {
	fmt.Println("db.init")
	initCaches()

	negativeInf := time.Time{}
	positiveInf, _ := time.Parse("2006", "3000")

	pq.EnableInfinityTs(negativeInf, positiveInf)
}

// UUID returns a new unique identifier
func UUID() (string, error) {
	u, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	uuid := strings.Join(strings.Split(u.String(), "-"), "")
	return uuid, nil
}

func initCaches() {
	usersCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in users-cache:", item.Key().(string), item.Data().(*DbUser).Username)
	})
	usersCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from users-cache:", item.Key().(string), item.Data().(*DbUser).Username, item.CreatedOn())
	})
	usersCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				user, err := context.LoadUserByID(key.(int64))
				if err != nil {
					fmt.Println("usersCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &user)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})

	proposalsCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title)
	})
	proposalsCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title, item.CreatedOn())
	})
	proposalsCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				proposal, err := context.LoadProposalByID(key.(int64))
				if err != nil {
					fmt.Println("proposalsCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &proposal)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})
}
<commit_msg>Create index on users(authtoken)<commit_after>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/muesli/polly/api/config"

	"github.com/lib/pq"
	"github.com/muesli/cache2go"
	uuid "github.com/nu7hatch/gouuid"
)

var (
	pgDB   *sql.DB
	pgConn config.PostgreSQLConnection

	proposalsCache = cache2go.Cache("track")
	usersCache     = cache2go.Cache("user")

	// ErrInvalidID is the error returned when encountering an invalid database ID
	ErrInvalidID = errors.New("Invalid id")
)

// SetupPostgres sets the db configuration
func SetupPostgres(pc config.PostgreSQLConnection) {
	pgConn = pc
}

// GetDatabase connects to the database on first run and returns the existing
// connection on further calls
func GetDatabase() *sql.DB {
	if pgDB == nil {
		var err error
		pgDB, err = sql.Open("postgres", pgConn.Marshal())
		if err != nil {
			panic(err)
		}

		tables := []string{
			`CREATE TABLE IF NOT EXISTS users
				(
				  id          	bigserial 	PRIMARY KEY,
				  username    	text      	NOT NULL,
				  password		text		NOT NULL,
				  about       	text,
				  email       	text		NOT NULL,
				  activated   	bool		DEFAULT false,
				  authtoken   	text[]     	NOT NULL,
				  CONSTRAINT  	uk_username	UNIQUE (username),
				  CONSTRAINT  	uk_email 	UNIQUE (email)
				)`,
			`CREATE TABLE IF NOT EXISTS proposals
				(
				  id          	bigserial 	PRIMARY KEY,
				  userid      	bigserial 	NOT NULL,
				  title       	text      	NOT NULL,
				  description	text      	NOT NULL,
				  recipient		text		NOT NULL,
				  value			int			NOT NULL,
				  starts		timestamp	NOT NULL,
				  votes	      	int       	DEFAULT 0,
				  moderated     bool        DEFAULT false,
				  CONSTRAINT  	fk_user		FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
			`CREATE TABLE IF NOT EXISTS votes
				(
				  id          	bigserial			PRIMARY KEY,
				  userid    	bigserial			NOT NULL,
				  proposalid   	bigserial			NOT NULL,
				  vote			bool				NOT NULL,
				  CONSTRAINT  	uk_user_proposal	UNIQUE (userid, proposalid),
				  CONSTRAINT  	fk_user				FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
				  CONSTRAINT  	fk_proposal			FOREIGN KEY (proposalid) REFERENCES proposals (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
		}

		// FIXME: add IF NOT EXISTS to CREATE INDEX statements (coming in v9.5)
		// See: http://www.postgresql.org/docs/devel/static/sql-createindex.html
		indexes := []string{
			`CREATE INDEX idx_users_email ON users(email)`,
			`CREATE INDEX idx_users_authtoken ON users(authtoken)`,
			`CREATE INDEX idx_proposals_moderated ON proposals(moderated)`,
			`CREATE INDEX idx_proposals_value ON proposals(value)`,
			`CREATE INDEX idx_proposals_userid ON proposals(userid)`,
			`CREATE INDEX idx_proposals_starts ON proposals(starts)`,
			`CREATE INDEX idx_votes_userid ON votes(userid)`,
			`CREATE INDEX idx_votes_proposalid ON votes(proposalid)`,
		}

		for _, v := range tables {
			fmt.Println("Creating table:", v)
			_, err = pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
		for _, v := range indexes {
			fmt.Println("Creating index:", v)
			_, err = pgDB.Exec(v)
			if err != nil && strings.Index(err.Error(), "already exists") < 0 {
				fmt.Println("Error:", err)
			}
		}

	}

	return pgDB
}

// WipeDatabase drops all database tables - use carefully!
func WipeDatabase() {
	// Commented out to prevent accidental usage

	/*
		drops := []string{
			`DROP TABLE votes`,
			`DROP TABLE proposals`,
			`DROP TABLE users`,
		}

		for _, v := range drops {
			fmt.Println("Dropping table:", v)
			_, err := pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
	*/
}

func init() {
	fmt.Println("db.init")
	initCaches()

	negativeInf := time.Time{}
	positiveInf, _ := time.Parse("2006", "3000")

	pq.EnableInfinityTs(negativeInf, positiveInf)
}

// UUID returns a new unique identifier
func UUID() (string, error) {
	u, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	uuid := strings.Join(strings.Split(u.String(), "-"), "")
	return uuid, nil
}

func initCaches() {
	usersCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in users-cache:", item.Key().(string), item.Data().(*DbUser).Username)
	})
	usersCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from users-cache:", item.Key().(string), item.Data().(*DbUser).Username, item.CreatedOn())
	})
	usersCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				user, err := context.LoadUserByID(key.(int64))
				if err != nil {
					fmt.Println("usersCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &user)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})

	proposalsCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title)
	})
	proposalsCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title, item.CreatedOn())
	})
	proposalsCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				proposal, err := context.LoadProposalByID(key.(int64))
				if err != nil {
					fmt.Println("proposalsCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &proposal)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package e2e

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"

	"github.com/hyperledger/fabric/integration/helpers"
	"github.com/hyperledger/fabric/integration/world"
	"github.com/tedsuo/ifrit"
)

var _ = Describe("EndToEnd", func() {
	var (
		testDir    string
		w          *world.World
		deployment world.Deployment
	)

	BeforeEach(func() {
		var err error
		testDir, err = ioutil.TempDir("", "e2e")
		Expect(err).NotTo(HaveOccurred())

		deployment = world.Deployment{
			Channel: "testchannel",
			Chaincode: world.Chaincode{
				Name:     "mycc",
				Version:  "0.0",
				Path:     "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				ExecPath: os.Getenv("PATH"),
			},
			InitArgs: `{"Args":["init","a","100","b","200"]}`,
			Policy:   `OR ('Org1MSP.member','Org2MSP.member')`,
			Orderer:  "127.0.0.1:7050",
		}
	})

	AfterEach(func() {
		if w != nil {
			w.Close(deployment)
		}
		os.RemoveAll(testDir)
	})

	Describe("basic solo network with 2 orgs", func() {
		BeforeEach(func() {
			w = world.GenerateBasicConfig("solo", 1, 2, testDir, components)
		})

		It("executes a basic solo network with 2 orgs", func() {
			By("generating files to bootstrap the network")
			w.BootstrapNetwork(deployment.Channel)
			Expect(filepath.Join(testDir, "configtx.yaml")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "crypto.yaml")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "crypto", "peerOrganizations")).To(BeADirectory())
			Expect(filepath.Join(testDir, "crypto", "ordererOrganizations")).To(BeADirectory())
			Expect(filepath.Join(testDir, "systestchannel_block.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "testchannel_tx.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "Org1_anchors_update_tx.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "Org2_anchors_update_tx.pb")).To(BeARegularFile())

			By("setting up directories for the network")
			helpers.CopyFile(filepath.Join("testdata", "orderer.yaml"), filepath.Join(testDir, "orderer.yaml"))
			w.CopyPeerConfigs("testdata")

			By("building the network")
			w.BuildNetwork()

			By("setting up the channel")
			w.SetupChannel(deployment, []string{"peer0.org1.example.com", "peer0.org2.example.com"})

			RunQueryInvokeQuery(w, deployment)
		})
	})

	Describe("basic kaka network with 2 orgs", func() {
		BeforeEach(func() {
			w = world.GenerateBasicConfig("kafka", 2, 2, testDir, components)
			w.SetupWorld(deployment)
		})

		It("executes a basic kafka network with 2 orgs", func() {
			RunQueryInvokeQuery(w, deployment)
		})
	})
})

func RunQueryInvokeQuery(w *world.World, deployment world.Deployment) {
	By("querying the chaincode")
	adminPeer := components.Peer()
	adminPeer.LogLevel = "debug"
	adminPeer.ConfigDir = filepath.Join(w.Rootpath, "peer0.org1.example.com")
	adminPeer.MSPConfigPath = filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
	adminRunner := adminPeer.QueryChaincode(deployment.Chaincode.Name, deployment.Channel, `{"Args":["query","a"]}`)
	execute(adminRunner)
	Eventually(adminRunner.Buffer()).Should(gbytes.Say("100"))

	By("invoking the chaincode")
	adminRunner = adminPeer.InvokeChaincode(deployment.Chaincode.Name, deployment.Channel, `{"Args":["invoke","a","b","10"]}`, deployment.Orderer, "--waitForEvent")
	execute(adminRunner)
	Eventually(adminRunner.Err()).Should(gbytes.Say("Chaincode invoke successful. result: status:200"))

	By("querying the chaincode again")
	adminRunner = adminPeer.QueryChaincode(deployment.Chaincode.Name, deployment.Channel, `{"Args":["query","a"]}`)
	execute(adminRunner)
	Eventually(adminRunner.Buffer()).Should(gbytes.Say("90"))

	By("updating the channel")
	adminPeer = components.Peer()
	adminPeer.ConfigDir = filepath.Join(w.Rootpath, "peer0.org1.example.com")
	adminPeer.MSPConfigPath = filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
	adminRunner = adminPeer.UpdateChannel(filepath.Join(w.Rootpath, "Org1_anchors_update_tx.pb"), deployment.Channel, deployment.Orderer)
	execute(adminRunner)
	Eventually(adminRunner.Err()).Should(gbytes.Say("Successfully submitted channel update"))
}

func execute(r ifrit.Runner) (err error) {
	p := ifrit.Invoke(r)
	Eventually(p.Ready()).Should(BeClosed())
	Eventually(p.Wait(), 30*time.Second).Should(Receive(&err))
	return err
}
<commit_msg>[FAB-10506] change endoremsent policy for e2e<commit_after>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package e2e

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"

	"github.com/hyperledger/fabric/integration/helpers"
	"github.com/hyperledger/fabric/integration/world"
	"github.com/tedsuo/ifrit"
)

var _ = Describe("EndToEnd", func() {
	var (
		testDir    string
		w          *world.World
		deployment world.Deployment
	)

	BeforeEach(func() {
		var err error
		testDir, err = ioutil.TempDir("", "e2e")
		Expect(err).NotTo(HaveOccurred())

		deployment = world.Deployment{
			Channel: "testchannel",
			Chaincode: world.Chaincode{
				Name:     "mycc",
				Version:  "0.0",
				Path:     "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				ExecPath: os.Getenv("PATH"),
			},
			InitArgs: `{"Args":["init","a","100","b","200"]}`,
			Policy:   `AND ('Org1MSP.member','Org2MSP.member')`,
			Orderer:  "127.0.0.1:7050",
		}
	})

	AfterEach(func() {
		if w != nil {
			w.Close(deployment)
		}
		os.RemoveAll(testDir)
	})

	Describe("basic solo network with 2 orgs", func() {
		BeforeEach(func() {
			w = world.GenerateBasicConfig("solo", 1, 2, testDir, components)
		})

		It("executes a basic solo network with 2 orgs", func() {
			By("generating files to bootstrap the network")
			w.BootstrapNetwork(deployment.Channel)
			Expect(filepath.Join(testDir, "configtx.yaml")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "crypto.yaml")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "crypto", "peerOrganizations")).To(BeADirectory())
			Expect(filepath.Join(testDir, "crypto", "ordererOrganizations")).To(BeADirectory())
			Expect(filepath.Join(testDir, "systestchannel_block.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "testchannel_tx.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "Org1_anchors_update_tx.pb")).To(BeARegularFile())
			Expect(filepath.Join(testDir, "Org2_anchors_update_tx.pb")).To(BeARegularFile())

			By("setting up directories for the network")
			helpers.CopyFile(filepath.Join("testdata", "orderer.yaml"), filepath.Join(testDir, "orderer.yaml"))
			w.CopyPeerConfigs("testdata")

			By("building the network")
			w.BuildNetwork()

			By("setting up the channel")
			w.SetupChannel(deployment, w.PeerIDs())

			RunQueryInvokeQuery(w, deployment)
		})
	})

	Describe("basic kaka network with 2 orgs", func() {
		BeforeEach(func() {
			w = world.GenerateBasicConfig("kafka", 2, 2, testDir, components)
			w.SetupWorld(deployment)
		})

		It("executes a basic kafka network with 2 orgs", func() {
			RunQueryInvokeQuery(w, deployment)
		})
	})
})

func RunQueryInvokeQuery(w *world.World, deployment world.Deployment) {
	By("querying the chaincode")
	adminPeer := components.Peer()
	adminPeer.LogLevel = "debug"
	adminPeer.ConfigDir = filepath.Join(w.Rootpath, "peer0.org1.example.com")
	adminPeer.MSPConfigPath = filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
	adminRunner := adminPeer.QueryChaincode(deployment.Chaincode.Name, deployment.Channel, `{"Args":["query","a"]}`)
	execute(adminRunner)
	Eventually(adminRunner.Buffer()).Should(gbytes.Say("100"))

	By("invoking the chaincode")
	adminRunner = adminPeer.InvokeChaincode(
		deployment.Chaincode.Name,
		deployment.Channel,
		`{"Args":["invoke","a","b","10"]}`,
		deployment.Orderer,
		"--waitForEvent",
		"--peerAddresses", "127.0.0.1:7051",
		"--peerAddresses", "127.0.0.1:8051",
	)
	execute(adminRunner)
	Eventually(adminRunner.Err()).Should(gbytes.Say("Chaincode invoke successful. result: status:200"))

	By("querying the chaincode again")
	adminRunner = adminPeer.QueryChaincode(deployment.Chaincode.Name, deployment.Channel, `{"Args":["query","a"]}`)
	execute(adminRunner)
	Eventually(adminRunner.Buffer()).Should(gbytes.Say("90"))

	By("updating the channel")
	adminPeer = components.Peer()
	adminPeer.ConfigDir = filepath.Join(w.Rootpath, "peer0.org1.example.com")
	adminPeer.MSPConfigPath = filepath.Join(w.Rootpath, "crypto", "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp")
	adminRunner = adminPeer.UpdateChannel(filepath.Join(w.Rootpath, "Org1_anchors_update_tx.pb"), deployment.Channel, deployment.Orderer)
	execute(adminRunner)
	Eventually(adminRunner.Err()).Should(gbytes.Say("Successfully submitted channel update"))
}

func execute(r ifrit.Runner) (err error) {
	p := ifrit.Invoke(r)
	Eventually(p.Ready()).Should(BeClosed())
	Eventually(p.Wait(), 30*time.Second).Should(Receive(&err))
	return err
}
<|endoftext|>


<commit_before>package status

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/gdamore/tcell/v2"
	tview "gitlab.com/tslocum/cview"

	"github.com/zrepl/zrepl/client/status/viewmodel"
)

func interactive(c Client, flag statusFlags) error {

	// Set this so we don't overwrite the default terminal colors
	// See https://github.com/rivo/tview/blob/master/styles.go
	tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
	tview.Styles.ContrastBackgroundColor = tcell.ColorDefault
	tview.Styles.PrimaryTextColor = tcell.ColorDefault
	tview.Styles.BorderColor = tcell.ColorDefault
	app := tview.NewApplication()

	jobDetailSplit := tview.NewFlex()
	jobMenu := tview.NewTreeView()
	jobMenuRoot := tview.NewTreeNode("jobs")
	jobMenuRoot.SetSelectable(true)
	jobMenu.SetRoot(jobMenuRoot)
	jobMenu.SetCurrentNode(jobMenuRoot)
	jobMenu.SetSelectedTextColor(tcell.ColorGreen)
	jobTextDetail := tview.NewTextView()
	jobTextDetail.SetWrap(false)

	jobMenu.SetBorder(true)
	jobTextDetail.SetBorder(true)

	toolbarSplit := tview.NewFlex()
	toolbarSplit.SetDirection(tview.FlexRow)
	inputBarContainer := tview.NewFlex()
	fsFilterInput := tview.NewInputField()
	fsFilterInput.SetBorder(false)
	fsFilterInput.SetFieldBackgroundColor(tcell.ColorDefault)
	inputBarLabel := tview.NewTextView()
	inputBarLabel.SetText("[::b]FILTER ")
	inputBarLabel.SetDynamicColors(true)
	inputBarContainer.AddItem(inputBarLabel, 7, 1, false)
	inputBarContainer.AddItem(fsFilterInput, 0, 10, false)
	toolbarSplit.AddItem(inputBarContainer, 1, 0, false)
	toolbarSplit.AddItem(jobDetailSplit, 0, 10, false)

	bottombar := tview.NewFlex()
	bottombar.SetDirection(tview.FlexColumn)
	bottombarDateView := tview.NewTextView()
	bottombar.AddItem(bottombarDateView, len(time.Now().String()), 0, false)
	bottomBarStatus := tview.NewTextView()
	bottomBarStatus.SetDynamicColors(true)
	bottomBarStatus.SetTextAlign(tview.AlignRight)
	bottombar.AddItem(bottomBarStatus, 0, 10, false)
	toolbarSplit.AddItem(bottombar, 1, 0, false)

	tabbableWithJobMenu := []tview.Primitive{jobMenu, jobTextDetail, fsFilterInput}
	tabbableWithoutJobMenu := []tview.Primitive{jobTextDetail, fsFilterInput}
	var tabbable []tview.Primitive
	tabbableActiveIndex := 0
	tabbableRedraw := func() {
		if len(tabbable) == 0 {
			app.SetFocus(nil)
			return
		}
		if tabbableActiveIndex >= len(tabbable) {
			app.SetFocus(tabbable[0])
			return
		}
		app.SetFocus(tabbable[tabbableActiveIndex])
	}
	tabbableCycle := func() {
		if len(tabbable) == 0 {
			return
		}
		tabbableActiveIndex = (tabbableActiveIndex + 1) % len(tabbable)
		app.SetFocus(tabbable[tabbableActiveIndex])
		tabbableRedraw()
	}

	jobMenuVisisble := false
	reconfigureJobDetailSplit := func(setJobMenuVisible bool) {
		if jobMenuVisisble == setJobMenuVisible {
			return
		}
		jobMenuVisisble = setJobMenuVisible
		if setJobMenuVisible {
			jobDetailSplit.RemoveItem(jobTextDetail)
			jobDetailSplit.AddItem(jobMenu, 0, 1, true)
			jobDetailSplit.AddItem(jobTextDetail, 0, 5, false)
			tabbable = tabbableWithJobMenu
		} else {
			jobDetailSplit.RemoveItem(jobMenu)
			tabbable = tabbableWithoutJobMenu
		}
		tabbableRedraw()
	}

	showModal := func(m *tview.Modal, modalDoneFunc func(idx int, label string)) {
		preModalFocus := app.GetFocus()
		m.SetDoneFunc(func(idx int, label string) {
			if modalDoneFunc != nil {
				modalDoneFunc(idx, label)
			}
			app.SetRoot(toolbarSplit, true)
			app.SetFocus(preModalFocus)
			app.Draw()
		})
		app.SetRoot(m, true)
		app.Draw()
	}

	app.SetRoot(toolbarSplit, true)
	// initial focus
	tabbableActiveIndex = len(tabbable)
	tabbableCycle()
	reconfigureJobDetailSplit(true)

	m := viewmodel.New()
	params := &viewmodel.Params{
		Report:                  nil,
		SelectedJob:             nil,
		FSFilter:                func(_ string) bool { return true },
		DetailViewWidth:         100,
		DetailViewWrap:          false,
		ShortKeybindingOverview: "[::b]Q[::-] quit  [::b]<TAB>[::-] switch panes  [::b]Shift+M[::-] toggle navbar  [::b]Shift+S[::-] signal job [::b]</>[::-] filter filesystems",
	}
	paramsMtx := &sync.Mutex{}
	var redraw func()
	viewmodelupdate := func(cb func(*viewmodel.Params)) {
		paramsMtx.Lock()
		defer paramsMtx.Unlock()
		cb(params)
		m.Update(*params)
	}
	redraw = func() {
		jobs := m.Jobs()
		if flag.Job != "" {
			job_found := false
			for _, job := range jobs {
				if strings.Compare(flag.Job, job.Name()) == 0 {
					jobs = []*viewmodel.Job{job}
					job_found = true
					break
				}
			}
			if !job_found {
				jobs = nil
			}
		}
		redrawJobsList := false
		var selectedJobN *tview.TreeNode
		if len(jobMenuRoot.GetChildren()) == len(jobs) {
			for i, jobN := range jobMenuRoot.GetChildren() {
				if jobN.GetReference().(*viewmodel.Job) != jobs[i] {
					redrawJobsList = true
					break
				}
				if jobN.GetReference().(*viewmodel.Job) == m.SelectedJob() {
					selectedJobN = jobN
				}
			}
		} else {
			redrawJobsList = true
		}
		if redrawJobsList {
			selectedJobN = nil
			children := make([]*tview.TreeNode, len(jobs))
			for i := range jobs {
				jobN := tview.NewTreeNode(jobs[i].JobTreeTitle())
				jobN.SetReference(jobs[i])
				jobN.SetSelectable(true)
				children[i] = jobN
				jobN.SetSelectedFunc(func() {
					viewmodelupdate(func(p *viewmodel.Params) {
						p.SelectedJob = jobN.GetReference().(*viewmodel.Job)
					})
				})
				if jobs[i] == m.SelectedJob() {
					selectedJobN = jobN
				}
			}
			jobMenuRoot.SetChildren(children)
		}

		if selectedJobN != nil && jobMenu.GetCurrentNode() != selectedJobN {
			jobMenu.SetCurrentNode(selectedJobN)
		} else if selectedJobN == nil {
			// select something, otherwise selection breaks (likely bug in tview)
			jobMenu.SetCurrentNode(jobMenuRoot)
		}

		if selJ := m.SelectedJob(); selJ != nil {
			jobTextDetail.SetText(selJ.FullDescription())
		} else {
			jobTextDetail.SetText("please select a job")
		}

		bottombardatestring := m.DateString()
		bottombarDateView.SetText(bottombardatestring)
		bottombar.ResizeItem(bottombarDateView, len(bottombardatestring), 0)

		bottomBarStatus.SetText(m.BottomBarStatus())

		app.Draw()

	}

	go func() {
		defer func() {
			if err := recover(); err != nil {
				app.Suspend(func() {
					panic(err)
				})
			}
		}()
		for {
			st, err := c.Status()
			viewmodelupdate(func(p *viewmodel.Params) {
				p.Report = st.Jobs
				p.ReportFetchError = err
			})
			app.QueueUpdateDraw(redraw)

			time.Sleep(flag.Delay)
		}
	}()

	jobMenu.SetChangedFunc(func(jobN *tview.TreeNode) {
		viewmodelupdate(func(p *viewmodel.Params) {
			p.SelectedJob, _ = jobN.GetReference().(*viewmodel.Job)
		})
		redraw()
		jobTextDetail.ScrollToBeginning()
	})
	jobMenu.SetSelectedFunc(func(jobN *tview.TreeNode) {
		app.SetFocus(jobTextDetail)
	})

	app.SetBeforeDrawFunc(func(screen tcell.Screen) bool {
		viewmodelupdate(func(p *viewmodel.Params) {
			_, _, p.DetailViewWidth, _ = jobTextDetail.GetInnerRect()
		})
		return false
	})

	app.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
		if e.Key() == tcell.KeyTab {
			tabbableCycle()
			return nil
		}

		if e.Key() == tcell.KeyRune && app.GetFocus() == fsFilterInput {
			return e
		}

		if e.Key() == tcell.KeyRune && e.Rune() == '/' {
			if app.GetFocus() != fsFilterInput {
				app.SetFocus(fsFilterInput)
			}
			return e
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'M' {
			reconfigureJobDetailSplit(!jobMenuVisisble)
			return nil
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'q' {
			app.Stop()
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'S' {
			job, ok := jobMenu.GetCurrentNode().GetReference().(*viewmodel.Job)
			if !ok {
				return nil
			}
			signals := []string{"wakeup", "reset"}
			clientFuncs := []func(job string) error{c.SignalWakeup, c.SignalReset}
			sigMod := tview.NewModal()
			sigMod.SetBackgroundColor(tcell.ColorDefault)
			sigMod.SetBorder(true)
			sigMod.GetForm().SetButtonTextColorFocused(tcell.ColorGreen)
			sigMod.AddButtons(signals)
			sigMod.SetText(fmt.Sprintf("Send a signal to job %q", job.Name()))
			showModal(sigMod, func(idx int, _ string) {
				go func() {
					if idx == -1 {
						return
					}
					err := clientFuncs[idx](job.Name())
					if err != nil {
						app.QueueUpdate(func() {
							me := tview.NewModal()
							me.SetText(fmt.Sprintf("signal error: %s", err))
							me.AddButtons([]string{"Close"})
							showModal(me, nil)
						})
					}
				}()
			})
		}

		return e
	})

	fsFilterInput.SetChangedFunc(func(searchterm string) {
		viewmodelupdate(func(p *viewmodel.Params) {
			p.FSFilter = func(fs string) bool {
				r, err := regexp.Compile(searchterm)
				if err != nil {
					return true
				}
				return r.MatchString(fs)
			}
		})
		redraw()
		jobTextDetail.ScrollToBeginning()
	})
	fsFilterInput.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		if event.Key() == tcell.KeyEnter {
			app.SetFocus(jobTextDetail)
			return nil
		}
		return event
	})

	jobTextDetail.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		if event.Key() == tcell.KeyRune && event.Rune() == 'w' {
			// toggle wrapping
			viewmodelupdate(func(p *viewmodel.Params) {
				p.DetailViewWrap = !p.DetailViewWrap
			})
			redraw()
			return nil
		}
		return event
	})

	return app.Run()
}
<commit_msg>zrepl status UI: include `w` shortcut to wrap lines in help bar<commit_after>package status

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/gdamore/tcell/v2"
	tview "gitlab.com/tslocum/cview"

	"github.com/zrepl/zrepl/client/status/viewmodel"
)

func interactive(c Client, flag statusFlags) error {

	// Set this so we don't overwrite the default terminal colors
	// See https://github.com/rivo/tview/blob/master/styles.go
	tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
	tview.Styles.ContrastBackgroundColor = tcell.ColorDefault
	tview.Styles.PrimaryTextColor = tcell.ColorDefault
	tview.Styles.BorderColor = tcell.ColorDefault
	app := tview.NewApplication()

	jobDetailSplit := tview.NewFlex()
	jobMenu := tview.NewTreeView()
	jobMenuRoot := tview.NewTreeNode("jobs")
	jobMenuRoot.SetSelectable(true)
	jobMenu.SetRoot(jobMenuRoot)
	jobMenu.SetCurrentNode(jobMenuRoot)
	jobMenu.SetSelectedTextColor(tcell.ColorGreen)
	jobTextDetail := tview.NewTextView()
	jobTextDetail.SetWrap(false)

	jobMenu.SetBorder(true)
	jobTextDetail.SetBorder(true)

	toolbarSplit := tview.NewFlex()
	toolbarSplit.SetDirection(tview.FlexRow)
	inputBarContainer := tview.NewFlex()
	fsFilterInput := tview.NewInputField()
	fsFilterInput.SetBorder(false)
	fsFilterInput.SetFieldBackgroundColor(tcell.ColorDefault)
	inputBarLabel := tview.NewTextView()
	inputBarLabel.SetText("[::b]FILTER ")
	inputBarLabel.SetDynamicColors(true)
	inputBarContainer.AddItem(inputBarLabel, 7, 1, false)
	inputBarContainer.AddItem(fsFilterInput, 0, 10, false)
	toolbarSplit.AddItem(inputBarContainer, 1, 0, false)
	toolbarSplit.AddItem(jobDetailSplit, 0, 10, false)

	bottombar := tview.NewFlex()
	bottombar.SetDirection(tview.FlexColumn)
	bottombarDateView := tview.NewTextView()
	bottombar.AddItem(bottombarDateView, len(time.Now().String()), 0, false)
	bottomBarStatus := tview.NewTextView()
	bottomBarStatus.SetDynamicColors(true)
	bottomBarStatus.SetTextAlign(tview.AlignRight)
	bottombar.AddItem(bottomBarStatus, 0, 10, false)
	toolbarSplit.AddItem(bottombar, 1, 0, false)

	tabbableWithJobMenu := []tview.Primitive{jobMenu, jobTextDetail, fsFilterInput}
	tabbableWithoutJobMenu := []tview.Primitive{jobTextDetail, fsFilterInput}
	var tabbable []tview.Primitive
	tabbableActiveIndex := 0
	tabbableRedraw := func() {
		if len(tabbable) == 0 {
			app.SetFocus(nil)
			return
		}
		if tabbableActiveIndex >= len(tabbable) {
			app.SetFocus(tabbable[0])
			return
		}
		app.SetFocus(tabbable[tabbableActiveIndex])
	}
	tabbableCycle := func() {
		if len(tabbable) == 0 {
			return
		}
		tabbableActiveIndex = (tabbableActiveIndex + 1) % len(tabbable)
		app.SetFocus(tabbable[tabbableActiveIndex])
		tabbableRedraw()
	}

	jobMenuVisisble := false
	reconfigureJobDetailSplit := func(setJobMenuVisible bool) {
		if jobMenuVisisble == setJobMenuVisible {
			return
		}
		jobMenuVisisble = setJobMenuVisible
		if setJobMenuVisible {
			jobDetailSplit.RemoveItem(jobTextDetail)
			jobDetailSplit.AddItem(jobMenu, 0, 1, true)
			jobDetailSplit.AddItem(jobTextDetail, 0, 5, false)
			tabbable = tabbableWithJobMenu
		} else {
			jobDetailSplit.RemoveItem(jobMenu)
			tabbable = tabbableWithoutJobMenu
		}
		tabbableRedraw()
	}

	showModal := func(m *tview.Modal, modalDoneFunc func(idx int, label string)) {
		preModalFocus := app.GetFocus()
		m.SetDoneFunc(func(idx int, label string) {
			if modalDoneFunc != nil {
				modalDoneFunc(idx, label)
			}
			app.SetRoot(toolbarSplit, true)
			app.SetFocus(preModalFocus)
			app.Draw()
		})
		app.SetRoot(m, true)
		app.Draw()
	}

	app.SetRoot(toolbarSplit, true)
	// initial focus
	tabbableActiveIndex = len(tabbable)
	tabbableCycle()
	reconfigureJobDetailSplit(true)

	m := viewmodel.New()
	params := &viewmodel.Params{
		Report:                  nil,
		SelectedJob:             nil,
		FSFilter:                func(_ string) bool { return true },
		DetailViewWidth:         100,
		DetailViewWrap:          false,
		ShortKeybindingOverview: "[::b]Q[::-] quit  [::b]<TAB>[::-] switch panes [::b]W[::-] wrap lines  [::b]Shift+M[::-] toggle navbar  [::b]Shift+S[::-] signal job [::b]</>[::-] filter filesystems",
	}
	paramsMtx := &sync.Mutex{}
	var redraw func()
	viewmodelupdate := func(cb func(*viewmodel.Params)) {
		paramsMtx.Lock()
		defer paramsMtx.Unlock()
		cb(params)
		m.Update(*params)
	}
	redraw = func() {
		jobs := m.Jobs()
		if flag.Job != "" {
			job_found := false
			for _, job := range jobs {
				if strings.Compare(flag.Job, job.Name()) == 0 {
					jobs = []*viewmodel.Job{job}
					job_found = true
					break
				}
			}
			if !job_found {
				jobs = nil
			}
		}
		redrawJobsList := false
		var selectedJobN *tview.TreeNode
		if len(jobMenuRoot.GetChildren()) == len(jobs) {
			for i, jobN := range jobMenuRoot.GetChildren() {
				if jobN.GetReference().(*viewmodel.Job) != jobs[i] {
					redrawJobsList = true
					break
				}
				if jobN.GetReference().(*viewmodel.Job) == m.SelectedJob() {
					selectedJobN = jobN
				}
			}
		} else {
			redrawJobsList = true
		}
		if redrawJobsList {
			selectedJobN = nil
			children := make([]*tview.TreeNode, len(jobs))
			for i := range jobs {
				jobN := tview.NewTreeNode(jobs[i].JobTreeTitle())
				jobN.SetReference(jobs[i])
				jobN.SetSelectable(true)
				children[i] = jobN
				jobN.SetSelectedFunc(func() {
					viewmodelupdate(func(p *viewmodel.Params) {
						p.SelectedJob = jobN.GetReference().(*viewmodel.Job)
					})
				})
				if jobs[i] == m.SelectedJob() {
					selectedJobN = jobN
				}
			}
			jobMenuRoot.SetChildren(children)
		}

		if selectedJobN != nil && jobMenu.GetCurrentNode() != selectedJobN {
			jobMenu.SetCurrentNode(selectedJobN)
		} else if selectedJobN == nil {
			// select something, otherwise selection breaks (likely bug in tview)
			jobMenu.SetCurrentNode(jobMenuRoot)
		}

		if selJ := m.SelectedJob(); selJ != nil {
			jobTextDetail.SetText(selJ.FullDescription())
		} else {
			jobTextDetail.SetText("please select a job")
		}

		bottombardatestring := m.DateString()
		bottombarDateView.SetText(bottombardatestring)
		bottombar.ResizeItem(bottombarDateView, len(bottombardatestring), 0)

		bottomBarStatus.SetText(m.BottomBarStatus())

		app.Draw()

	}

	go func() {
		defer func() {
			if err := recover(); err != nil {
				app.Suspend(func() {
					panic(err)
				})
			}
		}()
		for {
			st, err := c.Status()
			viewmodelupdate(func(p *viewmodel.Params) {
				p.Report = st.Jobs
				p.ReportFetchError = err
			})
			app.QueueUpdateDraw(redraw)

			time.Sleep(flag.Delay)
		}
	}()

	jobMenu.SetChangedFunc(func(jobN *tview.TreeNode) {
		viewmodelupdate(func(p *viewmodel.Params) {
			p.SelectedJob, _ = jobN.GetReference().(*viewmodel.Job)
		})
		redraw()
		jobTextDetail.ScrollToBeginning()
	})
	jobMenu.SetSelectedFunc(func(jobN *tview.TreeNode) {
		app.SetFocus(jobTextDetail)
	})

	app.SetBeforeDrawFunc(func(screen tcell.Screen) bool {
		viewmodelupdate(func(p *viewmodel.Params) {
			_, _, p.DetailViewWidth, _ = jobTextDetail.GetInnerRect()
		})
		return false
	})

	app.SetInputCapture(func(e *tcell.EventKey) *tcell.EventKey {
		if e.Key() == tcell.KeyTab {
			tabbableCycle()
			return nil
		}

		if e.Key() == tcell.KeyRune && app.GetFocus() == fsFilterInput {
			return e
		}

		if e.Key() == tcell.KeyRune && e.Rune() == '/' {
			if app.GetFocus() != fsFilterInput {
				app.SetFocus(fsFilterInput)
			}
			return e
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'M' {
			reconfigureJobDetailSplit(!jobMenuVisisble)
			return nil
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'q' {
			app.Stop()
		}

		if e.Key() == tcell.KeyRune && e.Rune() == 'S' {
			job, ok := jobMenu.GetCurrentNode().GetReference().(*viewmodel.Job)
			if !ok {
				return nil
			}
			signals := []string{"wakeup", "reset"}
			clientFuncs := []func(job string) error{c.SignalWakeup, c.SignalReset}
			sigMod := tview.NewModal()
			sigMod.SetBackgroundColor(tcell.ColorDefault)
			sigMod.SetBorder(true)
			sigMod.GetForm().SetButtonTextColorFocused(tcell.ColorGreen)
			sigMod.AddButtons(signals)
			sigMod.SetText(fmt.Sprintf("Send a signal to job %q", job.Name()))
			showModal(sigMod, func(idx int, _ string) {
				go func() {
					if idx == -1 {
						return
					}
					err := clientFuncs[idx](job.Name())
					if err != nil {
						app.QueueUpdate(func() {
							me := tview.NewModal()
							me.SetText(fmt.Sprintf("signal error: %s", err))
							me.AddButtons([]string{"Close"})
							showModal(me, nil)
						})
					}
				}()
			})
		}

		return e
	})

	fsFilterInput.SetChangedFunc(func(searchterm string) {
		viewmodelupdate(func(p *viewmodel.Params) {
			p.FSFilter = func(fs string) bool {
				r, err := regexp.Compile(searchterm)
				if err != nil {
					return true
				}
				return r.MatchString(fs)
			}
		})
		redraw()
		jobTextDetail.ScrollToBeginning()
	})
	fsFilterInput.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		if event.Key() == tcell.KeyEnter {
			app.SetFocus(jobTextDetail)
			return nil
		}
		return event
	})

	jobTextDetail.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		if event.Key() == tcell.KeyRune && event.Rune() == 'w' {
			// toggle wrapping
			viewmodelupdate(func(p *viewmodel.Params) {
				p.DetailViewWrap = !p.DetailViewWrap
			})
			redraw()
			return nil
		}
		return event
	})

	return app.Run()
}
<|endoftext|>


<commit_before>package kodingcontext

import (
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/mitchellh/goamz/s3"
)

type Storage interface {
	Write(string, io.Reader) error
	Read(string) (io.Reader, error)
	Remove(string) error
	Clone(string, Storage) error
	BasePath() (string, error)
	Clean(string) error
}

var _ Storage = S3Storage{}
var _ Storage = FileStorage{}

type S3Storage struct {
	// Bucket holds the plans of terraform
	bucket *s3.Bucket
}

func NewS3Storage(bucket *s3.Bucket) S3Storage {
	return S3Storage{
		bucket: bucket,
	}
}

func (s S3Storage) BasePath() (string, error) {
	return "", nil
}

func (s S3Storage) Clean(path string) error {
	return nil
}

func (s S3Storage) Write(path string, file io.Reader) error {
	// TODO(cihangir): we can use bucket.PutReader here
	content, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	err = s.bucket.Put(path, content, "application/json", s3.Private)
	if err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Remove(path string) error {
	if err := s.bucket.Del(path); err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Read(path string) (io.Reader, error) {
	if r, err := s.bucket.GetReader(path); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func (s S3Storage) Clone(path string, target Storage) error {
	filePath := path + "/"
	// Limits the response to keys that begin with the specified prefix. You can
	// use prefixes to separate a bucket into different groupings of keys. (You
	// can think of using prefix to make groups in the same way you'd use a
	// folder in a file system.)
	prefix := filePath

	// If you don't specify the prefix parameter, then the substring starts at
	// the beginning of the key
	delim := ""

	// Specifies the key to start with when listing objects in a bucket. Amazon
	// S3 returns object keys in alphabetical order, starting with key after the
	// marker in order.
	marker := ""

	// Sets the maximum number of keys returned in the response body. You can
	// add this to your request if you want to retrieve fewer than the default
	// 1000 keys.
	max := 0

	// read all elements in a bucket, we are gonna have more than 1000 items in
	// that bucket/folder
	result, err := s.bucket.List(prefix, delim, marker, max)
	if err != nil {
		return err
	}

	// write them all to target
	for _, res := range result.Contents {
		newPath := res.Key
		r, err := s.Read(newPath)
		if err != nil {
			return err
		}

		if err := target.Write(newPath, r); err != nil {
			return err
		}
	}

	return nil
}

type FileStorage struct {
	basePath string
}

func NewFileStorage(basePath string) FileStorage {
	return FileStorage{
		basePath: basePath,
	}
}

func (f FileStorage) BasePath() (string, error) {
	if f.basePath != "" {
		return f.basePath, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "storage")
	if err != nil {
		return "", err
	}

	f.basePath = dir

	return f.basePath, nil
}

func (f FileStorage) Clean(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	return os.RemoveAll(fullPath)
}

func (f FileStorage) Write(filePath string, file io.Reader) (err error) {
	contents := strings.Split(filePath, string(os.PathSeparator))

	dirPath, err := f.fullPath(strings.Join(contents[:len(contents)-1], string(os.PathSeparator)))
	if err != nil {
		return err
	}

	if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
		return err
	}

	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	tf, err := os.Create(fullPath)
	if err != nil {
		return err
	}

	defer func() {
		// Sync commits the current contents of the file to disk
		if err = tf.Sync(); err != nil {
			return
		}

		if err = tf.Close(); err != nil {
			return
		}
	}()

	_, err = io.Copy(tf, file)
	return err
}

func (f FileStorage) Remove(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	if err := os.RemoveAll(fullPath); err != nil {
		return err
	}

	return nil
}

func (f FileStorage) Read(filePath string) (io.Reader, error) {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return nil, err
	}

	r, err := os.Open(fullPath)
	if err != nil {
		return nil, err
	}

	return r, nil
}

func (f FileStorage) Clone(filePath string, target Storage) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	fileInfos, err := ioutil.ReadDir(fullPath)
	if err != nil {
		return err
	}

	for _, fileInfo := range fileInfos {
		fnPath := path.Join(filePath, fileInfo.Name())

		file, err := f.Read(fnPath)
		if err != nil {
			return err
		}

		fpath := path.Join(filePath, fileInfo.Name())
		if err := target.Write(fpath, file); err != nil {
			return err
		}
	}

	return nil
}

func (f FileStorage) fullPath(filePath string) (string, error) {
	dir, err := f.BasePath()
	if err != nil {
		return "", err
	}

	return path.Join(dir, filePath), nil
}
<commit_msg>Terraformer: fix edge case where we have the folder in a bucket but no content in it<commit_after>package kodingcontext

import (
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/mitchellh/goamz/s3"
)

type Storage interface {
	Write(string, io.Reader) error
	Read(string) (io.Reader, error)
	Remove(string) error
	Clone(string, Storage) error
	BasePath() (string, error)
	Clean(string) error
}

var _ Storage = S3Storage{}
var _ Storage = FileStorage{}

type S3Storage struct {
	// Bucket holds the plans of terraform
	bucket *s3.Bucket
}

func NewS3Storage(bucket *s3.Bucket) S3Storage {
	return S3Storage{
		bucket: bucket,
	}
}

func (s S3Storage) BasePath() (string, error) {
	return "", nil
}

func (s S3Storage) Clean(path string) error {
	return nil
}

func (s S3Storage) Write(path string, file io.Reader) error {
	// TODO(cihangir): we can use bucket.PutReader here
	content, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	err = s.bucket.Put(path, content, "application/json", s3.Private)
	if err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Remove(path string) error {
	if err := s.bucket.Del(path); err != nil {
		return err
	}

	return nil
}

func (s S3Storage) Read(path string) (io.Reader, error) {
	if r, err := s.bucket.GetReader(path); err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

func (s S3Storage) Clone(path string, target Storage) error {
	filePath := path + "/"
	// Limits the response to keys that begin with the specified prefix. You can
	// use prefixes to separate a bucket into different groupings of keys. (You
	// can think of using prefix to make groups in the same way you'd use a
	// folder in a file system.)
	prefix := filePath

	// If you don't specify the prefix parameter, then the substring starts at
	// the beginning of the key
	delim := ""

	// Specifies the key to start with when listing objects in a bucket. Amazon
	// S3 returns object keys in alphabetical order, starting with key after the
	// marker in order.
	marker := ""

	// Sets the maximum number of keys returned in the response body. You can
	// add this to your request if you want to retrieve fewer than the default
	// 1000 keys.
	max := 0

	// read all elements in a bucket, we are gonna have more than 1000 items in
	// that bucket/folder
	result, err := s.bucket.List(prefix, delim, marker, max)
	if err != nil {
		return err
	}

	// write them all to target
	for _, res := range result.Contents {
		newPath := res.Key

		// if bucket is created but doesnt have any content
		if newPath == filePath {
			continue
		}

		r, err := s.Read(newPath)
		if err != nil {
			return err
		}

		if err := target.Write(newPath, r); err != nil {
			return err
		}
	}

	return nil
}

type FileStorage struct {
	basePath string
}

func NewFileStorage(basePath string) FileStorage {
	return FileStorage{
		basePath: basePath,
	}
}

func (f FileStorage) BasePath() (string, error) {
	if f.basePath != "" {
		return f.basePath, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "storage")
	if err != nil {
		return "", err
	}

	f.basePath = dir

	return f.basePath, nil
}

func (f FileStorage) Clean(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	return os.RemoveAll(fullPath)
}

func (f FileStorage) Write(filePath string, file io.Reader) (err error) {
	contents := strings.Split(filePath, string(os.PathSeparator))

	dirPath, err := f.fullPath(strings.Join(contents[:len(contents)-1], string(os.PathSeparator)))
	if err != nil {
		return err
	}

	if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
		return err
	}

	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	tf, err := os.Create(fullPath)
	if err != nil {
		return err
	}

	defer func() {
		// Sync commits the current contents of the file to disk
		if err = tf.Sync(); err != nil {
			return
		}

		if err = tf.Close(); err != nil {
			return
		}
	}()

	_, err = io.Copy(tf, file)
	return err
}

func (f FileStorage) Remove(filePath string) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	if err := os.RemoveAll(fullPath); err != nil {
		return err
	}

	return nil
}

func (f FileStorage) Read(filePath string) (io.Reader, error) {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return nil, err
	}

	r, err := os.Open(fullPath)
	if err != nil {
		return nil, err
	}

	return r, nil
}

func (f FileStorage) Clone(filePath string, target Storage) error {
	fullPath, err := f.fullPath(filePath)
	if err != nil {
		return err
	}

	fileInfos, err := ioutil.ReadDir(fullPath)
	if err != nil {
		return err
	}

	for _, fileInfo := range fileInfos {
		fnPath := path.Join(filePath, fileInfo.Name())

		file, err := f.Read(fnPath)
		if err != nil {
			return err
		}

		fpath := path.Join(filePath, fileInfo.Name())
		if err := target.Write(fpath, file); err != nil {
			return err
		}
	}

	return nil
}

func (f FileStorage) fullPath(filePath string) (string, error) {
	dir, err := f.BasePath()
	if err != nil {
		return "", err
	}

	return path.Join(dir, filePath), nil
}
<|endoftext|>


<commit_before>package couchdb

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// This file contains error handling code for couchdb request
// Possible errors in connecting to couchdb
// 503 Service Unavailable when the stack cant connect to couchdb or when
// 		 couchdb response is interrupted mid-stream
// 500 When the viper provided configuration does not allow us to properly
// 		 call http.newRequest, ie. wrong couchdbURL config

// Possible native couchdb errors
// 400 Bad Request : Bad request structure. The error can indicate an error
// 		with the request URL, path or headers. Differences in the supplied MD5
// 		hash and content also trigger this error, as this may indicate message
// 		corruption.
// 401 Unauthorized : The item requested was not available using the supplied
// 		authorization, or authorization was not supplied.
// 		{"error":"unauthorized","reason":"You are not a server admin."}
// 		{"error":"unauthorized","reason":"Name or password is incorrect."}
// 403 Forbidden : The requested item or operation is forbidden.
// 404 Not Found : The requested content could not be found. The content will
// 		include further information, as a JSON object, if available.
// 		**The structure will contain two keys, error and reason.**
//    {"error":"not_found","reason":"deleted"}
//    {"error":"not_found","reason":"missing"}
//    {"error":"not_found","reason":"no_db_file"}
// 405 Resource Not Allowed : A request was made using an invalid HTTP request
// 		type for the URL requested. For example, you have requested a PUT when a
// 		POST is required. Errors of this type can also triggered by invalid URL
// 		strings.
// 406 Not Acceptable : The requested content type is not supported by the
// 		server.
// 409 Conflict : Request resulted in an update conflict.
// 		{"error":"conflict","reason":"Document update conflict."}
// 412 Precondition Failed : The request headers from the client and the
// 		capabilities of the server do not match.
// 415 Bad Content Type : The content types supported, and the content type of
// 		the information being requested or submitted indicate that the content
// 		type is not supported.
// 416 Requested Range Not Satisfiable : The range specified in the request
// 		header cannot be satisfied by the server.
// 417 Expectation Failed : When sending documents in bulk, the bulk load
// 		operation failed.
// 500 Internal Server Error : The request was invalid, either because the
// 		supplied JSON was invalid, or invalid information was supplied as part
// 		of the request.

// Error represent an error from couchdb
type Error struct {
	StatusCode  int
	CouchdbJSON []byte `json:"-"`
	Name        string `json:"error"`
	Reason      string `json:"reason"`
	Original    error
}

func (e *Error) Error() string {
	if e.CouchdbJSON != nil {
		return fmt.Sprintf("CouchdbError %d : %s", e.StatusCode, e.CouchdbJSON)
	}
	return fmt.Sprintf("CouchdbError %d : %s(%s)", e.StatusCode, e.Name, e.Reason)
}

// JSON returns the hash to output in HTTP for a given error
func (e *Error) JSON() map[string]interface{} {
	jsonMap := map[string]interface{}{
		"status": string(e.StatusCode),
		"error":  e.Name,
		"reason": e.Reason,
	}
	if e.Original != nil {
		jsonMap["original"] = e.Original.Error()
	}
	return jsonMap
}

func isNoDatabaseError(err error) bool {
	if err == nil {
		return false
	}
	coucherr, iscoucherr := err.(*Error)
	if !iscoucherr {
		return false
	}
	return coucherr.Reason == "no_db_file" ||
		coucherr.Reason == "Database does not exist."
}

func newRequestError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "wrong_config",
		Original:   originalError,
	}
}

func newConnectionError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "cant_connect",
		Original:   originalError,
	}
}

func newIOReadError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "hangup",
		Original:   originalError,
	}
}

func newCouchdbError(statusCode int, couchdbJSON []byte) error {
	var err = &Error{
		CouchdbJSON: couchdbJSON,
	}
	parseErr := json.Unmarshal(couchdbJSON, err)
	if parseErr != nil {
		err.Name = "wrong_json"
		err.Reason = parseErr.Error()
	}
	err.StatusCode = statusCode
	return err
}
<commit_msg>Fix misleading comment<commit_after>package couchdb

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// This file contains error handling code for couchdb request
// Possible errors in connecting to couchdb
// 503 Service Unavailable when the stack cant connect to couchdb or when
// 		 couchdb response is interrupted mid-stream
// 500 When the viper provided configuration does not allow us to properly
// 		 call http.newRequest, ie. wrong couchdbURL config

// Possible native couchdb errors
// 400 Bad Request : Bad request structure. The error can indicate an error
// 		with the request URL, path or headers. Differences in the supplied MD5
// 		hash and content also trigger this error, as this may indicate message
// 		corruption.
// 401 Unauthorized : The item requested was not available using the supplied
// 		authorization, or authorization was not supplied.
// 		{"error":"unauthorized","reason":"You are not a server admin."}
// 		{"error":"unauthorized","reason":"Name or password is incorrect."}
// 403 Forbidden : The requested item or operation is forbidden.
// 404 Not Found : The requested content could not be found. The content will
// 		include further information, as a JSON object, if available.
// 		**The structure will contain two keys, error and reason.**
//    {"error":"not_found","reason":"deleted"}
//    {"error":"not_found","reason":"missing"}
//    {"error":"not_found","reason":"no_db_file"}
// 405 Resource Not Allowed : A request was made using an invalid HTTP request
// 		type for the URL requested. For example, you have requested a PUT when a
// 		POST is required. Errors of this type can also triggered by invalid URL
// 		strings.
// 406 Not Acceptable : The requested content type is not supported by the
// 		server.
// 409 Conflict : Request resulted in an update conflict.
// 		{"error":"conflict","reason":"Document update conflict."}
// 412 Precondition Failed : The request headers from the client and the
// 		capabilities of the server do not match.
// 415 Bad Content Type : The content types supported, and the content type of
// 		the information being requested or submitted indicate that the content
// 		type is not supported.
// 416 Requested Range Not Satisfiable : The range specified in the request
// 		header cannot be satisfied by the server.
// 417 Expectation Failed : When sending documents in bulk, the bulk load
// 		operation failed.
// 500 Internal Server Error : The request was invalid, either because the
// 		supplied JSON was invalid, or invalid information was supplied as part
// 		of the request.

// Error represent an error from couchdb
type Error struct {
	StatusCode  int
	CouchdbJSON []byte `json:"-"`
	Name        string `json:"error"`
	Reason      string `json:"reason"`
	Original    error
}

func (e *Error) Error() string {
	if e.CouchdbJSON != nil {
		return fmt.Sprintf("CouchdbError %d : %s", e.StatusCode, e.CouchdbJSON)
	}
	return fmt.Sprintf("CouchdbError %d : %s(%s)", e.StatusCode, e.Name, e.Reason)
}

// JSON returns the json representation of this error
func (e *Error) JSON() map[string]interface{} {
	jsonMap := map[string]interface{}{
		"status": string(e.StatusCode),
		"error":  e.Name,
		"reason": e.Reason,
	}
	if e.Original != nil {
		jsonMap["original"] = e.Original.Error()
	}
	return jsonMap
}

func isNoDatabaseError(err error) bool {
	if err == nil {
		return false
	}
	coucherr, iscoucherr := err.(*Error)
	if !iscoucherr {
		return false
	}
	return coucherr.Reason == "no_db_file" ||
		coucherr.Reason == "Database does not exist."
}

func newRequestError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "wrong_config",
		Original:   originalError,
	}
}

func newConnectionError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "cant_connect",
		Original:   originalError,
	}
}

func newIOReadError(originalError error) error {
	return &Error{
		StatusCode: http.StatusServiceUnavailable,
		Name:       "no_couch",
		Reason:     "hangup",
		Original:   originalError,
	}
}

func newCouchdbError(statusCode int, couchdbJSON []byte) error {
	var err = &Error{
		CouchdbJSON: couchdbJSON,
	}
	parseErr := json.Unmarshal(couchdbJSON, err)
	if parseErr != nil {
		err.Name = "wrong_json"
		err.Reason = parseErr.Error()
	}
	err.StatusCode = statusCode
	return err
}
<|endoftext|>


<commit_before><commit_msg>veyron.io/veyron/veyron/runtimes/google/ipc/stream/crypto: Run tls_generate_old.sh to generate tls_old.go based on new tls.go, to fix Jenkins build.<commit_after><|endoftext|>


<commit_before>package analytics

//
// dependencies
//

import "github.com/jehiah/go-strftime"
import "github.com/nu7hatch/gouuid"
import . "encoding/json"
import "net/http"
import "bytes"
import "time"
import "log"

//
// Library version
//

const Version = "0.0.1"

//
// Default API end-point
//

const api = "https://api.segment.io"

//
// Segment.io client
//

type Client struct {
	Debug         bool
	BufferSize    int
	FlushInterval time.Duration
	key           string
	url           string
	buffer        []*interface{}
}

//
// Message context library
//

type contextLibrary struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

//
// Message context
//

type context struct {
	Library contextLibrary `json:"library"`
}

//
// Identify message
//

type identify struct {
	Action    string      `json:"action"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Alias message
//

type alias struct {
	Action     string `json:"action"`
	PreviousId string `json:"previousId"`
	Timestamp  string `json:"timestamp"`
}

//
// Track message
//

type track struct {
	Action     string      `json:"action"`
	Event      string      `json:"event"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Group message
//

type group struct {
	Action    string      `json:"action"`
	GroupId   string      `json:"groupId"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Page message
//

type page struct {
	Action     string      `json:"action"`
	Category   string      `json:"category"`
	Name       string      `json:"name"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Batch message
//

type batch struct {
	Context   context        `json:"context"`
	RequestId string         `json:"requestId"`
	Messages  []*interface{} `json:"batch"`
}

//
// Return a new Segment.io client
// with the given write key.
//

func New(key string) (c *Client) {
	defer func() {
		go func() {
			for {
				time.Sleep(c.FlushInterval)
				c.log("interval %v reached", c.FlushInterval)
				c.flush()
			}
		}()
	}()

	return &Client{
		Debug:         false,
		BufferSize:    500,
		FlushInterval: 10 * time.Second,
		key:           key,
		url:           api,
		buffer:        make([]*interface{}, 0),
	}
}

//
// Set target url
//

func (c *Client) URL(url string) {
	c.url = url
}

//
// Return formatted timestamp.
//

func timestamp() string {
	return strftime.Format("%Y-%m-%dT%H:%M:%S%z", time.Now())
}

// Return a batch message primed
// with context properties
//

func createBatch(msgs []*interface{}) (*batch, error) {
	uid, err := uuid.NewV4()

	if err != nil {
		return nil, err
	}

	batch := &batch{
		RequestId: uid.String(),
		Messages:  msgs,
		Context: context{
			Library: contextLibrary{
				Name:    "analytics-go",
				Version: Version,
			},
		},
	}

	return batch, nil
}

//
// Flush the buffered messages.
//

func (c *Client) flush() error {
	if len(c.buffer) == 0 {
		c.log("no messages to flush")
		return nil
	}

	c.log("flushing %d messages", len(c.buffer))
	batch, err := createBatch(c.buffer)

	if err != nil {
		return err
	}

	json, err := Marshal(batch)

	if err != nil {
		return err
	}

	c.buffer = nil

	client := &http.Client{}
	req, err := http.NewRequest("POST", c.url+"/v1/batch", bytes.NewBuffer(json))

	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(json)))
	req.SetBasicAuth(c.key, "")

	_, err = client.Do(req)

	return err
}

//
// Buffer the given message and flush
// when the buffer exceeds .BufferSize.
//

func (c *Client) bufferMessage(msg interface{}) error {
	c.buffer = append(c.buffer, &msg)

	c.log("buffer (%d/%d) %v", len(c.buffer), c.BufferSize, msg)

	if len(c.buffer) >= c.BufferSize {
		return c.flush()
	}

	return nil
}

//
// Log in debug mode.
//

func (c *Client) log(format string, v ...interface{}) {
	if c.Debug {
		log.Printf(format, v...)
	}
}

//
// Buffer an alias message
//

func (c *Client) Alias(previousId string) error {
	return c.bufferMessage(&alias{"Alias", previousId, timestamp()})
}

//
// Buffer a page message
//

func (c *Client) Page(name string, category string, properties interface{}) error {
	return c.bufferMessage(&page{"Page", name, category, properties, timestamp()})
}

//
// Buffer a screen message
//

func (c *Client) Screen(name string, category string, properties interface{}) error {
	return c.bufferMessage(&page{"Screen", name, category, properties, timestamp()})
}

//
// Buffer a group message
//

func (c *Client) Group(id string, traits interface{}) error {
	return c.bufferMessage(&group{"Group", id, traits, timestamp()})
}

//
// Buffer an identify message
//

func (c *Client) Identify(traits interface{}) error {
	return c.bufferMessage(&identify{"Identify", traits, timestamp()})
}

//
// Buffer a track message
//

func (c *Client) Track(event string, properties interface{}) error {
	return c.bufferMessage(&track{"Track", event, properties, timestamp()})
}
<commit_msg>replace .URL() with .Endpoint<commit_after>package analytics

//
// dependencies
//

import "github.com/jehiah/go-strftime"
import "github.com/nu7hatch/gouuid"
import . "encoding/json"
import "net/http"
import "bytes"
import "time"
import "log"

//
// Library version
//

const Version = "0.0.1"

//
// Default API end-point
//

const api = "https://api.segment.io"

//
// Segment.io client
//

type Client struct {
	Debug         bool
	BufferSize    int
	FlushInterval time.Duration
	Endpoint      string
	Key           string
	buffer        []*interface{}
}

//
// Message context library
//

type contextLibrary struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

//
// Message context
//

type context struct {
	Library contextLibrary `json:"library"`
}

//
// Identify message
//

type identify struct {
	Action    string      `json:"action"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Alias message
//

type alias struct {
	Action     string `json:"action"`
	PreviousId string `json:"previousId"`
	Timestamp  string `json:"timestamp"`
}

//
// Track message
//

type track struct {
	Action     string      `json:"action"`
	Event      string      `json:"event"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Group message
//

type group struct {
	Action    string      `json:"action"`
	GroupId   string      `json:"groupId"`
	Traits    interface{} `json:"trailts"`
	Timestamp string      `json:"timestamp"`
}

//
// Page message
//

type page struct {
	Action     string      `json:"action"`
	Category   string      `json:"category"`
	Name       string      `json:"name"`
	Properties interface{} `json:"properties"`
	Timestamp  string      `json:"timestamp"`
}

//
// Batch message
//

type batch struct {
	Context   context        `json:"context"`
	RequestId string         `json:"requestId"`
	Messages  []*interface{} `json:"batch"`
}

//
// Return a new Segment.io client
// with the given write key.
//

func New(key string) (c *Client) {
	defer func() {
		go func() {
			for {
				time.Sleep(c.FlushInterval)
				c.log("interval %v reached", c.FlushInterval)
				c.flush()
			}
		}()
	}()

	return &Client{
		Debug:         false,
		BufferSize:    500,
		FlushInterval: 10 * time.Second,
		Key:           key,
		Endpoint:      api,
		buffer:        make([]*interface{}, 0),
	}
}

//
// Return formatted timestamp.
//

func timestamp() string {
	return strftime.Format("%Y-%m-%dT%H:%M:%S%z", time.Now())
}

// Return a batch message primed
// with context properties
//

func createBatch(msgs []*interface{}) (*batch, error) {
	uid, err := uuid.NewV4()

	if err != nil {
		return nil, err
	}

	batch := &batch{
		RequestId: uid.String(),
		Messages:  msgs,
		Context: context{
			Library: contextLibrary{
				Name:    "analytics-go",
				Version: Version,
			},
		},
	}

	return batch, nil
}

//
// Flush the buffered messages.
//

func (c *Client) flush() error {
	if len(c.buffer) == 0 {
		c.log("no messages to flush")
		return nil
	}

	c.log("flushing %d messages", len(c.buffer))
	batch, err := createBatch(c.buffer)

	if err != nil {
		return err
	}

	json, err := Marshal(batch)

	if err != nil {
		return err
	}

	c.buffer = nil

	client := &http.Client{}
	url := c.Endpoint + "/v1/batch"
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(json))

	if err != nil {
		return err
	}

	req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Content-Length", string(len(json)))
	req.SetBasicAuth(c.Key, "")

	_, err = client.Do(req)

	return err
}

//
// Buffer the given message and flush
// when the buffer exceeds .BufferSize.
//

func (c *Client) bufferMessage(msg interface{}) error {
	c.buffer = append(c.buffer, &msg)

	c.log("buffer (%d/%d) %v", len(c.buffer), c.BufferSize, msg)

	if len(c.buffer) >= c.BufferSize {
		return c.flush()
	}

	return nil
}

//
// Log in debug mode.
//

func (c *Client) log(format string, v ...interface{}) {
	if c.Debug {
		log.Printf(format, v...)
	}
}

//
// Buffer an alias message
//

func (c *Client) Alias(previousId string) error {
	return c.bufferMessage(&alias{"Alias", previousId, timestamp()})
}

//
// Buffer a page message
//

func (c *Client) Page(name string, category string, properties interface{}) error {
	return c.bufferMessage(&page{"Page", name, category, properties, timestamp()})
}

//
// Buffer a screen message
//

func (c *Client) Screen(name string, category string, properties interface{}) error {
	return c.bufferMessage(&page{"Screen", name, category, properties, timestamp()})
}

//
// Buffer a group message
//

func (c *Client) Group(id string, traits interface{}) error {
	return c.bufferMessage(&group{"Group", id, traits, timestamp()})
}

//
// Buffer an identify message
//

func (c *Client) Identify(traits interface{}) error {
	return c.bufferMessage(&identify{"Identify", traits, timestamp()})
}

//
// Buffer a track message
//

func (c *Client) Track(event string, properties interface{}) error {
	return c.bufferMessage(&track{"Track", event, properties, timestamp()})
}
<|endoftext|>


<commit_before>package colorquant

import (
	"testing"
	"image"
	"image/color/palette"
	"image/color"
)

func TestQuant_Paletted(t *testing.T) {
	img := image.NewPaletted(image.Rect(0, 0, 10, 10), palette.WebSafe)

	res := Quant{}.Quantize(img, 10)
	if p, ok := res.(*image.Paletted); ok {
		palette := make([][4]int32, len(p.Palette))
		for i, col := range p.Palette {
			r, g, b, a := col.RGBA()
			palette[i][0] = int32(r)
			palette[i][1] = int32(g)
			palette[i][2] = int32(b)
			palette[i][3] = int32(a)
		}

		if palette == nil {
			t.Errorf("The expected image should be a paletted image!")
		}
	}
}

func TestQuant_Median(t *testing.T) {
	img := image.NewPaletted(image.Rect(0, 0, 10, 10), palette.WebSafe)
	for i := 0; i < img.Bounds().Dx(); i++ {
		for j := 0; j < img.Bounds().Dy(); j ++ {
			img.Set(i, j, color.RGBA{0xff, 0, 0, 0})
		}
	}
	qz := newQuantizer(img, 2)
	qz.cluster()

	cls := &cluster{
		[]point{
			{0, 0},
			{1, 0},
			{0, 1},
			{1, 1},
		}, 1, 1,
	}
	res := qz.Median(cls)
	if res != 0 {
		t.Errorf("The expected result should be 0, got %d", res)
	}
}<commit_msg>Add quantization level test<commit_after>package colorquant

import (
	"testing"
	"image"
	"image/color/palette"
	"image/color"
	"math/rand"
)

func TestQuant_Paletted(t *testing.T) {
	img := image.NewPaletted(image.Rect(0, 0, 10, 10), palette.WebSafe)

	res := Quant{}.Quantize(img, 10)
	if p, ok := res.(*image.Paletted); ok {
		palette := make([][4]int32, len(p.Palette))
		for i, col := range p.Palette {
			r, g, b, a := col.RGBA()
			palette[i][0] = int32(r)
			palette[i][1] = int32(g)
			palette[i][2] = int32(b)
			palette[i][3] = int32(a)
		}

		if palette == nil {
			t.Errorf("The expected image should be a paletted image!")
		}
	}
}

func TestQuant_Median(t *testing.T) {
	img := image.NewPaletted(image.Rect(0, 0, 10, 10), palette.WebSafe)
	for i := 0; i < img.Bounds().Dx(); i++ {
		for j := 0; j < img.Bounds().Dy(); j ++ {
			img.Set(i, j, color.RGBA{0xff, 0, 0, 0})
		}
	}
	qz := newQuantizer(img, 2)
	qz.cluster()

	cls := &cluster{
		[]point{
			{0, 0},
			{1, 0},
			{0, 1},
			{1, 1},
		}, 1, 1,
	}
	res := qz.Median(cls)
	if res != 0 {
		t.Errorf("The expected result should be 0, got %d", res)
	}
}

func TestQuant_Level (t *testing.T) {
	quantLevel := 10
	reds := []uint32{}
	img := image.NewRGBA(image.Rect(0, 0, 10, 10))

	for i := 0; i < img.Bounds().Dx(); i++ {
		for j := 0; j < img.Bounds().Dy(); j ++ {
			col := rand.Intn(255)
			img.Set(i, j, color.RGBA{uint8(col), 0, 0, 0})
		}
	}

	res := Quant{}.Quantize(img, quantLevel)

	if p, ok := res.(*image.Paletted); ok {
		for _, col := range p.Palette {
			r, _, _, _ := col.RGBA()
			reds = append(reds, r)
		}

		if len(reds) != quantLevel {
			t.Errorf("The quantization level should be %d, got %d", quantLevel, len(reds))
		}
	}
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.61"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<commit_msg>fn tool: 0.3.62 release [skip ci]<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.62"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"net/http"
	"path"
	"strings"

	"github.com/gin-gonic/gin"
	"gitlab-odx.oracle.com/odx/functions/api"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/runner/common"
)

/* handleRouteCreateOrUpdate is used to handle POST PUT and PATCH for routes.
   Post will only create route if its not there and create app if its not.
       create only
	   Post does not skip validation of zero values
   Put will create app if its not there and if route is there update if not it will create new route.
       update if exists or create if not exists
	   Put does not skip validation of zero values
   Patch will not create app if it does not exist since the route needs to exist as well...
       update only
	   Patch accepts partial updates / skips validation of zero values.
*/
func (s *Server) handleRouteCreateOrUpdate(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := common.Logger(ctx)
	method := strings.ToUpper(c.Request.Method)

	var wroute models.RouteWrapper

	err, resperr := s.bindAndValidate(ctx, c, method, &wroute)
	if err != nil {
		log.WithError(err).Debug(resperr)
		c.JSON(http.StatusBadRequest, simpleError(resperr))
		return
	}

	// Create the app if it does not exist.
	err, resperr = s.ensureApp(ctx, c, wroute, method)
	if err != nil {
		log.WithError(err).Debug(resperr)
		handleErrorResponse(c, resperr)
		return
	}

	resp, err := s.updateOrInsertRoute(ctx, method, wroute)
	if err != nil {
		handleErrorResponse(c, err)
		return
	}

	s.cacheRefresh(resp.Route)

	c.JSON(http.StatusOK, resp)
}

// ensureApp will only execute if it is on post or put. Patch is not allowed to create apps.
func (s *Server) ensureApp(ctx context.Context, c *gin.Context, wroute models.RouteWrapper, method string) (error, error) {
	if !(method == http.MethodPost || method == http.MethodPut) {
		return nil, nil
	}
	var app *models.App
	var err error
	app, err = s.Datastore.GetApp(ctx, wroute.Route.AppName)
	if err != nil && err != models.ErrAppsNotFound {
		return err, models.ErrAppsGet
	} else if app == nil {
		// Create a new application
		newapp := &models.App{Name: wroute.Route.AppName}
		if err = newapp.Validate(); err != nil {
			return err, err
		}

		err = s.FireBeforeAppCreate(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

		_, err = s.Datastore.InsertApp(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

		err = s.FireAfterAppCreate(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

	}
	return nil, nil
}

/* bindAndValidate binds the RouteWrapper to the json from the request and validates that it is correct.
If it is a put or patch it makes sure that the path in the url matches the provideed one in the body.
Defaults are set and if patch skipZero is true for validating the RouteWrapper
*/
func (s *Server) bindAndValidate(ctx context.Context, c *gin.Context, method string, wroute *models.RouteWrapper) (error, error) {
	err := c.BindJSON(wroute)
	if err != nil {
		return err, models.ErrInvalidJSON
	}

	if wroute.Route == nil {
		return err, models.ErrRoutesMissingNew
	}
	wroute.Route.AppName = c.MustGet(api.AppName).(string)

	if method == http.MethodPut || method == http.MethodPatch {
		p := path.Clean(c.MustGet(api.Path).(string))

		if wroute.Route.Path != "" && wroute.Route.Path != p {
			return models.ErrRoutesPathImmutable, models.ErrRoutesPathImmutable
		}
		wroute.Route.Path = p
	}

	wroute.Route.SetDefaults()

	if err = wroute.Validate(method == http.MethodPatch); err != nil {
		return models.ErrRoutesCreate, err
	}
	return nil, nil
}

// updateOrInsertRoute will either update or insert the route respective the method.
func (s *Server) updateOrInsertRoute(ctx context.Context, method string, wroute models.RouteWrapper) (routeResponse, error) {
	var route *models.Route
	var err error
	resp := routeResponse{"Route successfully created", nil}
	up := routeResponse{"Route successfully updated", nil}

	switch method {
	case http.MethodPost:
		route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
	case http.MethodPut:
		route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
		if err == models.ErrRoutesNotFound {
			// try insert then
			route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
		}
	case http.MethodPatch:
		// When patching if there is an error around the app we will return one and the update fails.
		route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
		resp = up
	}
	resp.Route = route
	return resp, err
}
<commit_msg>Check both errors<commit_after>package server

import (
	"context"
	"net/http"
	"path"
	"strings"

	"github.com/gin-gonic/gin"
	"gitlab-odx.oracle.com/odx/functions/api"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/runner/common"
)

/* handleRouteCreateOrUpdate is used to handle POST PUT and PATCH for routes.
   Post will only create route if its not there and create app if its not.
       create only
	   Post does not skip validation of zero values
   Put will create app if its not there and if route is there update if not it will create new route.
       update if exists or create if not exists
	   Put does not skip validation of zero values
   Patch will not create app if it does not exist since the route needs to exist as well...
       update only
	   Patch accepts partial updates / skips validation of zero values.
*/
func (s *Server) handleRouteCreateOrUpdate(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := common.Logger(ctx)
	method := strings.ToUpper(c.Request.Method)

	var wroute models.RouteWrapper

	err, resperr := s.bindAndValidate(ctx, c, method, &wroute)
	if err != nil || resperr != nil {
		log.WithError(err).Debug(resperr)
		c.JSON(http.StatusBadRequest, simpleError(resperr))
		return
	}

	// Create the app if it does not exist.
	err, resperr = s.ensureApp(ctx, c, &wroute, method)
	if err != nil || resperr != nil {
		log.WithError(err).Debug(resperr)
		handleErrorResponse(c, resperr)
		return
	}

	resp, err := s.updateOrInsertRoute(ctx, method, wroute)
	if err != nil {
		handleErrorResponse(c, err)
		return
	}

	s.cacheRefresh(resp.Route)

	c.JSON(http.StatusOK, resp)
}

// ensureApp will only execute if it is on post or put. Patch is not allowed to create apps.
func (s *Server) ensureApp(ctx context.Context, c *gin.Context, wroute *models.RouteWrapper, method string) (error, error) {
	if !(method == http.MethodPost || method == http.MethodPut) {
		return nil, nil
	}
	var app *models.App
	var err error
	app, err = s.Datastore.GetApp(ctx, wroute.Route.AppName)
	if err != nil && err != models.ErrAppsNotFound {
		return err, models.ErrAppsGet
	} else if app == nil {
		// Create a new application
		newapp := &models.App{Name: wroute.Route.AppName}
		if err = newapp.Validate(); err != nil {
			return err, err
		}

		err = s.FireBeforeAppCreate(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

		_, err = s.Datastore.InsertApp(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

		err = s.FireAfterAppCreate(ctx, newapp)
		if err != nil {
			return err, models.ErrAppsCreate
		}

	}
	return nil, nil
}

/* bindAndValidate binds the RouteWrapper to the json from the request and validates that it is correct.
If it is a put or patch it makes sure that the path in the url matches the provideed one in the body.
Defaults are set and if patch skipZero is true for validating the RouteWrapper
*/
func (s *Server) bindAndValidate(ctx context.Context, c *gin.Context, method string, wroute *models.RouteWrapper) (error, error) {
	err := c.BindJSON(wroute)
	if err != nil {
		return err, models.ErrInvalidJSON
	}

	if wroute.Route == nil {
		return err, models.ErrRoutesMissingNew
	}
	wroute.Route.AppName = c.MustGet(api.AppName).(string)

	if method == http.MethodPut || method == http.MethodPatch {
		p := path.Clean(c.MustGet(api.Path).(string))

		if wroute.Route.Path != "" && wroute.Route.Path != p {
			return models.ErrRoutesPathImmutable, models.ErrRoutesPathImmutable
		}
		wroute.Route.Path = p
	}

	wroute.Route.SetDefaults()

	if err = wroute.Validate(method == http.MethodPatch); err != nil {
		return models.ErrRoutesCreate, err
	}
	return nil, nil
}

// updateOrInsertRoute will either update or insert the route respective the method.
func (s *Server) updateOrInsertRoute(ctx context.Context, method string, wroute models.RouteWrapper) (routeResponse, error) {
	var route *models.Route
	var err error
	resp := routeResponse{"Route successfully created", nil}
	up := routeResponse{"Route successfully updated", nil}

	switch method {
	case http.MethodPost:
		route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
	case http.MethodPut:
		route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
		if err == models.ErrRoutesNotFound {
			// try insert then
			route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
		}
	case http.MethodPatch:
		// When patching if there is an error around the app we will return one and the update fails.
		route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
		resp = up
	}
	resp.Route = route
	return resp, err
}
<|endoftext|>


<commit_before><commit_msg>care windows specific plugins<commit_after><|endoftext|>


<commit_before>package main

import (
  //"fmt"
  "encoding/json"
  "flag"
  "github.com/vole/web"
  osuser "os/user"
  "path"
  "io/ioutil"
  "lib/config"
  "lib/store"
)

var port = flag.String("port", "6789", "Port on which to run the web server.")

var DIR = func() string {
  dir := "."
  user, err := osuser.Current()
  if err == nil {
    dir = user.HomeDir
  }
  return path.Join(dir, "VoleTest")
}()

var userStore = &store.UserStore{
  Path: DIR,
  Version: "v1",
}

func main() {
  flag.Parse()

  config, err := config.Load()
  if err != nil {
    panic(err)
  }

  web.Get("/api/config", func(ctx *web.Context) string {
    ctx.ContentType("json")

    configJson, err := json.Marshal(config)
    if err != nil {
      ctx.Abort(500, "Error marshalling config.")
    }

    return string(configJson)
  })

  web.Get("/api/posts", func(ctx *web.Context) string {
    ctx.ContentType("json")

    allPosts, err := userStore.GetPosts()
    if err != nil {
      ctx.Abort(500, "Error loading posts.")
    }

    postsJson, err := allPosts.Json()
    if err != nil {
      ctx.Abort(500, "Error getting posts as json.")
    }

    return postsJson
  })

  web.Get("/api/users", func(ctx *web.Context) string {
    ctx.ContentType("json")

    _, isMyUserFilter := ctx.Params["is_my_user"]

    var users *store.UserCollection

    if isMyUserFilter {
      myUser, _ := userStore.GetMyUser()
      if myUser != nil {
        users = myUser.Collection()
      } else {
        users = store.GetEmptyUserCollection()
      }
    } else {
      users, err = userStore.GetUsers()
      if err != nil {
        ctx.Abort(500, "Error loading all users.")
      }
    }

    usersJson, err := users.Json()
    if err != nil {
      ctx.Abort(500, "Error getting users as json.")
    }

    return usersJson
  })

  web.Post("/api/users", func(ctx *web.Context) string {
    body, err := ioutil.ReadAll(ctx.Request.Body)
    if err != nil {
      ctx.Abort(500, "Error reading request body.")
    }
    user, err := userStore.NewUserFromContainerJson(body)
    if err != nil {
      ctx.Abort(500, "Invalid JSON")
    }
    if err := user.Save(); err != nil {
      ctx.Abort(500, "Error saving user")
    }
    if err := userStore.SetMyUser(user); err != nil {
      ctx.Abort(500, "Error setting my user")
    }

    container := user.Container()
    userJson, err := container.Json()
    if err != nil {
      ctx.Abort(500, "Could not create container")
    }
    return userJson
  })

  web.Post("/api/posts", func(ctx *web.Context) string {
    body, err := ioutil.ReadAll(ctx.Request.Body)
    if err != nil {
      ctx.Abort(500, "Error reading request body.")
    }

    user, err := userStore.GetMyUser()
    if err != nil {
      ctx.Abort(500, "Error reading my user when posting.")
    }
    post, err := user.NewPostFromContainerJson(body)
    if err != nil {
      ctx.Abort(500, "Invalid JSON")
    }
    if err := post.Save(); err != nil {
      ctx.Abort(500, "Error saving post")
    }
    container := post.Container()
    postJson, err := container.Json()
    if err != nil {
      ctx.Abort(500, "Could not create container")
    }
    return postJson
  })

  web.Run("0.0.0.0:" + *port)
}
<commit_msg>Back to main Vole dir<commit_after>package main

import (
  //"fmt"
  "encoding/json"
  "flag"
  "github.com/vole/web"
  osuser "os/user"
  "path"
  "io/ioutil"
  "lib/config"
  "lib/store"
)

var port = flag.String("port", "6789", "Port on which to run the web server.")

var DIR = func() string {
  dir := "."
  user, err := osuser.Current()
  if err == nil {
    dir = user.HomeDir
  }
  return path.Join(dir, "Vole")
}()

var userStore = &store.UserStore{
  Path: DIR,
  Version: "v1",
}

func main() {
  flag.Parse()

  config, err := config.Load()
  if err != nil {
    panic(err)
  }

  web.Get("/api/config", func(ctx *web.Context) string {
    ctx.ContentType("json")

    configJson, err := json.Marshal(config)
    if err != nil {
      ctx.Abort(500, "Error marshalling config.")
    }

    return string(configJson)
  })

  web.Get("/api/posts", func(ctx *web.Context) string {
    ctx.ContentType("json")

    allPosts, err := userStore.GetPosts()
    if err != nil {
      ctx.Abort(500, "Error loading posts.")
    }

    postsJson, err := allPosts.Json()
    if err != nil {
      ctx.Abort(500, "Error getting posts as json.")
    }

    return postsJson
  })

  web.Get("/api/users", func(ctx *web.Context) string {
    ctx.ContentType("json")

    _, isMyUserFilter := ctx.Params["is_my_user"]

    var users *store.UserCollection

    if isMyUserFilter {
      myUser, _ := userStore.GetMyUser()
      if myUser != nil {
        users = myUser.Collection()
      } else {
        users = store.GetEmptyUserCollection()
      }
    } else {
      users, err = userStore.GetUsers()
      if err != nil {
        ctx.Abort(500, "Error loading all users.")
      }
    }

    usersJson, err := users.Json()
    if err != nil {
      ctx.Abort(500, "Error getting users as json.")
    }

    return usersJson
  })

  web.Post("/api/users", func(ctx *web.Context) string {
    body, err := ioutil.ReadAll(ctx.Request.Body)
    if err != nil {
      ctx.Abort(500, "Error reading request body.")
    }
    user, err := userStore.NewUserFromContainerJson(body)
    if err != nil {
      ctx.Abort(500, "Invalid JSON")
    }
    if err := user.Save(); err != nil {
      ctx.Abort(500, "Error saving user")
    }
    if err := userStore.SetMyUser(user); err != nil {
      ctx.Abort(500, "Error setting my user")
    }

    container := user.Container()
    userJson, err := container.Json()
    if err != nil {
      ctx.Abort(500, "Could not create container")
    }
    return userJson
  })

  web.Post("/api/posts", func(ctx *web.Context) string {
    body, err := ioutil.ReadAll(ctx.Request.Body)
    if err != nil {
      ctx.Abort(500, "Error reading request body.")
    }

    user, err := userStore.GetMyUser()
    if err != nil {
      ctx.Abort(500, "Error reading my user when posting.")
    }
    post, err := user.NewPostFromContainerJson(body)
    if err != nil {
      ctx.Abort(500, "Invalid JSON")
    }
    if err := post.Save(); err != nil {
      ctx.Abort(500, "Error saving post")
    }
    container := post.Container()
    postJson, err := container.Json()
    if err != nil {
      ctx.Abort(500, "Could not create container")
    }
    return postJson
  })

  web.Run("0.0.0.0:" + *port)
}
<|endoftext|>


<commit_before>package commands

import (
	"os"
	"os/exec"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/ailispaw/talk2docker/client"
)

var cmdDocker = &cobra.Command{
	Use:     "docker [OPTIONS] COMMAND [arg...]",
	Aliases: []string{"dock"},
	Short:   "Execute the original docker cli",
	Long:    APP_NAME + " docker - Execute the original docker cli",
	Run:     docker,
}

func init() {
	cmdDocker.SetUsageFunc(func(ctx *cobra.Command) error {
		docker(ctx, []string{"--help"})
		return nil
	})
}

func docker(ctx *cobra.Command, args []string) {
	if _, err := exec.LookPath("docker"); err != nil {
		log.Fatal(err)
	}

	config, err := client.LoadConfig(configPath)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	var env []string
	for _, value := range os.Environ() {
		switch {
		case strings.HasPrefix(value, "DOCKER_HOST="):
		case strings.HasPrefix(value, "DOCKER_CERT_PATH="):
		case strings.HasPrefix(value, "DOCKER_TLS_VERIFY="):
		default:
			env = append(env, value)
		}
	}

	args = append([]string{"--host", host.URL}, args...)
	if host.TLS {
		args = append([]string{"--tls", "true"}, args...)
		args = append([]string{"--tlscacert", host.TLSCaCert}, args...)
		args = append([]string{"--tlscert", host.TLSCert}, args...)
		args = append([]string{"--tlskey", host.TLSKey}, args...)
		args = append([]string{"--tlsverify", FormatBool(host.TLSVerify, "true", "false")}, args...)
	}

	cmd := exec.Command("docker", args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fix a bug of docker command with TLS<commit_after>package commands

import (
	"os"
	"os/exec"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/ailispaw/talk2docker/client"
)

var cmdDocker = &cobra.Command{
	Use:     "docker [OPTIONS] COMMAND [arg...]",
	Aliases: []string{"dock"},
	Short:   "Execute the original docker cli",
	Long:    APP_NAME + " docker - Execute the original docker cli",
	Run:     docker,
}

func init() {
	cmdDocker.SetUsageFunc(func(ctx *cobra.Command) error {
		docker(ctx, []string{"--help"})
		return nil
	})
}

func docker(ctx *cobra.Command, args []string) {
	if _, err := exec.LookPath("docker"); err != nil {
		log.Fatal(err)
	}

	config, err := client.LoadConfig(configPath)
	if err != nil {
		log.Fatal(err)
	}

	host, err := config.GetHost(hostName)
	if err != nil {
		log.Fatal(err)
	}

	var env []string
	for _, value := range os.Environ() {
		switch {
		case strings.HasPrefix(value, "DOCKER_HOST="):
		case strings.HasPrefix(value, "DOCKER_CERT_PATH="):
		case strings.HasPrefix(value, "DOCKER_TLS_VERIFY="):
		default:
			env = append(env, value)
		}
	}

	args = append([]string{"--host", host.URL}, args...)
	if host.TLS {
		args = append([]string{"--tls"}, args...)
		args = append([]string{"--tlscacert", host.TLSCaCert}, args...)
		args = append([]string{"--tlscert", host.TLSCert}, args...)
		args = append([]string{"--tlskey", host.TLSKey}, args...)
		if host.TLSVerify {
			args = append([]string{"--tlsverify"}, args...)
		}
	}

	cmd := exec.Command("docker", args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>