<commit_before>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package digitalocean

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/iaas"
	"gopkg.in/check.v1"
)

func Test(t *testing.T) { check.TestingT(t) }

func (s *digitaloceanSuite) SetUpSuite(c *check.C) {
	config.Set("iaas:digitalocean:token", "test")
}

type digitaloceanSuite struct{}

var _ = check.Suite(&digitaloceanSuite{})

func (s *digitaloceanSuite) TestCreateMachine(c *check.C) {
	var createRequest map[string]interface{}
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets" {
			err := json.NewDecoder(r.Body).Decode(&createRequest)
			c.Assert(err, check.IsNil)
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "new", "networks": {"v4": [], "v6": []}}}`)
		}
		if r.URL.Path == "/v2/droplets/1" {
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [{"ip_address": "104.131.186.241", "netmask": "255.255.240.0", "gateway": "104.131.176.1", "type": "public"}], "v6": [{"ip_address": "2604:A880:0800:0010:0000:0000:031D:2001", "netmask": 64, "gateway": "2604:A880:0800:0010:0000:0000:0000:0001", "type": "public"}]}}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":     "example.com",
		"region":   "nyc3",
		"size":     "512mb",
		"image":    "ubuntu-14-04-x64",
		"ssh-keys": "5050,2032,07:b9:a1:65:1b,13",
	}
	m, err := do.CreateMachine(params)
	c.Assert(err, check.IsNil)
	c.Assert(m, check.NotNil)
	c.Assert(m.Address, check.Equals, "104.131.186.241")
	c.Assert(m.Id, check.Equals, "1")
	c.Assert(m.Status, check.Equals, "active")
	expectedKeys := []interface{}{float64(5050), float64(2032), "07:b9:a1:65:1b", float64(13)}
	c.Assert(createRequest["ssh_keys"], check.DeepEquals, expectedKeys)
	c.Assert(createRequest["private_networking"], check.Equals, false)
}

func (s *digitaloceanSuite) TestCreateMachinePrivateNetworking(c *check.C) {
	var createRequest map[string]interface{}
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets" {
			err := json.NewDecoder(r.Body).Decode(&createRequest)
			c.Assert(err, check.IsNil)
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "new", "networks": {"v4": [], "v6": []}}}`)
		}
		if r.URL.Path == "/v2/droplets/1" {
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [{"ip_address": "104.131.186.241", "netmask": "255.255.240.0", "gateway": "104.131.176.1", "type": "public"}, {"ip_address": "10.128.19.90", "netmask": "255.255.240.0", "gateway": "10.128.19.1", "type": "private"}], "v6": [{"ip_address": "2604:A880:0800:0010:0000:0000:031D:2001", "netmask": 64, "gateway": "2604:A880:0800:0010:0000:0000:0000:0001", "type": "public"}]}}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":               "example.com",
		"region":             "nyc3",
		"size":               "512mb",
		"image":              "ubuntu-14-04-x64",
		"ssh-keys":           "5050,2032,07:b9:a1:65:1b,13",
		"private-networking": "1",
	}
	m, err := do.CreateMachine(params)
	c.Assert(err, check.IsNil)
	c.Assert(m, check.NotNil)
	c.Assert(m.Address, check.Equals, "10.128.19.90")
	c.Assert(m.Id, check.Equals, "1")
	c.Assert(m.Status, check.Equals, "active")
	expectedKeys := []interface{}{float64(5050), float64(2032), "07:b9:a1:65:1b", float64(13)}
	c.Assert(createRequest["ssh_keys"], check.DeepEquals, expectedKeys)
	c.Assert(createRequest["private_networking"], check.Equals, true)
}

func (s *digitaloceanSuite) TestCreateMachineFailure(c *check.C) {
	config.Set("iaas:digitalocean:wait-timeout", 1)
	defer config.Unset("iaas:digitalocean:wait-timeout")
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [], "v6": []}}}`)
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":   "example.com",
		"region": "nyc3",
		"size":   "512mb",
		"image":  "ubuntu-14-04-x64",
	}
	_, err := do.CreateMachine(params)
	c.Assert(err, check.NotNil)
	c.Assert(err.Error(), check.Equals, "timed out waiting for machine network")
}

func (s *digitaloceanSuite) TestDeleteMachine(c *check.C) {
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(204)
		w.Header().Set("Content-type", "application/json")
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	machine := iaas.Machine{Id: "503", CreationParams: map[string]string{"projectid": "projid"}}
	err := do.DeleteMachine(&machine)
	c.Assert(err, check.IsNil)
}

func (s *digitaloceanSuite) TestDeleteMachineFailure(c *check.C) {
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Header().Set("Content-type", "application/json")
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	machine := iaas.Machine{Id: "13", CreationParams: map[string]string{"projectid": "projid"}}
	err := do.DeleteMachine(&machine)
	c.Assert(err, check.NotNil)
	c.Assert(err.Error(), check.Equals, "failed to delete machine")
}
<commit_msg>trying to fix test<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package digitalocean

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/iaas"
	"gopkg.in/check.v1"
)

func Test(t *testing.T) { check.TestingT(t) }

func (s *digitaloceanSuite) SetUpSuite(c *check.C) {
	config.Set("iaas:digitalocean:token", "test")
}

type digitaloceanSuite struct{}

var _ = check.Suite(&digitaloceanSuite{})

func (s *digitaloceanSuite) TestCreateMachine(c *check.C) {
	var createRequest map[string]interface{}
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets" {
			err := json.NewDecoder(r.Body).Decode(&createRequest)
			c.Assert(err, check.IsNil)
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "new", "networks": {"v4": [], "v6": []}}}`)
		}
		if r.URL.Path == "/v2/droplets/1" {
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [{"ip_address": "104.131.186.241", "netmask": "255.255.240.0", "gateway": "104.131.176.1", "type": "public"}], "v6": [{"ip_address": "2604:A880:0800:0010:0000:0000:031D:2001", "netmask": 64, "gateway": "2604:A880:0800:0010:0000:0000:0000:0001", "type": "public"}]}}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":     "example.com",
		"region":   "nyc3",
		"size":     "512mb",
		"image":    "ubuntu-14-04-x64",
		"ssh-keys": "5050,2032,07:b9:a1:65:1b,13",
	}
	m, err := do.CreateMachine(params)
	c.Assert(err, check.IsNil)
	c.Assert(m, check.NotNil)
	c.Assert(m.Address, check.Equals, "104.131.186.241")
	c.Assert(m.Id, check.Equals, "1")
	c.Assert(m.Status, check.Equals, "active")
	expectedKeys := []interface{}{float64(5050), float64(2032), "07:b9:a1:65:1b", float64(13)}
	c.Assert(createRequest["ssh_keys"], check.DeepEquals, expectedKeys)
	c.Assert(createRequest["private_networking"], check.Equals, false)
}

func (s *digitaloceanSuite) TestCreateMachinePrivateNetworking(c *check.C) {
	var createRequest map[string]interface{}
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets" {
			err := json.NewDecoder(r.Body).Decode(&createRequest)
			c.Assert(err, check.IsNil)
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "new", "networks": {"v4": [], "v6": []}}}`)
		}
		if r.URL.Path == "/v2/droplets/1" {
			fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [{"ip_address": "104.131.186.241", "netmask": "255.255.240.0", "gateway": "104.131.176.1", "type": "public"}, {"ip_address": "10.128.19.90", "netmask": "255.255.240.0", "gateway": "10.128.19.1", "type": "private"}], "v6": [{"ip_address": "2604:A880:0800:0010:0000:0000:031D:2001", "netmask": 64, "gateway": "2604:A880:0800:0010:0000:0000:0000:0001", "type": "public"}]}}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":               "example.com",
		"region":             "nyc3",
		"size":               "512mb",
		"image":              "ubuntu-14-04-x64",
		"ssh-keys":           "5050,2032,07:b9:a1:65:1b,13",
		"private-networking": "1",
	}
	m, err := do.CreateMachine(params)
	c.Assert(err, check.IsNil)
	c.Assert(m, check.NotNil)
	c.Assert(m.Address, check.Equals, "10.128.19.90")
	c.Assert(m.Id, check.Equals, "1")
	c.Assert(m.Status, check.Equals, "active")
	expectedKeys := []interface{}{float64(5050), float64(2032), "07:b9:a1:65:1b", float64(13)}
	c.Assert(createRequest["ssh_keys"], check.DeepEquals, expectedKeys)
	c.Assert(createRequest["private_networking"], check.Equals, true)
}

func (s *digitaloceanSuite) TestCreateMachineFailure(c *check.C) {
	config.Set("iaas:digitalocean:wait-timeout", 1)
	defer config.Unset("iaas:digitalocean:wait-timeout")
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"droplet": {"id": 1, "status": "active", "networks": {"v4": [], "v6": []}}}`)
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	params := map[string]string{
		"name":   "example.com",
		"region": "nyc3",
		"size":   "512mb",
		"image":  "ubuntu-14-04-x64",
	}
	_, err := do.CreateMachine(params)
	c.Assert(err, check.NotNil)
	c.Assert(err.Error(), check.Equals, "timed out waiting for machine network")
}

func (s *digitaloceanSuite) TestDeleteMachine(c *check.C) {
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets/503" {
			w.WriteHeader(204)
			w.Header().Set("Content-type", "application/json")
		}
		if r.URL.Path == "/v2/droplets/503/actions" {
			fmt.Fprintln(w, `{"action":{"id": 123456, "status": "in-progress", "started_at": "2014-11-04T17:08:03Z", "resource_id": 503, "resource_type": "droplet"}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	machine := iaas.Machine{Id: "503", CreationParams: map[string]string{"projectid": "projid"}}
	err := do.DeleteMachine(&machine)
	c.Assert(err, check.IsNil)
}

func (s *digitaloceanSuite) TestDeleteMachineFailure(c *check.C) {
	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v2/droplets/13" {
			w.WriteHeader(200)
			w.Header().Set("Content-type", "application/json")
		}
		if r.URL.Path == "/v2/droplets/13/actions" {
			fmt.Fprintln(w, `{"action":{"id": 123456, "status": "in-progress", "started_at": "2014-11-04T17:08:03Z", "resource_id": 13, "resource_type": "droplet"}}`)
		}
	}))
	defer fakeServer.Close()
	config.Set("iaas:digitalocean:url", fakeServer.URL)
	do := newDigitalOceanIaas("digitalocean")
	machine := iaas.Machine{Id: "13", CreationParams: map[string]string{"projectid": "projid"}}
	err := do.DeleteMachine(&machine)
	c.Assert(err, check.NotNil)
	c.Assert(err.Error(), check.Equals, "failed to delete machine")
}
<|endoftext|>


<commit_before>package bbox

import (
	"github.com/nsf/termbox-go"
)

type Coord [2]int

type Key struct {
	Ch  rune        // a unicode character
	Key termbox.Key // one of Key* constants, invalid if 'Ch' is not 0
}

var keymaps = map[Key]*Coord{
	{'1', 0}: &Coord{0, 0},
	{'2', 0}: &Coord{0, 1},
	{'3', 0}: &Coord{0, 2},
	{'4', 0}: &Coord{0, 3},
	{'5', 0}: &Coord{0, 4},
	{'6', 0}: &Coord{0, 5},
	{'7', 0}: &Coord{0, 6},
	{'8', 0}: &Coord{0, 7},
	{'!', 0}: &Coord{0, 8},
	{'@', 0}: &Coord{0, 9},
	{'#', 0}: &Coord{0, 10},
	{'$', 0}: &Coord{0, 11},
	{'%', 0}: &Coord{0, 12},
	{'^', 0}: &Coord{0, 13},
	{'&', 0}: &Coord{0, 14},
	{'*', 0}: &Coord{0, 15},

	{'q', 0}: &Coord{1, 0},
	{'w', 0}: &Coord{1, 1},
	{'e', 0}: &Coord{1, 2},
	{'r', 0}: &Coord{1, 3},
	{'t', 0}: &Coord{1, 4},
	{'y', 0}: &Coord{1, 5},
	{'u', 0}: &Coord{1, 6},
	{'i', 0}: &Coord{1, 7},
	{'Q', 0}: &Coord{1, 8},
	{'W', 0}: &Coord{1, 9},
	{'E', 0}: &Coord{1, 10},
	{'R', 0}: &Coord{1, 11},
	{'T', 0}: &Coord{1, 12},
	{'Y', 0}: &Coord{1, 13},
	{'U', 0}: &Coord{1, 14},
	{'I', 0}: &Coord{1, 15},

	{'a', 0}: &Coord{2, 0},
	{'s', 0}: &Coord{2, 1},
	{'d', 0}: &Coord{2, 2},
	{'f', 0}: &Coord{2, 3},
	{'g', 0}: &Coord{2, 4},
	{'h', 0}: &Coord{2, 5},
	{'j', 0}: &Coord{2, 6},
	{'k', 0}: &Coord{2, 7},
	{'A', 0}: &Coord{2, 8},
	{'S', 0}: &Coord{2, 9},
	{'D', 0}: &Coord{2, 10},
	{'F', 0}: &Coord{2, 11},
	{'G', 0}: &Coord{2, 12},
	{'H', 0}: &Coord{2, 13},
	{'J', 0}: &Coord{2, 14},
	{'K', 0}: &Coord{2, 15},

	{'z', 0}: &Coord{3, 0},
	{'x', 0}: &Coord{3, 1},
	{'c', 0}: &Coord{3, 2},
	{'v', 0}: &Coord{3, 3},
	{'b', 0}: &Coord{3, 4},
	{'n', 0}: &Coord{3, 5},
	{'m', 0}: &Coord{3, 6},
	{',', 0}: &Coord{3, 7},
	{'Z', 0}: &Coord{3, 8},
	{'X', 0}: &Coord{3, 9},
	{'C', 0}: &Coord{3, 10},
	{'V', 0}: &Coord{3, 11},
	{'B', 0}: &Coord{3, 12},
	{'N', 0}: &Coord{3, 13},
	{'M', 0}: &Coord{3, 14},
	{'<', 0}: &Coord{3, 15},
}

// mapping from keyboard box
var keymaps_rpi = map[Key]*Coord{
	// 2 x 21 = [volume down]
	// 2 x 24 = [mute]
	// 3 x 19 = ` (quit)

	{'1', 0}:            &Coord{0, 0}, // 3 x 20
	{'q', 0}:            &Coord{0, 1}, // 3 x 21
	{0, termbox.KeyTab}: &Coord{0, 2}, // 3 x 22
	{'a', 0}:            &Coord{0, 3}, // 3 x 23
	{'z', 0}:            &Coord{0, 4}, // 3 x 24
	{0, termbox.KeyF1}:  &Coord{0, 5}, // 4 x 19
	{'2', 0}:            &Coord{0, 6}, // 4 x 20
	{'w', 0}:            &Coord{0, 7}, // 4 x 21
	{'S', 0}:            &Coord{0, 8}, // 4 x 23
	// 4 x 24 = §
	{'x', 0}:           &Coord{0, 9},  // 4 x 25
	{0, termbox.KeyF2}: &Coord{0, 10}, // 5 x 19
	{'3', 0}:           &Coord{0, 11}, // 5 x 20
	{'e', 0}:           &Coord{0, 12}, // 5 x 21
	{'d', 0}:           &Coord{0, 13}, // 5 x 22
	{'c', 0}:           &Coord{0, 14}, // 5 x 23
	{0, termbox.KeyF4}: &Coord{0, 15}, // 5 x 24

	{'5', 0}: &Coord{1, 0},  // 6 x 19
	{'4', 0}: &Coord{1, 1},  // 6 x 20
	{'r', 0}: &Coord{1, 2},  // 6 x 21
	{'t', 0}: &Coord{1, 3},  // 6 x 22
	{'f', 0}: &Coord{1, 4},  // 6 x 23
	{'g', 0}: &Coord{1, 5},  // 6 x 24
	{'v', 0}: &Coord{1, 6},  // 6 x 25
	{'b', 0}: &Coord{1, 7},  // 6 x 26
	{'6', 0}: &Coord{1, 8},  // 7 x 19
	{'7', 0}: &Coord{1, 9},  // 7 x 20
	{'u', 0}: &Coord{1, 10}, // 7 x 21
	{'y', 0}: &Coord{1, 11}, // 7 x 22
	{'j', 0}: &Coord{1, 12}, // 7 x 23
	{'h', 0}: &Coord{1, 13}, // 7 x 24
	{'m', 0}: &Coord{1, 14}, // 7 x 25
	{'n', 0}: &Coord{1, 15}, // 7 x 26

	{'=', 0}:           &Coord{2, 0},  // 8 x 19
	{'8', 0}:           &Coord{2, 1},  // 8 x 20
	{'i', 0}:           &Coord{2, 2},  // 8 x 21
	{']', 0}:           &Coord{2, 3},  // 8 x 22
	{'K', 0}:           &Coord{2, 4},  // 8 x 23
	{0, termbox.KeyF6}: &Coord{2, 5},  // 8 x 24
	{',', 0}:           &Coord{2, 6},  // 8 x 25
	{0, termbox.KeyF8}: &Coord{2, 7},  // 9 x 19
	{'9', 0}:           &Coord{2, 8},  // 9 x 20
	{'o', 0}:           &Coord{2, 9},  // 9 x 21
	{'l', 0}:           &Coord{2, 10}, // 9 x 23
	{'.', 0}:           &Coord{2, 11}, // 9 x 25
	{'-', 0}:           &Coord{2, 12}, // 10 x 19
	{'0', 0}:           &Coord{2, 13}, // 10 x 20
	{'p', 0}:           &Coord{2, 14}, // 10 x 21
	{'[', 0}:           &Coord{2, 15}, // 10 x 22

	{';', 0}:                   &Coord{3, 0}, // 10 x 23
	{'\'', 0}:                  &Coord{3, 1}, // 10 x 24
	{'\\', 0}:                  &Coord{3, 2}, // 10 x 25
	{'/', 0}:                   &Coord{3, 3}, // 10 x 26
	{0, termbox.KeyF9}:         &Coord{3, 4}, // 11 x 19
	{0, termbox.KeyF10}:        &Coord{3, 5}, // 11 x 20
	{0, termbox.KeyBackspace2}: &Coord{3, 6}, // 11 x 22 // 'delete' on mac keyboard
	// 11 x 23 = \ ***
	{0, termbox.KeyF5}:    &Coord{3, 7},  // 11 x 24
	{0, termbox.KeyEnter}: &Coord{3, 8},  // 11 x 25
	{0, termbox.KeySpace}: &Coord{3, 9},  // 11 x 26
	{0, termbox.KeyF12}:   &Coord{3, 10}, // 12 x 20
	// 12 x 21 = 8 ***
	// 12 x 22 = 5 ***
	// 12 x 23 = 2 ***
	// 12 x 24 = 0 ***
	// 12 x 25 = / ***
	{0, termbox.KeyArrowRight}: &Coord{3, 11}, // 12 x 26
	{0, termbox.KeyDelete}:     &Coord{3, 12}, // 13 x 19 // not on mac keyboard
	// 13 x 20 = [fn f11]
	// 13 x 21 = 7 ***
	// 13 x 22 = 4 ***
	// 13 x 23 = 1 ***
	{0, termbox.KeyArrowDown}: &Coord{3, 13}, // 13 x 26
	{0, termbox.KeyPgup}:      &Coord{3, 14}, // 14 x 19 // not on mac keyboard
	{0, termbox.KeyPgdn}:      &Coord{3, 15}, // 14 x 20 // not on mac keyboard
	// 14 x 21 = 9 ***
	// 14 x 22 = 6 ***
	// 14 x 23 = 3 ***
	// 14 x 24 = . ***
	// 14 x 25 = *
	// 14 x 26 = - ***
	// 15 x 19 = KeyHome
	// 15 x 20 = KeyEnd
	// 15 x 21 = +
	// 15 x 23 = KeyEnter ***
	// 15 x 24 = KeyArrowUp
	// 15 x 25 = [brightness up]
	// 15 x 26 = KeyArrowLeft
	// 16 x 21 = [brightness down]
	// 17 x 24 = [launch itunes?]
	// 18 x 22 = [volume up]
}
<commit_msg>keyboard updates for pi<commit_after>package bbox

import (
	"github.com/nsf/termbox-go"
)

type Coord [2]int

type Key struct {
	Ch  rune        // a unicode character
	Key termbox.Key // one of Key* constants, invalid if 'Ch' is not 0
}

var keymaps = map[Key]*Coord{
	{'1', 0}: &Coord{0, 0},
	{'2', 0}: &Coord{0, 1},
	{'3', 0}: &Coord{0, 2},
	{'4', 0}: &Coord{0, 3},
	{'5', 0}: &Coord{0, 4},
	{'6', 0}: &Coord{0, 5},
	{'7', 0}: &Coord{0, 6},
	{'8', 0}: &Coord{0, 7},
	{'!', 0}: &Coord{0, 8},
	{'@', 0}: &Coord{0, 9},
	{'#', 0}: &Coord{0, 10},
	{'$', 0}: &Coord{0, 11},
	{'%', 0}: &Coord{0, 12},
	{'^', 0}: &Coord{0, 13},
	{'&', 0}: &Coord{0, 14},
	{'*', 0}: &Coord{0, 15},

	{'q', 0}: &Coord{1, 0},
	{'w', 0}: &Coord{1, 1},
	{'e', 0}: &Coord{1, 2},
	{'r', 0}: &Coord{1, 3},
	{'t', 0}: &Coord{1, 4},
	{'y', 0}: &Coord{1, 5},
	{'u', 0}: &Coord{1, 6},
	{'i', 0}: &Coord{1, 7},
	{'Q', 0}: &Coord{1, 8},
	{'W', 0}: &Coord{1, 9},
	{'E', 0}: &Coord{1, 10},
	{'R', 0}: &Coord{1, 11},
	{'T', 0}: &Coord{1, 12},
	{'Y', 0}: &Coord{1, 13},
	{'U', 0}: &Coord{1, 14},
	{'I', 0}: &Coord{1, 15},

	{'a', 0}: &Coord{2, 0},
	{'s', 0}: &Coord{2, 1},
	{'d', 0}: &Coord{2, 2},
	{'f', 0}: &Coord{2, 3},
	{'g', 0}: &Coord{2, 4},
	{'h', 0}: &Coord{2, 5},
	{'j', 0}: &Coord{2, 6},
	{'k', 0}: &Coord{2, 7},
	{'A', 0}: &Coord{2, 8},
	{'S', 0}: &Coord{2, 9},
	{'D', 0}: &Coord{2, 10},
	{'F', 0}: &Coord{2, 11},
	{'G', 0}: &Coord{2, 12},
	{'H', 0}: &Coord{2, 13},
	{'J', 0}: &Coord{2, 14},
	{'K', 0}: &Coord{2, 15},

	{'z', 0}: &Coord{3, 0},
	{'x', 0}: &Coord{3, 1},
	{'c', 0}: &Coord{3, 2},
	{'v', 0}: &Coord{3, 3},
	{'b', 0}: &Coord{3, 4},
	{'n', 0}: &Coord{3, 5},
	{'m', 0}: &Coord{3, 6},
	{',', 0}: &Coord{3, 7},
	{'Z', 0}: &Coord{3, 8},
	{'X', 0}: &Coord{3, 9},
	{'C', 0}: &Coord{3, 10},
	{'V', 0}: &Coord{3, 11},
	{'B', 0}: &Coord{3, 12},
	{'N', 0}: &Coord{3, 13},
	{'M', 0}: &Coord{3, 14},
	{'<', 0}: &Coord{3, 15},
}

// mapping from keyboard box
var keymaps_rpi = map[Key]*Coord{
	// 2 x 21 = [volume down]
	// 2 x 24 = [mute]
	// 3 x 19 = ` (quit)

	{'1', 0}:            &Coord{0, 0}, // 3 x 20
	{'q', 0}:            &Coord{0, 1}, // 3 x 21
	{0, termbox.KeyTab}: &Coord{0, 2}, // 3 x 22
	{'a', 0}:            &Coord{0, 3}, // 3 x 23
	{'z', 0}:            &Coord{0, 4}, // 3 x 25
	{0, termbox.KeyF1}:  &Coord{0, 5}, // 4 x 19
	{'2', 0}:            &Coord{0, 6}, // 4 x 20
	{'w', 0}:            &Coord{0, 7}, // 4 x 21
	{'s', 0}:            &Coord{0, 8}, // 4 x 23
	// 4 x 24 = §
	{'x', 0}:           &Coord{0, 9},  // 4 x 25
	{0, termbox.KeyF2}: &Coord{0, 10}, // 5 x 19
	{'3', 0}:           &Coord{0, 11}, // 5 x 20
	{'e', 0}:           &Coord{0, 12}, // 5 x 21
	{0, termbox.KeyF3}: &Coord{0, 13}, // 5 x 22
	{'d', 0}:           &Coord{0, 14}, // 5 x 23
	{0, termbox.KeyF4}: &Coord{0, 15}, // 5 x 24

	{'5', 0}: &Coord{1, 0},  // 6 x 19
	{'4', 0}: &Coord{1, 1},  // 6 x 20
	{'r', 0}: &Coord{1, 2},  // 6 x 21
	{'t', 0}: &Coord{1, 3},  // 6 x 22
	{'f', 0}: &Coord{1, 4},  // 6 x 23
	{'g', 0}: &Coord{1, 5},  // 6 x 24
	{'v', 0}: &Coord{1, 6},  // 6 x 25
	{'b', 0}: &Coord{1, 7},  // 6 x 26
	{'6', 0}: &Coord{1, 8},  // 7 x 19
	{'7', 0}: &Coord{1, 9},  // 7 x 20
	{'u', 0}: &Coord{1, 10}, // 7 x 21
	{'y', 0}: &Coord{1, 11}, // 7 x 22
	{'j', 0}: &Coord{1, 12}, // 7 x 23
	{'h', 0}: &Coord{1, 13}, // 7 x 24
	{'m', 0}: &Coord{1, 14}, // 7 x 25
	{'n', 0}: &Coord{1, 15}, // 7 x 26

	{'=', 0}:           &Coord{2, 0},  // 8 x 19
	{'8', 0}:           &Coord{2, 1},  // 8 x 20
	{'i', 0}:           &Coord{2, 2},  // 8 x 21
	{']', 0}:           &Coord{2, 3},  // 8 x 22
	{'k', 0}:           &Coord{2, 4},  // 8 x 23
	{0, termbox.KeyF6}: &Coord{2, 5},  // 8 x 24
	{',', 0}:           &Coord{2, 6},  // 8 x 25
	{0, termbox.KeyF8}: &Coord{2, 7},  // 9 x 19
	{'9', 0}:           &Coord{2, 8},  // 9 x 20
	{'o', 0}:           &Coord{2, 9},  // 9 x 21
	{0, termbox.KeyF7}: &Coord{2, 10}, // 9 x 22
	{'l', 0}:           &Coord{2, 11}, // 9 x 23
	{'.', 0}:           &Coord{2, 12}, // 9 x 25
	{'-', 0}:           &Coord{2, 13}, // 10 x 19
	{'0', 0}:           &Coord{2, 14}, // 10 x 20
	{'p', 0}:           &Coord{2, 15}, // 10 x 21

	{'[', 0}:                   &Coord{3, 0}, // 10 x 22
	{';', 0}:                   &Coord{3, 1}, // 10 x 23
	{'\'', 0}:                  &Coord{3, 2}, // 10 x 24
	{'\\', 0}:                  &Coord{3, 3}, // 10 x 25
	{'/', 0}:                   &Coord{3, 4}, // 10 x 26
	{0, termbox.KeyF9}:         &Coord{3, 5}, // 11 x 19
	{0, termbox.KeyF10}:        &Coord{3, 6}, // 11 x 20
	{0, termbox.KeyBackspace2}: &Coord{3, 7}, // 11 x 22 // 'delete' on mac keyboard
	// 11 x 23 = \ ***
	{0, termbox.KeyF5}:    &Coord{3, 8},  // 11 x 24
	{0, termbox.KeyEnter}: &Coord{3, 9},  // 11 x 25
	{0, termbox.KeySpace}: &Coord{3, 10}, // 11 x 26
	{0, termbox.KeyF12}:   &Coord{3, 11}, // 12 x 20
	// 12 x 21 = 8 ***
	// 12 x 22 = 5 ***
	// 12 x 23 = 2 ***
	// 12 x 24 = 0 ***
	// 12 x 25 = / ***
	{0, termbox.KeyArrowRight}: &Coord{3, 12}, // 12 x 26
	{0, termbox.KeyDelete}:     &Coord{3, 13}, // 13 x 19
	// 13 x 20 = [fn f11]
	// 13 x 21 = 7 ***
	// 13 x 22 = 4 ***
	// 13 x 23 = 1 ***
	{0, termbox.KeyArrowDown}: &Coord{3, 14}, // 13 x 26
	{0, termbox.KeyF11}:       &Coord{3, 15}, // 14 x 20
	// 14 x 21 = 9 ***
	// 14 x 22 = 6 ***
	// 14 x 23 = 3 ***
	// 14 x 24 = . ***
	// 14 x 25 = *
	// 14 x 26 = - ***
	// 15 x 19 = KeyHome
	// 15 x 20 = KeyEnd
	// 15 x 21 = +
	// 15 x 23 = KeyEnter ***
	// 15 x 24 = KeyArrowUp
	// 15 x 25 = [brightness up]
	// 15 x 26 = KeyArrowLeft
	// 16 x 21 = [brightness down]
	// 17 x 24 = [launch itunes?]
	// 18 x 22 = [volume up]
}
<|endoftext|>


<commit_before>package rc

import (
	"context"
	"runtime"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/config/obscure"
	"github.com/rclone/rclone/fs/version"
)

func TestInternalNoop(t *testing.T) {
	call := Calls.Get("rc/noop")
	assert.NotNil(t, call)
	in := Params{
		"String": "hello",
		"Int":    42,
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in, out)
}

func TestInternalError(t *testing.T) {
	call := Calls.Get("rc/error")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.Error(t, err)
	require.Nil(t, out)
}

func TestInternalList(t *testing.T) {
	call := Calls.Get("rc/list")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, Params{"commands": Calls.List()}, out)
}

func TestCorePid(t *testing.T) {
	call := Calls.Get("core/pid")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	pid := out["pid"]
	assert.NotEqual(t, nil, pid)
	_, ok := pid.(int)
	assert.Equal(t, true, ok)
}

func TestCoreMemstats(t *testing.T) {
	call := Calls.Get("core/memstats")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	sys := out["Sys"]
	assert.NotEqual(t, nil, sys)
	_, ok := sys.(uint64)
	assert.Equal(t, true, ok)
}

func TestCoreGC(t *testing.T) {
	call := Calls.Get("core/gc")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.Nil(t, out)
	assert.Equal(t, Params(nil), out)
}

func TestCoreVersion(t *testing.T) {
	call := Calls.Get("core/version")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, fs.Version, out["version"])
	assert.Equal(t, runtime.GOOS, out["os"])
	assert.Equal(t, runtime.GOARCH, out["arch"])
	assert.Equal(t, runtime.Version(), out["goVersion"])
	_ = out["isGit"].(bool)
	v := out["decomposed"].(version.Version)
	assert.True(t, len(v) >= 2)
}

func TestCoreObscure(t *testing.T) {
	call := Calls.Get("core/obscure")
	assert.NotNil(t, call)
	in := Params{
		"clear": "potato",
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in["clear"], obscure.MustReveal(out["obscured"].(string)))
}

func TestCoreQuit(t *testing.T) {
	//The call should return an error if param exitCode is not parsed to int
	call := Calls.Get("core/quit")
	assert.NotNil(t, call)
	in := Params{
		"exitCode": "potato",
	}
	_, err := call.Fn(context.Background(), in)
	require.Error(t, err)
}

// core/command: Runs a raw rclone command
func TestCoreCommand(t *testing.T) {
	call := Calls.Get("core/command")

	in := Params{
		"command": "version",
		"opt":     map[string]string{},
		"arg":     []string{},
	}
	got, err := call.Fn(context.Background(), in)
	require.NoError(t, err)

	errorBool, err := got.GetBool("error")
	require.NoError(t, err)

	require.Equal(t, errorBool, false)
}
<commit_msg>core/command: pretend to be "rclone version" to make tests pass<commit_after>package rc

import (
	"context"
	"fmt"
	"os"
	"runtime"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/config/obscure"
	"github.com/rclone/rclone/fs/version"
)

func TestMain(m *testing.M) {
	// Pretend to be rclone version if we have a version string parameter
	if os.Args[len(os.Args)-1] == "version" {
		fmt.Printf("rclone %s\n", fs.Version)
		os.Exit(0)
	}
	os.Exit(m.Run())
}

func TestInternalNoop(t *testing.T) {
	call := Calls.Get("rc/noop")
	assert.NotNil(t, call)
	in := Params{
		"String": "hello",
		"Int":    42,
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in, out)
}

func TestInternalError(t *testing.T) {
	call := Calls.Get("rc/error")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.Error(t, err)
	require.Nil(t, out)
}

func TestInternalList(t *testing.T) {
	call := Calls.Get("rc/list")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, Params{"commands": Calls.List()}, out)
}

func TestCorePid(t *testing.T) {
	call := Calls.Get("core/pid")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	pid := out["pid"]
	assert.NotEqual(t, nil, pid)
	_, ok := pid.(int)
	assert.Equal(t, true, ok)
}

func TestCoreMemstats(t *testing.T) {
	call := Calls.Get("core/memstats")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	sys := out["Sys"]
	assert.NotEqual(t, nil, sys)
	_, ok := sys.(uint64)
	assert.Equal(t, true, ok)
}

func TestCoreGC(t *testing.T) {
	call := Calls.Get("core/gc")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.Nil(t, out)
	assert.Equal(t, Params(nil), out)
}

func TestCoreVersion(t *testing.T) {
	call := Calls.Get("core/version")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, fs.Version, out["version"])
	assert.Equal(t, runtime.GOOS, out["os"])
	assert.Equal(t, runtime.GOARCH, out["arch"])
	assert.Equal(t, runtime.Version(), out["goVersion"])
	_ = out["isGit"].(bool)
	v := out["decomposed"].(version.Version)
	assert.True(t, len(v) >= 2)
}

func TestCoreObscure(t *testing.T) {
	call := Calls.Get("core/obscure")
	assert.NotNil(t, call)
	in := Params{
		"clear": "potato",
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in["clear"], obscure.MustReveal(out["obscured"].(string)))
}

func TestCoreQuit(t *testing.T) {
	//The call should return an error if param exitCode is not parsed to int
	call := Calls.Get("core/quit")
	assert.NotNil(t, call)
	in := Params{
		"exitCode": "potato",
	}
	_, err := call.Fn(context.Background(), in)
	require.Error(t, err)
}

// core/command: Runs a raw rclone command
func TestCoreCommand(t *testing.T) {
	call := Calls.Get("core/command")

	in := Params{
		"command": "version",
		"opt":     map[string]string{},
		"arg":     []string{},
	}
	got, err := call.Fn(context.Background(), in)
	require.NoError(t, err)

	assert.Equal(t, fmt.Sprintf("rclone %s\n", fs.Version), got["result"])
	assert.Equal(t, false, got["error"])
}
<|endoftext|>


<commit_before>package rc

import (
	"context"
	"runtime"
	"testing"

	"github.com/rclone/rclone/fstest"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/config/obscure"
	"github.com/rclone/rclone/fs/version"
)

func TestInternalNoop(t *testing.T) {
	call := Calls.Get("rc/noop")
	assert.NotNil(t, call)
	in := Params{
		"String": "hello",
		"Int":    42,
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in, out)
}

func TestInternalError(t *testing.T) {
	call := Calls.Get("rc/error")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.Error(t, err)
	require.Nil(t, out)
}

func TestInternalList(t *testing.T) {
	call := Calls.Get("rc/list")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, Params{"commands": Calls.List()}, out)
}

func TestCorePid(t *testing.T) {
	call := Calls.Get("core/pid")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	pid := out["pid"]
	assert.NotEqual(t, nil, pid)
	_, ok := pid.(int)
	assert.Equal(t, true, ok)
}

func TestCoreMemstats(t *testing.T) {
	call := Calls.Get("core/memstats")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	sys := out["Sys"]
	assert.NotEqual(t, nil, sys)
	_, ok := sys.(uint64)
	assert.Equal(t, true, ok)
}

func TestCoreGC(t *testing.T) {
	call := Calls.Get("core/gc")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.Nil(t, out)
	assert.Equal(t, Params(nil), out)
}

func TestCoreVersion(t *testing.T) {
	call := Calls.Get("core/version")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, fs.Version, out["version"])
	assert.Equal(t, runtime.GOOS, out["os"])
	assert.Equal(t, runtime.GOARCH, out["arch"])
	assert.Equal(t, runtime.Version(), out["goVersion"])
	_ = out["isGit"].(bool)
	v := out["decomposed"].(version.Version)
	assert.True(t, len(v) >= 2)
}

func TestCoreObscure(t *testing.T) {
	call := Calls.Get("core/obscure")
	assert.NotNil(t, call)
	in := Params{
		"clear": "potato",
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in["clear"], obscure.MustReveal(out["obscured"].(string)))
}

func TestCoreQuit(t *testing.T) {
	//The call should return an error if param exitCode is not parsed to int
	call := Calls.Get("core/quit")
	assert.NotNil(t, call)
	in := Params{
		"exitCode": "potato",
	}
	_, err := call.Fn(context.Background(), in)
	require.Error(t, err)
}

// core/command: Runs a raw rclone command
func TestCoreCommand(t *testing.T) {
	call := Calls.Get("core/command")
	r := fstest.NewRun(t)
	defer r.Finalise()

	in := Params{
		"command": "ls",
		"opt": map[string]string{
			"max-depth": "1",
		},
		"arg": []string{
			r.FremoteName,
		},
	}
	got, err := call.Fn(context.Background(), in)
	require.NoError(t, err)

	errorBool, err := got.GetBool("error")
	require.NoError(t, err)

	require.Equal(t, errorBool, false)
}
<commit_msg>core/command: Add version command instead of ls<commit_after>package rc

import (
	"context"
	"runtime"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/config/obscure"
	"github.com/rclone/rclone/fs/version"
)

func TestInternalNoop(t *testing.T) {
	call := Calls.Get("rc/noop")
	assert.NotNil(t, call)
	in := Params{
		"String": "hello",
		"Int":    42,
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in, out)
}

func TestInternalError(t *testing.T) {
	call := Calls.Get("rc/error")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.Error(t, err)
	require.Nil(t, out)
}

func TestInternalList(t *testing.T) {
	call := Calls.Get("rc/list")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, Params{"commands": Calls.List()}, out)
}

func TestCorePid(t *testing.T) {
	call := Calls.Get("core/pid")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	pid := out["pid"]
	assert.NotEqual(t, nil, pid)
	_, ok := pid.(int)
	assert.Equal(t, true, ok)
}

func TestCoreMemstats(t *testing.T) {
	call := Calls.Get("core/memstats")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	sys := out["Sys"]
	assert.NotEqual(t, nil, sys)
	_, ok := sys.(uint64)
	assert.Equal(t, true, ok)
}

func TestCoreGC(t *testing.T) {
	call := Calls.Get("core/gc")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.Nil(t, out)
	assert.Equal(t, Params(nil), out)
}

func TestCoreVersion(t *testing.T) {
	call := Calls.Get("core/version")
	assert.NotNil(t, call)
	in := Params{}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, fs.Version, out["version"])
	assert.Equal(t, runtime.GOOS, out["os"])
	assert.Equal(t, runtime.GOARCH, out["arch"])
	assert.Equal(t, runtime.Version(), out["goVersion"])
	_ = out["isGit"].(bool)
	v := out["decomposed"].(version.Version)
	assert.True(t, len(v) >= 2)
}

func TestCoreObscure(t *testing.T) {
	call := Calls.Get("core/obscure")
	assert.NotNil(t, call)
	in := Params{
		"clear": "potato",
	}
	out, err := call.Fn(context.Background(), in)
	require.NoError(t, err)
	require.NotNil(t, out)
	assert.Equal(t, in["clear"], obscure.MustReveal(out["obscured"].(string)))
}

func TestCoreQuit(t *testing.T) {
	//The call should return an error if param exitCode is not parsed to int
	call := Calls.Get("core/quit")
	assert.NotNil(t, call)
	in := Params{
		"exitCode": "potato",
	}
	_, err := call.Fn(context.Background(), in)
	require.Error(t, err)
}

// core/command: Runs a raw rclone command
func TestCoreCommand(t *testing.T) {
	call := Calls.Get("core/command")

	in := Params{
		"command": "version",
		"opt":     map[string]string{},
		"arg":     []string{},
	}
	got, err := call.Fn(context.Background(), in)
	require.NoError(t, err)

	errorBool, err := got.GetBool("error")
	require.NoError(t, err)

	require.Equal(t, errorBool, false)
}
<|endoftext|>


<commit_before>// Copyright 2013 Denis Pobedrya <denis.pobedrya@gmail.com> All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"github.com/thoj/go-ircevent"
	"io/ioutil"
	"regexp"
	"time"
	"strconv"
	"os"
	"strings"
)

type Config struct {
	Nick     string
	User     string
	Server   string
	Channels []string
	Admins   []string
}

type UserData struct {
	Date time.Time
}

var (
	users     map[string]UserData
	admins    []string
	cmdRe     *regexp.Regexp
	chatCmdRe *regexp.Regexp
	dateRe    *regexp.Regexp
)

func loadConfig(path string) Config {
	var c Config
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	err = json.Unmarshal(b, &c)
	if err != nil {
		panic(err)
	}
	return c
}

func isAdmin(nick string) bool {
	for _, s := range admins {
		if nick == s {
			return true
		}
	}
	return false
}

func setDate(user string, date time.Time) {
	users[user] = UserData{date}
	save()
}

func save() {
	data, _ := json.Marshal(users)
	ioutil.WriteFile("users.json", data, os.FileMode(0644))
}

func load() {
	b, err := ioutil.ReadFile("users.json")
	if err != nil {
		fmt.Println("Error reading users file")
		return
	}
	err = json.Unmarshal(b, &users)
	if err != nil {
		fmt.Println("Error parsing users file")
	}
}

var funcs = map[string]func(string, string) string{
	"hello": func(nick, data string) string {
		return "Hello, " + nick
	},
	"set": func(nick, data string) string {
		p := dateRe.FindStringSubmatch(data)
		if p == nil {
			return "Data format is yyyy-mm-dd"
		}
		dateStr := p[1]
		user := p[2]
		date, err := time.Parse("2006-01-02", dateStr)
		if err != nil {
			return err.Error()
		}
		if user == "" {
			user = nick
		}
		if user == nick || isAdmin(nick) {
			setDate(user, date)
			return "Counter of user " + user + " updated"
		} else {
			return "Sorry, you can't change other people's counters"
		}
	},
	"reset": func(nick, data string) string {
		user := data
		if user == "" {
			user = nick
		}
		now := time.Now()
		if user == nick || isAdmin(nick) {
			setDate(user, time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC))
			return "Counter for user " + user + " updated"
		}
		return "Sorry, you can't change other people's counters"
	},
	"get": func(nick, data string) string {
		user := data
		if user == "" {
			user = nick
		}
		if u, ok := users[user]; ok {
			dur := time.Now().Sub(u.Date)
			days := int(dur.Hours() / 24)
			var dstr string
			if days == 1 {
				dstr = " day"
			} else {
				dstr = " days"
			}
			return user + " is winning for " + strconv.Itoa(days) + dstr
		}
		return "Counter not found for user " + user
	},
	"help": func(nick, data string) string {
		return "Commands: help, set, get, reset, hello"
	},
}

func onMessage(bot *irc.Connection, from, to, message string) {
	fmt.Printf("%s => %s: %s\n", from, to, message)
	re := cmdRe
	answer := ""
	answerTo := from
	if to[0] == '#' {
		answer = from + ": "
		re = chatCmdRe
		answerTo = to
	}
	m := re.FindStringSubmatch(message)
	if m != nil {
		cmd, data := strings.ToLower(m[1]), m[2]
		fmt.Println("Cmd:", cmd, "Data:", data)
		if funcs[cmd] != nil {
			answer += funcs[cmd](from, data)
		} else {
			answer += "Command not found"
		}
		bot.Privmsg(answerTo, answer)
	}
}

func compileRegex(nick string) {
	cmdReStr := "(\\w+)(?:\\s(.+))?" //regex for command in private
	chatCmdReStr := "^" + nick + "[:,]\\s" + cmdReStr
	cmdRe, _ = regexp.Compile(cmdReStr)
	chatCmdRe, _ = regexp.Compile(chatCmdReStr)
	dateRe, _ = regexp.Compile(`(\d\d\d\d-\d?\d-\d?\d)(?:\s(\w+))?`)
}

func main() {
	config := loadConfig("conf.json")
	admins = config.Admins
	users = make(map[string]UserData)
	load()
	compileRegex(config.Nick)
	bot := irc.IRC(config.Nick, config.User)
	bot.Connect(config.Server)
	for _, ch := range config.Channels {
		bot.Join(ch)
	}
	bot.AddCallback("PRIVMSG", func(e *irc.Event) {
		onMessage(bot, e.Nick, e.Arguments[0], e.Message)
	})
	bot.Loop()
}
<commit_msg>Added delete command<commit_after>// Copyright 2013 Denis Pobedrya <denis.pobedrya@gmail.com> All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"github.com/thoj/go-ircevent"
	"io/ioutil"
	"regexp"
	"time"
	"strconv"
	"os"
	"strings"
)

type Config struct {
	Nick     string
	User     string
	Server   string
	Channels []string
	Admins   []string
}

type UserData struct {
	Date time.Time
}

var (
	users     map[string]UserData
	admins    []string
	cmdRe     *regexp.Regexp
	chatCmdRe *regexp.Regexp
	dateRe    *regexp.Regexp
)

func loadConfig(path string) Config {
	var c Config
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	err = json.Unmarshal(b, &c)
	if err != nil {
		panic(err)
	}
	return c
}

func isAdmin(nick string) bool {
	for _, s := range admins {
		if nick == s {
			return true
		}
	}
	return false
}

func setDate(user string, date time.Time) {
	users[user] = UserData{date}
	save()
}

func save() {
	data, _ := json.Marshal(users)
	ioutil.WriteFile("users.json", data, os.FileMode(0644))
}

func load() {
	b, err := ioutil.ReadFile("users.json")
	if err != nil {
		fmt.Println("Error reading users file")
		return
	}
	err = json.Unmarshal(b, &users)
	if err != nil {
		fmt.Println("Error parsing users file")
	}
}

var funcs = map[string]func(string, string) string{
	"hello": func(nick, data string) string {
		return "Hello, " + nick
	},
	"set": func(nick, data string) string {
		p := dateRe.FindStringSubmatch(data)
		if p == nil {
			return "Data format is yyyy-mm-dd"
		}
		dateStr := p[1]
		user := p[2]
		date, err := time.Parse("2006-01-02", dateStr)
		if err != nil {
			return err.Error()
		}
		if user == "" {
			user = nick
		}
		if user == nick || isAdmin(nick) {
			setDate(user, date)
			return "Counter of user " + user + " updated"
		} else {
			return "Sorry, you can't change other people's counters"
		}
	},
	"reset": func(nick, data string) string {
		user := data
		if user == "" {
			user = nick
		}
		now := time.Now()
		if user == nick || isAdmin(nick) {
			setDate(user, time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC))
			return "Counter for user " + user + " updated"
		}
		return "Sorry, you can't change other people's counters"
	},
	"get": func(nick, data string) string {
		user := data
		if user == "" {
			user = nick
		}
		if u, ok := users[user]; ok {
			dur := time.Now().Sub(u.Date)
			days := int(dur.Hours() / 24)
			var dstr string
			if days == 1 {
				dstr = " day"
			} else {
				dstr = " days"
			}
			return user + " is winning for " + strconv.Itoa(days) + dstr
		}
		return "Counter not found for user " + user
	},
	"help": func(nick, data string) string {
		return "Commands: help, set, get, reset, delete, hello"
	},
	"delete": func(nick, data string) string {
		user := data
		if user == "" {
			user = nick
		}
		if user == nick || isAdmin(nick) {
    		if _, ok := users[user]; ok {
	    	    delete(users, user)
	    	    save()
	    	    return "Counter for user " + user + " deleted"
	    	} else {
	    	    return "Counter not found for user " + user
	    	}
    	}
    	return "You can't delete other people's counters"
	},
}

func onMessage(bot *irc.Connection, from, to, message string) {
	fmt.Printf("%s => %s: %s\n", from, to, message)
	re := cmdRe
	answer := ""
	answerTo := from
	if to[0] == '#' {
		answer = from + ": "
		re = chatCmdRe
		answerTo = to
	}
	m := re.FindStringSubmatch(message)
	if m != nil {
		cmd, data := strings.ToLower(m[1]), m[2]
		fmt.Println("Cmd:", cmd, "Data:", data)
		if funcs[cmd] != nil {
			answer += funcs[cmd](from, data)
		} else {
			answer += "Command not found"
		}
		bot.Privmsg(answerTo, answer)
	}
}

func compileRegex(nick string) {
	cmdReStr := "(\\w+)(?:\\s(.+))?" //regex for command in private
	chatCmdReStr := "^" + nick + "[:,]\\s" + cmdReStr
	cmdRe, _ = regexp.Compile(cmdReStr)
	chatCmdRe, _ = regexp.Compile(chatCmdReStr)
	dateRe, _ = regexp.Compile(`(\d\d\d\d-\d?\d-\d?\d)(?:\s(\w+))?`)
}

func main() {
	config := loadConfig("conf.json")
	admins = config.Admins
	users = make(map[string]UserData)
	load()
	compileRegex(config.Nick)
	bot := irc.IRC(config.Nick, config.User)
	bot.Connect(config.Server)
	for _, ch := range config.Channels {
		bot.Join(ch)
	}
	bot.AddCallback("PRIVMSG", func(e *irc.Event) {
		onMessage(bot, e.Nick, e.Arguments[0], e.Message)
	})
	bot.Loop()
}
<|endoftext|>


<commit_before>package main

import (
	"time"
)

// Satisfies the StatsReporter interface to make testing easier.
type MockStatsdClient struct {
	Counts map[string]float64
	Gauges map[string]float64
}

func (c *MockStatsdClient) Flush() error {
	return nil
}

func (c *MockStatsdClient) Count(bucket string, value, sampleRate float64) {
	c.Counts[bucket] = value
}

func (c *MockStatsdClient) Gauge(bucket string, value float64) {
	c.Gauges[bucket] = value
}

func (c *MockStatsdClient) Timing(bucket string, value time.Duration) {
}

func (c *MockStatsdClient) CountUnique(bucket, value string) {
}
<commit_msg>Use 'statsd' package name in testing subpackage.<commit_after>package statsd

import (
	"time"
)

// Satisfies the StatsReporter interface to make testing easier.
type MockStatsdClient struct {
	Counts map[string]float64
	Gauges map[string]float64
}

func (c *MockStatsdClient) Flush() error {
	return nil
}

func (c *MockStatsdClient) Count(bucket string, value, sampleRate float64) {
	c.Counts[bucket] = value
}

func (c *MockStatsdClient) Gauge(bucket string, value float64) {
	c.Gauges[bucket] = value
}

func (c *MockStatsdClient) Timing(bucket string, value time.Duration) {
}

func (c *MockStatsdClient) CountUnique(bucket, value string) {
}
<|endoftext|>


<commit_before>// Package bq includes all code related to BigQuery.
//
// NB: NOTES ON MEMORY USE AND HTTP SIZE
// The bigquery library uses JSON encoding of data, which appears to be the only
// option at this time.  Furthermore, it uses intermediate data representations,
// eventually creating a map[string]Value (unless you pass that in to begin with).
// In general, when we start pumping large volumes of data, both the map and the
// JSON will cause some memory pressure, and likely pretty severe limits on the size
// of the insert we can send, likely on the order of a couple MB of actual row footprint
// in BQ.
//
// Passing in slice of structs makes memory pressure a bit worse, but probably isn't
// worth worrying about.

package bq

import (
	"log"
	"os"
	"sync"
	"time"

	"cloud.google.com/go/bigquery"
	"golang.org/x/net/context"

	"github.com/m-lab/etl/etl"
	"github.com/m-lab/etl/metrics"
)

// TODO(dev) Use a more thoughtful setting for buffer size.
// TODO(P3) Include the project name in the parameters.
// For now, 10K per row times 100 results is 1MB, which is an order of
// magnitude below our 10MB max, so 100 might not be such a bad
// default.
func NewInserter(dataset string, dt etl.DataType, partition time.Time) (etl.Inserter, error) {
	suffix := ""
	table := etl.DataTypeToTable[dt]
	if time.Since(partition) < 30*24*time.Hour {
		// If within past 30 days, we can stream directly to partition.
		suffix = "$" + partition.Format("20060102")
	} else {
		// Otherwise, we use a templated table, and must merge it later.
		suffix = "_" + partition.Format("20060102")
	}

	return NewBQInserter(
		etl.InserterParams{Dataset: dataset, Table: table, Suffix: suffix,
			Timeout: 15 * time.Minute, BufferSize: etl.DataTypeToBQBufferSize[dt]}, nil)

}

// TODO - improve the naming between here and NewInserter.
// Pass in nil uploader for normal use, custom uploader for custom behavior
func NewBQInserter(params etl.InserterParams, uploader etl.Uploader) (etl.Inserter, error) {
	if uploader == nil {
		client := MustGetClient(params.Timeout)
		table := params.Table
		if params.Suffix[0] == '$' {
			// Suffix starting with $ is just a partition spec.
			table += params.Suffix
		}
		u := client.Dataset(params.Dataset).Table(table).Uploader()
		if params.Suffix[0] == '_' {
			// Suffix starting with _ is a template suffix.
			u.TableTemplateSuffix = params.Suffix
		}
		// This avoids problems when a single row of the insert has invalid
		// data.  We then have to carefully parse the returned error object.
		u.SkipInvalidRows = true
		uploader = u
	}
	in := BQInserter{params: params, uploader: uploader, timeout: params.Timeout}
	in.rows = make([]interface{}, 0, in.params.BufferSize)
	return &in, nil
}

//===============================================================================
var (
	clientOnce sync.Once // This avoids a race on setting bqClient.
	bqClient   *bigquery.Client
)

// Returns the Singleton bigquery client for this process.
// TODO - is there any advantage to using more than one client?
func MustGetClient(timeout time.Duration) *bigquery.Client {
	// We do this here, instead of in init(), because we only want to do it
	// when we actually want to access the bigquery backend.
	clientOnce.Do(func() {
		ctx, _ := context.WithTimeout(context.Background(), timeout)
		project, ok := os.LookupEnv("BIGQUERY_PROJECT")
		if !ok {
			project = os.Getenv("GCLOUD_PROJECT")
		}

		log.Printf("Using project: %s\n", project)
		// Heavyweight!
		var err error
		bqClient, err = bigquery.NewClient(ctx, project)
		if err != nil {
			panic(err.Error())
		}
	})
	return bqClient
}

//===============================================================================

// Generic implementation of bq.ValueSaver, based on map.  This avoids extra
// conversion steps in the bigquery library (except for the JSON conversion).
// IMPLEMENTS: bigquery.ValueSaver
type MapSaver struct {
	Values map[string]bigquery.Value
}

func (s *MapSaver) Save() (row map[string]bigquery.Value, insertID string, err error) {
	return s.Values, "", nil
}

//----------------------------------------------------------------------------

type BQInserter struct {
	etl.Inserter
	params   etl.InserterParams
	uploader etl.Uploader // May be a BQ Uploader, or a test Uploader
	timeout  time.Duration
	rows     []interface{}
	inserted int // Number of rows successfully inserted.
	badRows  int // Number of row failures, including rows in full failures.
	failures int // Number of complete insert failures.
}

// Caller should check error, and take appropriate action before calling again.
func (in *BQInserter) InsertRow(data interface{}) error {
	return in.InsertRows([]interface{}{data})
}

// Caller should check error, and take appropriate action before calling again.
// TODO - should this return a specific error to indicate that a flush is needed
// instead of flushing internally?  The "handle errors in the middle" would
// be easier, though other complications would ensue.
func (in *BQInserter) InsertRows(data []interface{}) error {
	metrics.WorkerState.WithLabelValues("insert").Inc()
	defer metrics.WorkerState.WithLabelValues("insert").Dec()

	for len(data)+len(in.rows) >= in.params.BufferSize {
		// space >= len(data)
		space := cap(in.rows) - len(in.rows)
		var add []interface{}
		add, data = data[:space], data[space:] // does this break?
		in.rows = append(in.rows, add...)
		err := in.Flush()
		if err != nil {
			// TODO - handle errors in middle better?
			return err
		}
	}
	in.rows = append(in.rows, data...)
	return nil
}

func (in *BQInserter) HandleInsertErrors(err error) error {
	switch typedErr := err.(type) {
	case bigquery.PutMultiError:
		if len(typedErr) == len(in.rows) {
			log.Printf("%v\n", err)
			metrics.BackendFailureCount.WithLabelValues(
				in.TableBase(), "failed insert").Inc()
			in.failures += 1
		}
		// If ALL rows failed, and number of rows is large, just report single failure.
		if len(typedErr) > 10 && len(typedErr) == len(in.rows) {
			log.Printf("Insert error: %v\n", err)
			metrics.ErrorCount.WithLabelValues(
				in.TableBase(), "unknown", "insert row error").
				Add(float64(len(typedErr)))
		} else {
			// Handle each error individually.
			// TODO Should we try to handle large numbers of row errors?
			for _, rowError := range typedErr {
				// These are rowInsertionErrors
				log.Printf("%v\n", rowError)
				// rowError.Errors is a MultiError
				for _, oneErr := range rowError.Errors {
					log.Printf("Insert error: %v\n", oneErr)
					metrics.ErrorCount.WithLabelValues(
						in.TableBase(), "unknown", "insert row error").Inc()
				}
			}
		}
		in.inserted += len(in.rows) - len(typedErr)
		in.badRows += len(typedErr)
		err = nil
	default:
		log.Printf("Unhandled insert error %v\n", typedErr)
		metrics.BackendFailureCount.WithLabelValues(
			in.TableBase(), "failed insert").Inc()
		metrics.ErrorCount.WithLabelValues(
			in.TableBase(), "unknown", "UNHANDLED insert error").Inc()
		// TODO - Conservative, but possibly not correct.
		// This at least preserves the count invariance.
		in.badRows += len(in.rows)
		err = nil
	}
	// Allocate new slice of rows.  Any failed rows are lost.
	in.rows = make([]interface{}, 0, in.params.BufferSize)
	return err
}

// TODO(dev) Should have a recovery mechanism for failed inserts.
func (in *BQInserter) Flush() error {
	metrics.WorkerState.WithLabelValues("flush").Inc()
	defer metrics.WorkerState.WithLabelValues("flush").Dec()

	if len(in.rows) == 0 {
		return nil
	}

	// This is heavyweight, and may run forever without a context deadline.
	ctx, _ := context.WithTimeout(context.Background(), in.timeout)
	err := in.uploader.Put(ctx, in.rows)
	if err == nil {
		in.inserted += len(in.rows)
		in.rows = make([]interface{}, 0, in.params.BufferSize)
	} else {
		// This adjusts the inserted count, failure count, and updates in.rows.
		err = in.HandleInsertErrors(err)
	}
	return err
}

func (in *BQInserter) FullTableName() string {
	return in.TableBase() + in.TableSuffix()
}
func (in *BQInserter) TableBase() string {
	return in.params.Table
}

// The $ or _ suffix.
func (in *BQInserter) TableSuffix() string {
	return in.params.Suffix
}
func (in *BQInserter) Dataset() string {
	return in.params.Dataset
}
func (in *BQInserter) RowsInBuffer() int {
	return len(in.rows)
}
func (in *BQInserter) Accepted() int {
	return in.inserted + in.badRows + len(in.rows)
}
func (in *BQInserter) Committed() int {
	return in.inserted
}
func (in *BQInserter) Failed() int {
	return in.badRows
}

//----------------------------------------------------------------------------

// Inserter wrapper that handles flush metrics.
// TODO - add prometheus counters for attempts, number of rows.
type DurationWrapper struct {
	etl.Inserter
}

func (dw DurationWrapper) Flush() error {
	t := time.Now()
	status := "succeed"
	err := dw.Inserter.Flush()
	if err != nil {
		status = "fail"
	}
	metrics.InsertionHistogram.WithLabelValues(
		dw.TableBase(), status).Observe(time.Since(t).Seconds())
	return err
}
<commit_msg>move buffer allocation<commit_after>// Package bq includes all code related to BigQuery.
//
// NB: NOTES ON MEMORY USE AND HTTP SIZE
// The bigquery library uses JSON encoding of data, which appears to be the only
// option at this time.  Furthermore, it uses intermediate data representations,
// eventually creating a map[string]Value (unless you pass that in to begin with).
// In general, when we start pumping large volumes of data, both the map and the
// JSON will cause some memory pressure, and likely pretty severe limits on the size
// of the insert we can send, likely on the order of a couple MB of actual row footprint
// in BQ.
//
// Passing in slice of structs makes memory pressure a bit worse, but probably isn't
// worth worrying about.

package bq

import (
	"log"
	"os"
	"sync"
	"time"

	"cloud.google.com/go/bigquery"
	"golang.org/x/net/context"

	"github.com/m-lab/etl/etl"
	"github.com/m-lab/etl/metrics"
)

// TODO(dev) Use a more thoughtful setting for buffer size.
// TODO(P3) Include the project name in the parameters.
// For now, 10K per row times 100 results is 1MB, which is an order of
// magnitude below our 10MB max, so 100 might not be such a bad
// default.
func NewInserter(dataset string, dt etl.DataType, partition time.Time) (etl.Inserter, error) {
	suffix := ""
	table := etl.DataTypeToTable[dt]
	if time.Since(partition) < 30*24*time.Hour {
		// If within past 30 days, we can stream directly to partition.
		suffix = "$" + partition.Format("20060102")
	} else {
		// Otherwise, we use a templated table, and must merge it later.
		suffix = "_" + partition.Format("20060102")
	}

	return NewBQInserter(
		etl.InserterParams{Dataset: dataset, Table: table, Suffix: suffix,
			Timeout: 15 * time.Minute, BufferSize: etl.DataTypeToBQBufferSize[dt]}, nil)

}

// TODO - improve the naming between here and NewInserter.
// Pass in nil uploader for normal use, custom uploader for custom behavior
func NewBQInserter(params etl.InserterParams, uploader etl.Uploader) (etl.Inserter, error) {
	if uploader == nil {
		client := MustGetClient(params.Timeout)
		table := params.Table
		if params.Suffix[0] == '$' {
			// Suffix starting with $ is just a partition spec.
			table += params.Suffix
		}
		u := client.Dataset(params.Dataset).Table(table).Uploader()
		if params.Suffix[0] == '_' {
			// Suffix starting with _ is a template suffix.
			u.TableTemplateSuffix = params.Suffix
		}
		// This avoids problems when a single row of the insert has invalid
		// data.  We then have to carefully parse the returned error object.
		u.SkipInvalidRows = true
		uploader = u
	}
	in := BQInserter{params: params, uploader: uploader, timeout: params.Timeout}
	in.rows = make([]interface{}, 0, in.params.BufferSize)
	return &in, nil
}

//===============================================================================
var (
	clientOnce sync.Once // This avoids a race on setting bqClient.
	bqClient   *bigquery.Client
)

// Returns the Singleton bigquery client for this process.
// TODO - is there any advantage to using more than one client?
func MustGetClient(timeout time.Duration) *bigquery.Client {
	// We do this here, instead of in init(), because we only want to do it
	// when we actually want to access the bigquery backend.
	clientOnce.Do(func() {
		ctx, _ := context.WithTimeout(context.Background(), timeout)
		project, ok := os.LookupEnv("BIGQUERY_PROJECT")
		if !ok {
			project = os.Getenv("GCLOUD_PROJECT")
		}

		log.Printf("Using project: %s\n", project)
		// Heavyweight!
		var err error
		bqClient, err = bigquery.NewClient(ctx, project)
		if err != nil {
			panic(err.Error())
		}
	})
	return bqClient
}

//===============================================================================

// Generic implementation of bq.ValueSaver, based on map.  This avoids extra
// conversion steps in the bigquery library (except for the JSON conversion).
// IMPLEMENTS: bigquery.ValueSaver
type MapSaver struct {
	Values map[string]bigquery.Value
}

func (s *MapSaver) Save() (row map[string]bigquery.Value, insertID string, err error) {
	return s.Values, "", nil
}

//----------------------------------------------------------------------------

type BQInserter struct {
	etl.Inserter
	params   etl.InserterParams
	uploader etl.Uploader // May be a BQ Uploader, or a test Uploader
	timeout  time.Duration
	rows     []interface{}
	inserted int // Number of rows successfully inserted.
	badRows  int // Number of row failures, including rows in full failures.
	failures int // Number of complete insert failures.
}

// Caller should check error, and take appropriate action before calling again.
func (in *BQInserter) InsertRow(data interface{}) error {
	return in.InsertRows([]interface{}{data})
}

// Caller should check error, and take appropriate action before calling again.
// TODO - should this return a specific error to indicate that a flush is needed
// instead of flushing internally?  The "handle errors in the middle" would
// be easier, though other complications would ensue.
func (in *BQInserter) InsertRows(data []interface{}) error {
	metrics.WorkerState.WithLabelValues("insert").Inc()
	defer metrics.WorkerState.WithLabelValues("insert").Dec()

	for len(data)+len(in.rows) >= in.params.BufferSize {
		// space >= len(data)
		space := cap(in.rows) - len(in.rows)
		var add []interface{}
		add, data = data[:space], data[space:] // does this break?
		in.rows = append(in.rows, add...)
		err := in.Flush()
		if err != nil {
			// TODO - handle errors in middle better?
			return err
		}
	}
	in.rows = append(in.rows, data...)
	return nil
}

func (in *BQInserter) HandleInsertErrors(err error) error {
	switch typedErr := err.(type) {
	case bigquery.PutMultiError:
		if len(typedErr) == len(in.rows) {
			log.Printf("%v\n", err)
			metrics.BackendFailureCount.WithLabelValues(
				in.TableBase(), "failed insert").Inc()
			in.failures += 1
		}
		// If ALL rows failed, and number of rows is large, just report single failure.
		if len(typedErr) > 10 && len(typedErr) == len(in.rows) {
			log.Printf("Insert error: %v\n", err)
			metrics.ErrorCount.WithLabelValues(
				in.TableBase(), "unknown", "insert row error").
				Add(float64(len(typedErr)))
		} else {
			// Handle each error individually.
			// TODO Should we try to handle large numbers of row errors?
			for _, rowError := range typedErr {
				// These are rowInsertionErrors
				log.Printf("%v\n", rowError)
				// rowError.Errors is a MultiError
				for _, oneErr := range rowError.Errors {
					log.Printf("Insert error: %v\n", oneErr)
					metrics.ErrorCount.WithLabelValues(
						in.TableBase(), "unknown", "insert row error").Inc()
				}
			}
		}
		in.inserted += len(in.rows) - len(typedErr)
		in.badRows += len(typedErr)
		err = nil
	default:
		log.Printf("Unhandled insert error %v\n", typedErr)
		metrics.BackendFailureCount.WithLabelValues(
			in.TableBase(), "failed insert").Inc()
		metrics.ErrorCount.WithLabelValues(
			in.TableBase(), "unknown", "UNHANDLED insert error").Inc()
		// TODO - Conservative, but possibly not correct.
		// This at least preserves the count invariance.
		in.badRows += len(in.rows)
		err = nil
	}
	return err
}

// TODO(dev) Should have a recovery mechanism for failed inserts.
func (in *BQInserter) Flush() error {
	metrics.WorkerState.WithLabelValues("flush").Inc()
	defer metrics.WorkerState.WithLabelValues("flush").Dec()

	if len(in.rows) == 0 {
		return nil
	}

	// This is heavyweight, and may run forever without a context deadline.
	ctx, _ := context.WithTimeout(context.Background(), in.timeout)
	err := in.uploader.Put(ctx, in.rows)
	if err == nil {
		in.inserted += len(in.rows)
	} else {
		// This adjusts the inserted count, failure count, and updates in.rows.
		err = in.HandleInsertErrors(err)
	}
	// Allocate new slice of rows.  Any failed rows are lost.
	in.rows = make([]interface{}, 0, in.params.BufferSize)
	return err
}

func (in *BQInserter) FullTableName() string {
	return in.TableBase() + in.TableSuffix()
}
func (in *BQInserter) TableBase() string {
	return in.params.Table
}

// The $ or _ suffix.
func (in *BQInserter) TableSuffix() string {
	return in.params.Suffix
}
func (in *BQInserter) Dataset() string {
	return in.params.Dataset
}
func (in *BQInserter) RowsInBuffer() int {
	return len(in.rows)
}
func (in *BQInserter) Accepted() int {
	return in.inserted + in.badRows + len(in.rows)
}
func (in *BQInserter) Committed() int {
	return in.inserted
}
func (in *BQInserter) Failed() int {
	return in.badRows
}

//----------------------------------------------------------------------------

// Inserter wrapper that handles flush metrics.
// TODO - add prometheus counters for attempts, number of rows.
type DurationWrapper struct {
	etl.Inserter
}

func (dw DurationWrapper) Flush() error {
	t := time.Now()
	status := "succeed"
	err := dw.Inserter.Flush()
	if err != nil {
		status = "fail"
	}
	metrics.InsertionHistogram.WithLabelValues(
		dw.TableBase(), status).Observe(time.Since(t).Seconds())
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"crypto/tls"
	"encoding/xml"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	xmpp "github.com/tsuibin/goxmpp2/xmpp"
	"golang.org/x/crypto/otr"
)

type XMPPClient struct {
	// Embedded client
	C *xmpp.Client

	// Connection Status channel:
	Status chan xmpp.Status

	conversations map[xmpp.JID]*otr.Conversation
}

func NewXMPPClient(jid xmpp.JID, pw string) (*XMPPClient, error) {
	client := &XMPPClient{}
	client.conversations = make(map[xmpp.JID]*otr.Conversation)

	tlsConf := tls.Config{InsecureSkipVerify: true}
	innerClient, err := xmpp.NewClient(&jid, pw,
		tlsConf, nil, xmpp.Presence{}, client.Status)

	if err != nil {
		log.Fatalf("NewClient(%v): %v", jid, err)
		return nil, err
	}

	client.C = innerClient

	// Remember to update the status:
	go func() {
		for status := range client.Status {
			fmt.Println("connection status %d", status)
		}
	}()

	return client, nil
}

// TODO: This is only a dummy.
func loadPrivateKey() *otr.PrivateKey {
	key := &otr.PrivateKey{}
	key.Generate(rand.Reader)

	// if file, err := os.Open("/tmp/keyfile"); err != nil {
	// 	// Generate fresh one:
	// 	key.Generate(rand.Reader)
	// 	fmt.Println("Key Generated", key.Serialize(nil))

	// 	// Save for next time:
	// 	ioutil.WriteFile("/tmp/keyfile", key.Serialize(nil), 0775)
	// } else {
	// 	buffer := make([]byte, 4096)
	// 	n, _ := file.Read(buffer)
	// 	_, ok := key.Parse(buffer[:n])
	// 	fmt.Print("Key was cached: ")
	// 	if ok {
	// 		fmt.Println("Success!")
	// 	} else {
	// 		fmt.Println("Nope.")
	// 	}
	// }

	return key
}

func (client *XMPPClient) getConversation(jid xmpp.JID) *otr.Conversation {
	con, ok := client.conversations[jid]
	if !ok {
		con = &otr.Conversation{}
		con.PrivateKey = loadPrivateKey()
		client.conversations[jid] = con
	}

	return con
}

func truncate(a string, l int) string {
	if len(a) > l {
		return a[:l]
	}

	return a
}

func (client *XMPPClient) Recv(msg *xmpp.Message) {
	con := client.getConversation(msg.From)

	for _, data := range msg.Body {
		data, encrypted, state, toSend, err := con.Receive([]byte(data.Chardata))
		if err != nil {
			fmt.Println("\n\n!!!!! ", err)
		}

		fmt.Printf("RECV: `%v` (encr: %v %v) (state-change: %v)\n",
			truncate(string(data), 20), encrypted, con.IsEncrypted(), state)

		if state == otr.NewKeys {
			authToSend, authErr := con.Authenticate("weis nich?", []byte("eule"))
			fmt.Println("==> AUTH REQUEST")
			if authErr != nil {
				fmt.Println("============ AUTH ==========")
				fmt.Println(authErr)
				fmt.Println("============ AUTH ==========")
			}
			toSend = append(toSend, authToSend...)
		}

		for _, s := range toSend {
			fmt.Printf("   SEND BACK: `%v`\n", truncate(string(s), 20))
			// client.Send(msg.From, string(s))
			xmsg := xmpp.Message{}
			xmsg.From = client.C.Jid
			xmsg.To = msg.From
			xmsg.Id = xmpp.NextId()

			xmsg.Type = "chat"
			xmsg.Lang = "en"
			xmsg.Body = []xmpp.Text{{XMLName: xml.Name{Local: "body"}, Chardata: string(s)}}

			client.C.Send <- &xmsg
		}
	}
}

func (client *XMPPClient) Send(to xmpp.JID, text string) {
	// Encrypt the message via OTR
	// (TODO: Abort if not encrypted: con.IsEncrypted)
	con := client.getConversation(to)
	base64Texts, err := con.Send([]byte(text))

	if err != nil {
		fmt.Println("!! ", err)
		return
	}

	fmt.Printf("SEND(%v): %v %v\n", con.IsEncrypted(), text, truncate(string(base64Texts[0]), 20))

	for _, base64Text := range base64Texts {
		xmsg := xmpp.Message{}
		xmsg.From = client.C.Jid
		xmsg.To = to
		xmsg.Id = xmpp.NextId()

		xmsg.Type = "chat"
		xmsg.Lang = "en"
		xmsg.Body = []xmpp.Text{{XMLName: xml.Name{Local: "body"}, Chardata: string(base64Text)}}

		client.C.Send <- &xmsg
	}
}

func (client *XMPPClient) Close() {
	// TODO: Iterate over all conversations and close them
	for _, conversation := range client.conversations {
		conversation.End()
	}
	client.C.Close()
}

func init() {
	// xmpp.Debug = true
}

func main() {
	jid := flag.String("jid", "", "JID to log in as")
	pwd := flag.String("pw", "", "password")
	to := flag.String("to", "bob@jabber.nullcat.de/desktop", "Receiver")
	send := flag.Bool("send", false, "Send otr query")

	flag.Parse()

	if *jid == "" || *pwd == "" {
		flag.Usage()
		os.Exit(2)
	}

	client, err := NewXMPPClient(xmpp.JID(*jid), *pwd)
	if err != nil {
		log.Fatalf("Could not create client: %v", err)
		return
	}

	defer client.Close()

	go func(ch <-chan xmpp.Stanza) {
		for stanza := range ch {
			if msg, ok := stanza.(*xmpp.Message); ok {
				// fmt.Printf("--->\n%s: %s\n<---\n", msg.From, msg.Body[0].Chardata)
				client.Recv(msg)
				// client.Send(msg.From, "pong!")
				// client.Send(msg.From, otr.QueryMessage)
			}
		}
	}(client.C.Recv)

	sendOtr := true
	for {
		if *send {
			if sendOtr {
				client.Send(xmpp.JID(*to), "Hello me. "+otr.QueryMessage)
				sendOtr = false
			} else {
				client.Send(xmpp.JID(*to), "Hello me. ")
			}
		}
		time.Sleep(5 * time.Second)
	}

	// stat := make(chan xmpp.Status)
	// go func() {
	// 	for s := range stat {
	// 		log.Printf("connection status %d", s)
	// 	}
	// }()

	// tlsConf := tls.Config{InsecureSkipVerify: true}
	// c, err := xmpp.NewClient(&jid, *pw, tlsConf, nil, xmpp.Presence{}, stat)
	// if err != nil {
	// 	log.Fatalf("NewClient(%v): %v", jid, err)
	// }
	// defer c.Close()

	// go func(ch <-chan xmpp.Stanza) {
	// 	for obj := range ch {
	// 		fmt.Printf("s: %v\n", obj)
	// 	}
	// 	fmt.Println("done reading")
	// }(c.Recv)

	//msg := createMessage("sahib@jabber.nullcat.de/xxx", "christoph@jabber.nullcat.de/xxx", "Hello Kitteh")
	//c.Send <- msg

	// roster := c.Roster.Get()
	// fmt.Printf("%d roster entries:\n", len(roster))
	// for i, entry := range roster {
	// 	fmt.Printf("%d: %v %v %v\n", i, entry.Jid, entry.Name, entry.Subscription)
	// }

	// p := make([]byte, 1024)
	// for {
	// 	nr, _ := os.Stdin.Read(p)
	// 	if nr == 0 {
	// 		break
	// 	}
	// 	s := string(p)
	// 	dec := xml.NewDecoder(strings.NewReader(s))
	// 	t, err := dec.Token()
	// 	if err != nil {
	// 		fmt.Printf("token: %s\n", err)
	// 		break
	// 	}
	// 	var se *xml.StartElement
	// 	var ok bool
	// 	if se, ok = t.(*xml.StartElement); !ok {
	// 		fmt.Println("Couldn't find start element")
	// 		break
	// 	}
	// 	var stan xmpp.Stanza
	// 	switch se.Name.Local {
	// 	case "iq":
	// 		stan = &xmpp.Iq{}
	// 	case "message":
	// 		stan = &xmpp.Message{}
	// 	case "presence":
	// 		stan = &xmpp.Presence{}
	// 	default:
	// 		fmt.Println("Can't parse non-stanza.")
	// 		continue
	// 	}
	// 	err = dec.Decode(stan)
	// 	if err == nil {
	// 		c.Send <- stan
	// 	} else {
	// 		fmt.Printf("Parse error: %v\n", err)
	// 		break
	// 	}
	// }
	// fmt.Println("done sending")
}
<commit_msg>otr: Wow, it works! Cleanup experiment a bit...<commit_after>package main

import (
	"crypto/rand"
	"crypto/tls"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	xmpp "github.com/tsuibin/goxmpp2/xmpp"
	"golang.org/x/crypto/otr"
)

type XMPPClient struct {
	// Embedded client
	C *xmpp.Client

	// Connection Status channel:
	Status chan xmpp.Status

	// Map of JID to Conversation layer
	conversations map[xmpp.JID]*otr.Conversation
}

func NewXMPPClient(jid xmpp.JID, pw string) (*XMPPClient, error) {
	client := &XMPPClient{}
	client.conversations = make(map[xmpp.JID]*otr.Conversation)

	// TODO: This tls config is probably a bad idea.
	tlsConf := tls.Config{InsecureSkipVerify: true}
	innerClient, err := xmpp.NewClient(&jid, pw,
		tlsConf, nil, xmpp.Presence{}, client.Status)

	if err != nil {
		log.Fatalf("NewClient(%v): %v", jid, err)
		return nil, err
	}

	client.C = innerClient

	// Remember to update the status:
	go func() {
		for status := range client.Status {
			fmt.Println("connection status %d", status)
		}
	}()

	return client, nil
}

// TODO: This is only a dummy.
func loadPrivateKey() *otr.PrivateKey {
	key := &otr.PrivateKey{}

	if file, err := os.Open("/tmp/keyfile"); err != nil {
		// Generate fresh one:
		key.Generate(rand.Reader)
		fmt.Printf("Key Generated: %x\n", key.Serialize(nil))

		// Save for next time:
		ioutil.WriteFile("/tmp/keyfile", key.Serialize(nil), 0775)
	} else {
		// TODO: This *seems* to work, assert it does.
		buffer := make([]byte, 4096)
		n, _ := file.Read(buffer)
		_, ok := key.Parse(buffer[:n])
		fmt.Print("Key was cached: ")
		if ok {
			fmt.Println("Success!")
		} else {
			fmt.Println("Nope.")
		}
	}

	return key
}

func (client *XMPPClient) getConversation(jid xmpp.JID) *otr.Conversation {
	con, ok := client.conversations[jid]
	if !ok {
		fmt.Printf("NEW CONVERSATION: `%v`\n", string(jid))
		con = &otr.Conversation{}
		con.PrivateKey = loadPrivateKey()
		client.conversations[jid] = con
	}

	return con
}

func truncate(a string, l int) string {
	if len(a) > l {
		return a[:l]
	}

	return a
}

func createMessage(from, to string, text []byte) *xmpp.Message {
	xmsg := &xmpp.Message{}
	xmsg.From = xmpp.JID(from)
	xmsg.To = xmpp.JID(to)
	xmsg.Id = xmpp.NextId()

	xmsg.Type = "chat"
	xmsg.Lang = "en"
	xmsg.Body = []xmpp.Text{{XMLName: xml.Name{Local: "body"}, Chardata: string(text)}}

	return xmsg
}

func (client *XMPPClient) Recv(msg *xmpp.Message) {
	con := client.getConversation(msg.From)
	sendBack := make([][]byte, 0)

	for _, data := range msg.Body {
		data, encrypted, state, toSend, err := con.Receive([]byte(data.Chardata))
		if err != nil {
			fmt.Println("\n\n!!!!! ", err)
		}

		sendBack = append(sendBack, toSend...)

		fmt.Printf("RECV: `%v` (encr: %v %v) (state-change: %v)\n",
			truncate(string(data), 20), encrypted, con.IsEncrypted(), state)

		switch state {
		case otr.NewKeys:
			authToSend, authErr := con.Authenticate("weis nich?", []byte("eule"))
			fmt.Println("==> AUTH REQUEST")
			if authErr != nil {
				fmt.Println("============ AUTH ==========")
				fmt.Println(authErr)
				fmt.Println("============ AUTH ==========")
			}
			sendBack = append(sendBack, authToSend...)
		}

		for _, s := range sendBack {
			fmt.Printf("   SEND(%v) BACK: `%v`\n", con.IsEncrypted(), truncate(string(s), 20))
			client.C.Send <- createMessage(string(client.C.Jid), string(msg.From), s)
		}
	}
}

func (client *XMPPClient) Send(to xmpp.JID, text string) {
	con := client.getConversation(to)

	base64Texts, err := con.Send([]byte(text))
	fmt.Printf("SEND(%v): %v %v\n", con.IsEncrypted(), text, truncate(string(base64Texts[0]), 20))

	if err != nil {
		fmt.Println("!! ", err)
		return
	}

	for _, base64Text := range base64Texts {
		client.C.Send <- createMessage(string(client.C.Jid), string(to), base64Text)
	}
}

func (client *XMPPClient) Close() {
	for jid, conversation := range client.conversations {
		fmt.Println("Closing OTR conversation to", jid)
		conversation.End()
	}
	client.C.Close()
}

func init() {
	// xmpp.Debug = true
}

func main() {
	jid := flag.String("jid", "", "JID to log in as")
	pwd := flag.String("pw", "", "password")
	to := flag.String("to", "bob@jabber.nullcat.de/desktop", "Receiver")
	send := flag.Bool("send", false, "Send otr query")

	flag.Parse()

	if *jid == "" || *pwd == "" {
		flag.Usage()
		os.Exit(2)
	}

	client, err := NewXMPPClient(xmpp.JID(*jid), *pwd)
	if err != nil {
		log.Fatalf("Could not create client: %v", err)
		return
	}

	defer client.Close()

	go func(ch <-chan xmpp.Stanza) {
		for stanza := range ch {
			if msg, ok := stanza.(*xmpp.Message); ok {
				// fmt.Printf("--->\n%s: %s\n<---\n", msg.From, msg.Body[0].Chardata)
				client.Recv(msg)
			}
		}
	}(client.C.Recv)

	sendOtr := true
	for {
		if *send {
			if sendOtr {
				client.Send(xmpp.JID(*to), "Hello me. "+otr.QueryMessage)
				sendOtr = false
			} else {
				client.Send(xmpp.JID(*to), "Hello me. ")
			}
		}
		time.Sleep(5 * time.Second)
	}
}
<|endoftext|>


<commit_before>package gocb

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

// View represents a Couchbase view within a design document.
type View struct {
	Map    string `json:"map,omitempty"`
	Reduce string `json:"reduce,omitempty"`
}

func (v View) hasReduce() bool {
	return v.Reduce != ""
}

// DesignDocument represents a Couchbase design document containing multiple views.
type DesignDocument struct {
	Name         string          `json:"-"`
	Views        map[string]View `json:"views,omitempty"`
	SpatialViews map[string]View `json:"spatial,omitempty"`
}

// IndexInfo represents a Couchbase GSI index.
type IndexInfo struct {
	Name      string    `json:"name"`
	IsPrimary bool      `json:"is_primary"`
	Type      IndexType `json:"using"`
	State     string    `json:"state"`
	Keyspace  string    `json:"keyspace_id"`
	Namespace string    `json:"namespace_id"`
	IndexKey  []string  `json:"index_key"`
}

// BucketManager provides methods for performing bucket management operations.
// See ClusterManager for methods that allow creating and removing buckets themselves.
type BucketManager struct {
	bucket   *Bucket
	username string
	password string
}

func (bm *BucketManager) capiRequest(method, uri, contentType string, body io.Reader) (*http.Response, error) {
	if contentType == "" && body != nil {
		panic("Content-type must be specified for non-null body.")
	}

	viewEp, err := bm.bucket.getViewEp()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, viewEp+uri, body)
	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(bm.username, bm.password)
	return bm.bucket.client.HttpClient().Do(req)
}

func (bm *BucketManager) mgmtRequest(method, uri, contentType string, body io.Reader) (*http.Response, error) {
	if contentType == "" && body != nil {
		panic("Content-type must be specified for non-null body.")
	}

	mgmtEp, err := bm.bucket.getMgmtEp()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, mgmtEp+uri, body)
	if err != nil {
		return nil, err
	}

	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}
	if bm.username != "" || bm.password != "" {
		req.SetBasicAuth(bm.username, bm.password)
	}

	return bm.bucket.client.HttpClient().Do(req)
}

// Flush will delete all the of the data from a bucket.
// Keep in mind that you must have flushing enabled in the buckets configuration.
func (bm *BucketManager) Flush() error {
	reqUri := fmt.Sprintf("/pools/default/buckets/%s/controller/doFlush", bm.bucket.name)
	resp, err := bm.mgmtRequest("POST", reqUri, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}
	return nil
}

// GetDesignDocument retrieves a single design document for the given bucket..
func (bm *BucketManager) GetDesignDocument(name string) (*DesignDocument, error) {
	reqUri := fmt.Sprintf("/_design/%s", name)

	resp, err := bm.capiRequest("GET", reqUri, "", nil)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return nil, clientError{string(data)}
	}

	ddocObj := DesignDocument{}
	jsonDec := json.NewDecoder(resp.Body)
	err = jsonDec.Decode(&ddocObj)
	if err != nil {
		return nil, err
	}

	ddocObj.Name = name
	return &ddocObj, nil
}

// GetDesignDocuments will retrieve all design documents for the given bucket.
func (bm *BucketManager) GetDesignDocuments() ([]*DesignDocument, error) {
	reqUri := fmt.Sprintf("/pools/default/buckets/%s/ddocs", bm.bucket.name)

	resp, err := bm.mgmtRequest("GET", reqUri, "", nil)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return nil, clientError{string(data)}
	}

	var ddocsObj struct {
		Rows []struct {
			Doc struct {
				Meta struct {
					Id string
				}
				Json DesignDocument
			}
		}
	}
	jsonDec := json.NewDecoder(resp.Body)
	err = jsonDec.Decode(&ddocsObj)
	if err != nil {
		return nil, err
	}

	var ddocs []*DesignDocument
	for index, ddocData := range ddocsObj.Rows {
		ddoc := &ddocsObj.Rows[index].Doc.Json
		ddoc.Name = ddocData.Doc.Meta.Id[8:]
		ddocs = append(ddocs, ddoc)
	}

	return ddocs, nil
}

// InsertDesignDocument inserts a design document to the given bucket.
func (bm *BucketManager) InsertDesignDocument(ddoc *DesignDocument) error {
	oldDdoc, err := bm.GetDesignDocument(ddoc.Name)
	if oldDdoc != nil || err == nil {
		return clientError{"Design document already exists"}
	}
	return bm.UpsertDesignDocument(ddoc)
}

// UpsertDesignDocument will insert a design document to the given bucket, or update
// an existing design document with the same name.
func (bm *BucketManager) UpsertDesignDocument(ddoc *DesignDocument) error {
	reqUri := fmt.Sprintf("/_design/%s", ddoc.Name)

	data, err := json.Marshal(&ddoc)
	if err != nil {
		return err
	}

	resp, err := bm.capiRequest("PUT", reqUri, "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}

	if resp.StatusCode != 201 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}

	return nil
}

// RemoveDesignDocument will remove a design document from the given bucket.
func (bm *BucketManager) RemoveDesignDocument(name string) error {
	reqUri := fmt.Sprintf("/_design/%s", name)

	resp, err := bm.capiRequest("DELETE", reqUri, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}

	return nil
}

func (bm *BucketManager) createIndex(indexName string, fields []string, ignoreIfExists, deferred bool) error {
	var qs string

	if len(fields) == 0 {
		qs += "CREATE PRIMARY INDEX"
	} else {
		qs += "CREATE INDEX"
	}
	if indexName != "" {
		qs += " `" + indexName + "`"
	}
	qs += " ON `" + bm.bucket.name + "`"
	if len(fields) > 0 {
		qs += " ("
		for i := 0; i < len(fields); i++ {
			if i > 0 {
				qs += ", "
			}
			qs += "`" + fields[i] + "`"
		}
		qs += ")"
	}
	if deferred {
		qs += " WITH {\"defer_build\": true}"
	}

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		if strings.Contains(err.Error(), "already exist") {
			if ignoreIfExists {
				return nil
			}
			return ErrIndexAlreadyExists
		}
		return err
	}

	return rows.Close()
}

// CreateIndex creates an index over the specified fields.
func (bm *BucketManager) CreateIndex(indexName string, fields []string, ignoreIfExists, deferred bool) error {
	if indexName == "" {
		return ErrIndexInvalidName
	}
	if len(fields) <= 0 {
		return ErrIndexNoFields
	}
	return bm.createIndex(indexName, fields, ignoreIfExists, deferred)
}

// CreatePrimaryIndex creates a primary index.  An empty customName uses the default naming.
func (bm *BucketManager) CreatePrimaryIndex(customName string, ignoreIfExists, deferred bool) error {
	return bm.createIndex(customName, nil, ignoreIfExists, deferred)
}

func (bm *BucketManager) dropIndex(indexName string, ignoreIfNotExists bool) error {
	var qs string

	if indexName == "" {
		qs += "DROP PRIMARY INDEX `" + bm.bucket.name + "`"
	} else {
		qs += "DROP INDEX `" + bm.bucket.name + "`.`" + indexName + "`"
	}

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		if strings.Contains(err.Error(), "not found") {
			if ignoreIfNotExists {
				return nil
			}
			return ErrIndexNotFound
		}
		return err
	}

	return rows.Close()
}

// DropIndex drops a specific index by name.
func (bm *BucketManager) DropIndex(indexName string, ignoreIfNotExists bool) error {
	if indexName == "" {
		return ErrIndexInvalidName
	}
	return bm.dropIndex(indexName, ignoreIfNotExists)
}

// DropPrimaryIndex drops the primary index.  Pass an empty customName for unnamed primary indexes.
func (bm *BucketManager) DropPrimaryIndex(customName string, ignoreIfNotExists bool) error {
	return bm.dropIndex(customName, ignoreIfNotExists)
}

// GetIndexes returns a list of all currently registered indexes.
func (bm *BucketManager) GetIndexes() ([]IndexInfo, error) {
	q := NewN1qlQuery("SELECT `indexes`.* FROM system:indexes")
	rows, err := bm.bucket.ExecuteN1qlQuery(q, nil)
	if err != nil {
		return nil, err
	}

	var indexes []IndexInfo
	var index IndexInfo
	for rows.Next(&index) {
		indexes = append(indexes, index)
		index = IndexInfo{}
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}

	return indexes, nil
}

// BuildDeferredIndexes builds all indexes which are currently in deferred state.
func (bm *BucketManager) BuildDeferredIndexes() ([]string, error) {
	indexList, err := bm.GetIndexes()
	if err != nil {
		return nil, err
	}

	var deferredList []string
	for i := 0; i < len(indexList); i++ {
		var index = indexList[i]
		if index.State == "deferred" || index.State == "pending" {
			deferredList = append(deferredList, index.Name)
		}
	}

	if len(deferredList) == 0 {
		// Don't try to build an empty index list
		return nil, nil
	}

	var qs string
	qs += "BUILD INDEX ON `" + bm.bucket.name + "`("
	for i := 0; i < len(deferredList); i++ {
		if i > 0 {
			qs += ", "
		}
		qs += "`" + deferredList[i] + "`"
	}
	qs += ")"

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		return nil, err
	}

	if err := rows.Close(); err != nil {
		return nil, err
	}

	return deferredList, nil
}

func checkIndexesActive(indexes []IndexInfo, checkList []string) (bool, error) {
	var checkIndexes []IndexInfo
	for i := 0; i < len(checkList); i++ {
		indexName := checkList[i]

		for j := 0; j < len(indexes); j++ {
			if indexes[j].Name == indexName {
				checkIndexes = append(checkIndexes, indexes[j])
				break
			}
		}
	}

	if len(checkIndexes) != len(checkList) {
		return false, ErrIndexNotFound
	}

	for i := 0; i < len(checkIndexes); i++ {
		if checkIndexes[i].State != "online" {
			return false, nil
		}
	}
	return true, nil
}

// WatchIndexes waits for a set of indexes to come online
func (bm *BucketManager) WatchIndexes(watchList []string, watchPrimary bool, timeout time.Duration) error {
	if watchPrimary {
		watchList = append(watchList, "#primary")
	}

	curInterval := 50 * time.Millisecond
	timeoutTime := time.Now().Add(timeout)
	for {
		indexes, err := bm.GetIndexes()
		if err != nil {
			return err
		}

		allOnline, err := checkIndexesActive(indexes, watchList)
		if err != nil {
			return err
		}

		if allOnline {
			break
		}

		curInterval += 500 * time.Millisecond
		if curInterval > 1000 {
			curInterval = 1000
		}

		if time.Now().Add(curInterval).After(timeoutTime) {
			return ErrTimeout
		}

		// Wait till our next poll interval
		time.Sleep(curInterval)
	}

	return nil
}
<commit_msg>Fixing BucketManager DropPrimaryIndex call<commit_after>package gocb

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

// View represents a Couchbase view within a design document.
type View struct {
	Map    string `json:"map,omitempty"`
	Reduce string `json:"reduce,omitempty"`
}

func (v View) hasReduce() bool {
	return v.Reduce != ""
}

// DesignDocument represents a Couchbase design document containing multiple views.
type DesignDocument struct {
	Name         string          `json:"-"`
	Views        map[string]View `json:"views,omitempty"`
	SpatialViews map[string]View `json:"spatial,omitempty"`
}

// IndexInfo represents a Couchbase GSI index.
type IndexInfo struct {
	Name      string    `json:"name"`
	IsPrimary bool      `json:"is_primary"`
	Type      IndexType `json:"using"`
	State     string    `json:"state"`
	Keyspace  string    `json:"keyspace_id"`
	Namespace string    `json:"namespace_id"`
	IndexKey  []string  `json:"index_key"`
}

// BucketManager provides methods for performing bucket management operations.
// See ClusterManager for methods that allow creating and removing buckets themselves.
type BucketManager struct {
	bucket   *Bucket
	username string
	password string
}

func (bm *BucketManager) capiRequest(method, uri, contentType string, body io.Reader) (*http.Response, error) {
	if contentType == "" && body != nil {
		panic("Content-type must be specified for non-null body.")
	}

	viewEp, err := bm.bucket.getViewEp()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, viewEp+uri, body)
	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(bm.username, bm.password)
	return bm.bucket.client.HttpClient().Do(req)
}

func (bm *BucketManager) mgmtRequest(method, uri, contentType string, body io.Reader) (*http.Response, error) {
	if contentType == "" && body != nil {
		panic("Content-type must be specified for non-null body.")
	}

	mgmtEp, err := bm.bucket.getMgmtEp()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, mgmtEp+uri, body)
	if err != nil {
		return nil, err
	}

	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}
	if bm.username != "" || bm.password != "" {
		req.SetBasicAuth(bm.username, bm.password)
	}

	return bm.bucket.client.HttpClient().Do(req)
}

// Flush will delete all the of the data from a bucket.
// Keep in mind that you must have flushing enabled in the buckets configuration.
func (bm *BucketManager) Flush() error {
	reqUri := fmt.Sprintf("/pools/default/buckets/%s/controller/doFlush", bm.bucket.name)
	resp, err := bm.mgmtRequest("POST", reqUri, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}
	return nil
}

// GetDesignDocument retrieves a single design document for the given bucket..
func (bm *BucketManager) GetDesignDocument(name string) (*DesignDocument, error) {
	reqUri := fmt.Sprintf("/_design/%s", name)

	resp, err := bm.capiRequest("GET", reqUri, "", nil)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return nil, clientError{string(data)}
	}

	ddocObj := DesignDocument{}
	jsonDec := json.NewDecoder(resp.Body)
	err = jsonDec.Decode(&ddocObj)
	if err != nil {
		return nil, err
	}

	ddocObj.Name = name
	return &ddocObj, nil
}

// GetDesignDocuments will retrieve all design documents for the given bucket.
func (bm *BucketManager) GetDesignDocuments() ([]*DesignDocument, error) {
	reqUri := fmt.Sprintf("/pools/default/buckets/%s/ddocs", bm.bucket.name)

	resp, err := bm.mgmtRequest("GET", reqUri, "", nil)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return nil, clientError{string(data)}
	}

	var ddocsObj struct {
		Rows []struct {
			Doc struct {
				Meta struct {
					Id string
				}
				Json DesignDocument
			}
		}
	}
	jsonDec := json.NewDecoder(resp.Body)
	err = jsonDec.Decode(&ddocsObj)
	if err != nil {
		return nil, err
	}

	var ddocs []*DesignDocument
	for index, ddocData := range ddocsObj.Rows {
		ddoc := &ddocsObj.Rows[index].Doc.Json
		ddoc.Name = ddocData.Doc.Meta.Id[8:]
		ddocs = append(ddocs, ddoc)
	}

	return ddocs, nil
}

// InsertDesignDocument inserts a design document to the given bucket.
func (bm *BucketManager) InsertDesignDocument(ddoc *DesignDocument) error {
	oldDdoc, err := bm.GetDesignDocument(ddoc.Name)
	if oldDdoc != nil || err == nil {
		return clientError{"Design document already exists"}
	}
	return bm.UpsertDesignDocument(ddoc)
}

// UpsertDesignDocument will insert a design document to the given bucket, or update
// an existing design document with the same name.
func (bm *BucketManager) UpsertDesignDocument(ddoc *DesignDocument) error {
	reqUri := fmt.Sprintf("/_design/%s", ddoc.Name)

	data, err := json.Marshal(&ddoc)
	if err != nil {
		return err
	}

	resp, err := bm.capiRequest("PUT", reqUri, "application/json", bytes.NewReader(data))
	if err != nil {
		return err
	}

	if resp.StatusCode != 201 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}

	return nil
}

// RemoveDesignDocument will remove a design document from the given bucket.
func (bm *BucketManager) RemoveDesignDocument(name string) error {
	reqUri := fmt.Sprintf("/_design/%s", name)

	resp, err := bm.capiRequest("DELETE", reqUri, "", nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = resp.Body.Close()
		if err != nil {
			logDebugf("Failed to close socket (%s)", err)
		}
		return clientError{string(data)}
	}

	return nil
}

func (bm *BucketManager) createIndex(indexName string, fields []string, ignoreIfExists, deferred bool) error {
	var qs string

	if len(fields) == 0 {
		qs += "CREATE PRIMARY INDEX"
	} else {
		qs += "CREATE INDEX"
	}
	if indexName != "" {
		qs += " `" + indexName + "`"
	}
	qs += " ON `" + bm.bucket.name + "`"
	if len(fields) > 0 {
		qs += " ("
		for i := 0; i < len(fields); i++ {
			if i > 0 {
				qs += ", "
			}
			qs += "`" + fields[i] + "`"
		}
		qs += ")"
	}
	if deferred {
		qs += " WITH {\"defer_build\": true}"
	}

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		if strings.Contains(err.Error(), "already exist") {
			if ignoreIfExists {
				return nil
			}
			return ErrIndexAlreadyExists
		}
		return err
	}

	return rows.Close()
}

// CreateIndex creates an index over the specified fields.
func (bm *BucketManager) CreateIndex(indexName string, fields []string, ignoreIfExists, deferred bool) error {
	if indexName == "" {
		return ErrIndexInvalidName
	}
	if len(fields) <= 0 {
		return ErrIndexNoFields
	}
	return bm.createIndex(indexName, fields, ignoreIfExists, deferred)
}

// CreatePrimaryIndex creates a primary index.  An empty customName uses the default naming.
func (bm *BucketManager) CreatePrimaryIndex(customName string, ignoreIfExists, deferred bool) error {
	return bm.createIndex(customName, nil, ignoreIfExists, deferred)
}

func (bm *BucketManager) dropIndex(indexName string, ignoreIfNotExists bool) error {
	var qs string

	if indexName == "" {
		qs += "DROP PRIMARY INDEX ON `" + bm.bucket.name + "`"
	} else {
		qs += "DROP INDEX `" + bm.bucket.name + "`.`" + indexName + "`"
	}

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		if strings.Contains(err.Error(), "not found") {
			if ignoreIfNotExists {
				return nil
			}
			return ErrIndexNotFound
		}
		return err
	}

	return rows.Close()
}

// DropIndex drops a specific index by name.
func (bm *BucketManager) DropIndex(indexName string, ignoreIfNotExists bool) error {
	if indexName == "" {
		return ErrIndexInvalidName
	}
	return bm.dropIndex(indexName, ignoreIfNotExists)
}

// DropPrimaryIndex drops the primary index.  Pass an empty customName for unnamed primary indexes.
func (bm *BucketManager) DropPrimaryIndex(customName string, ignoreIfNotExists bool) error {
	return bm.dropIndex(customName, ignoreIfNotExists)
}

// GetIndexes returns a list of all currently registered indexes.
func (bm *BucketManager) GetIndexes() ([]IndexInfo, error) {
	q := NewN1qlQuery("SELECT `indexes`.* FROM system:indexes")
	rows, err := bm.bucket.ExecuteN1qlQuery(q, nil)
	if err != nil {
		return nil, err
	}

	var indexes []IndexInfo
	var index IndexInfo
	for rows.Next(&index) {
		indexes = append(indexes, index)
		index = IndexInfo{}
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}

	return indexes, nil
}

// BuildDeferredIndexes builds all indexes which are currently in deferred state.
func (bm *BucketManager) BuildDeferredIndexes() ([]string, error) {
	indexList, err := bm.GetIndexes()
	if err != nil {
		return nil, err
	}

	var deferredList []string
	for i := 0; i < len(indexList); i++ {
		var index = indexList[i]
		if index.State == "deferred" || index.State == "pending" {
			deferredList = append(deferredList, index.Name)
		}
	}

	if len(deferredList) == 0 {
		// Don't try to build an empty index list
		return nil, nil
	}

	var qs string
	qs += "BUILD INDEX ON `" + bm.bucket.name + "`("
	for i := 0; i < len(deferredList); i++ {
		if i > 0 {
			qs += ", "
		}
		qs += "`" + deferredList[i] + "`"
	}
	qs += ")"

	rows, err := bm.bucket.ExecuteN1qlQuery(NewN1qlQuery(qs), nil)
	if err != nil {
		return nil, err
	}

	if err := rows.Close(); err != nil {
		return nil, err
	}

	return deferredList, nil
}

func checkIndexesActive(indexes []IndexInfo, checkList []string) (bool, error) {
	var checkIndexes []IndexInfo
	for i := 0; i < len(checkList); i++ {
		indexName := checkList[i]

		for j := 0; j < len(indexes); j++ {
			if indexes[j].Name == indexName {
				checkIndexes = append(checkIndexes, indexes[j])
				break
			}
		}
	}

	if len(checkIndexes) != len(checkList) {
		return false, ErrIndexNotFound
	}

	for i := 0; i < len(checkIndexes); i++ {
		if checkIndexes[i].State != "online" {
			return false, nil
		}
	}
	return true, nil
}

// WatchIndexes waits for a set of indexes to come online
func (bm *BucketManager) WatchIndexes(watchList []string, watchPrimary bool, timeout time.Duration) error {
	if watchPrimary {
		watchList = append(watchList, "#primary")
	}

	curInterval := 50 * time.Millisecond
	timeoutTime := time.Now().Add(timeout)
	for {
		indexes, err := bm.GetIndexes()
		if err != nil {
			return err
		}

		allOnline, err := checkIndexesActive(indexes, watchList)
		if err != nil {
			return err
		}

		if allOnline {
			break
		}

		curInterval += 500 * time.Millisecond
		if curInterval > 1000 {
			curInterval = 1000
		}

		if time.Now().Add(curInterval).After(timeoutTime) {
			return ErrTimeout
		}

		// Wait till our next poll interval
		time.Sleep(curInterval)
	}

	return nil
}
<|endoftext|>