<commit_before>package proxmox

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"regexp"
	"strconv"
	"time"
)

type ConfigQemu struct {
	Name         string  `json:"name"`
	Description  string  `json:"desc"`
	Memory       int     `json:"memory"`
	DiskSize     float64 `json:"diskGB"`
	Storage      string  `json:"storage"`
	QemuOs       string  `json:"os"`
	QemuCores    int     `json:"cores"`
	QemuSockets  int     `json:"sockets"`
	QemuIso      string  `json:"iso"`
	QemuNicModel string  `json:"nic"`
	QemuBrige    string  `json:"bridge"`
	QemuVlanTag  int     `json:"vlan"`
}

func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + string(config.QemuVlanTag)
	}
	params := map[string]string{
		"vmid":        strconv.Itoa(vmr.vmId),
		"name":        config.Name,
		"ide2":        config.QemuIso + ",media=cdrom",
		"ostype":      config.QemuOs,
		"virtio0":     config.Storage + ":" + strconv.FormatFloat(config.DiskSize, 'f', -1, 64),
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"cpu":         "host",
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}

	_, err = client.CreateQemuVm(vmr.node, params)
	return
}

/*

CloneVm
Example: Request

nodes/proxmox1-xx/qemu/1012/clone

newid:145
name:tf-clone1
target:proxmox1-xx
full:1
storage:xxx

*/
func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	params := map[string]string{
		"newid":   strconv.Itoa(vmr.vmId),
		"target":  vmr.node,
		"name":    config.Name,
		"storage": config.Storage,
		"full":    "1",
	}
	_, err = client.CloneQemuVm(sourceVmr, params)
	if err != nil {
		return
	}
	configParams := map[string]string{
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"memory":      strconv.Itoa(config.Memory),
		"description": config.Description,
	}
	_, err = client.SetVmConfig(vmr, configParams)
	return
}

func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
	config = &ConfigQemu{QemuVlanTag: -1}
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Println(config)
	return
}

var rxStorage = regexp.MustCompile("(.*?):.*?,size=(\\d+)G")
var rxIso = regexp.MustCompile("(.*?),media")
var rxNetwork = regexp.MustCompile("(.*?)=.*?,bridge=([^,]+)(?:,tag=)?(.*)")

func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
	vmConfig, err := client.GetVmConfig(vmr)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	// vmConfig Sample: map[ cpu:host
	// net0:virtio=62:DF:XX:XX:XX:XX,bridge=vmbr0
	// ide2:local:iso/xxx-xx.iso,media=cdrom memory:2048
	// smbios1:uuid=8b3bf833-aad8-4545-xxx-xxxxxxx digest:aa6ce5xxxxx1b9ce33e4aaeff564d4 sockets:1
	// name:terraform-ubuntu1404-template bootdisk:virtio0
	// virtio0:ProxmoxxxxISCSI:vm-1014-disk-2,size=4G
	// description:Base image
	// cores:2 ostype:l26 ]
	config = &ConfigQemu{
		Name:        vmConfig["name"].(string),
		Description: vmConfig["description"].(string),
		QemuOs:      vmConfig["ostype"].(string),
		Memory:      int(vmConfig["memory"].(float64)),
		QemuCores:   int(vmConfig["cores"].(float64)),
		QemuSockets: int(vmConfig["sockets"].(float64)),
		QemuVlanTag: -1,
	}

	storageMatch := rxStorage.FindStringSubmatch(vmConfig["virtio0"].(string))
	config.Storage = storageMatch[1]
	config.DiskSize, _ = strconv.ParseFloat(storageMatch[2], 64)

	isoMatch := rxIso.FindStringSubmatch(vmConfig["ide2"].(string))
	config.QemuIso = isoMatch[1]

	netMatch := rxNetwork.FindStringSubmatch(vmConfig["net0"].(string))
	config.QemuNicModel = netMatch[1]
	config.QemuBrige = netMatch[2]
	if netMatch[3] != "" {
		config.QemuVlanTag, _ = strconv.Atoi(netMatch[3])
	}

	return
}

// Useful waiting for ISO install to complete
func WaitForShutdown(vmr *VmRef, client *Client) (err error) {
	for ii := 0; ii < 100; ii++ {
		vmState, err := client.GetVmState(vmr)
		if err != nil {
			log.Print("Wait error:")
			log.Println(err)
		} else if vmState["status"] == "stopped" {
			return nil
		}
		time.Sleep(5 * time.Second)
	}
	return errors.New("Not shutdown within wait time")
}

// This is because proxmox create/config API won't let us make usernet devices
func SshForwardUsernet(vmr *VmRef, client *Client) (sshPort string, err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return "", err
	}
	if vmState["status"] == "stopped" {
		return "", errors.New("VM must be running first")
	}
	sshPort = strconv.Itoa(vmr.VmId() + 22000)
	_, err = client.MonitorCmd(vmr, "netdev_add user,id=net1,hostfwd=tcp::"+sshPort+"-:22")
	if err != nil {
		return "", err
	}
	_, err = client.MonitorCmd(vmr, "device_add virtio-net-pci,id=net1,netdev=net1,addr=0x13")
	if err != nil {
		return "", err
	}
	return
}

func MaxVmId(client *Client) (max int, err error) {
	resp, err := client.GetVmList()
	vms := resp["data"].([]interface{})
	max = 0
	for vmii := range vms {
		vm := vms[vmii].(map[string]interface{})
		vmid := int(vm["vmid"].(float64))
		if vmid > max {
			max = vmid
		}
	}
	return
}
<commit_msg>trim description<commit_after>package proxmox

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type ConfigQemu struct {
	Name         string  `json:"name"`
	Description  string  `json:"desc"`
	Memory       int     `json:"memory"`
	DiskSize     float64 `json:"diskGB"`
	Storage      string  `json:"storage"`
	QemuOs       string  `json:"os"`
	QemuCores    int     `json:"cores"`
	QemuSockets  int     `json:"sockets"`
	QemuIso      string  `json:"iso"`
	QemuNicModel string  `json:"nic"`
	QemuBrige    string  `json:"bridge"`
	QemuVlanTag  int     `json:"vlan"`
}

func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + string(config.QemuVlanTag)
	}
	params := map[string]string{
		"vmid":        strconv.Itoa(vmr.vmId),
		"name":        config.Name,
		"ide2":        config.QemuIso + ",media=cdrom",
		"ostype":      config.QemuOs,
		"virtio0":     config.Storage + ":" + strconv.FormatFloat(config.DiskSize, 'f', -1, 64),
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"cpu":         "host",
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}

	_, err = client.CreateQemuVm(vmr.node, params)
	return
}

/*

CloneVm
Example: Request

nodes/proxmox1-xx/qemu/1012/clone

newid:145
name:tf-clone1
target:proxmox1-xx
full:1
storage:xxx

*/
func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	params := map[string]string{
		"newid":   strconv.Itoa(vmr.vmId),
		"target":  vmr.node,
		"name":    config.Name,
		"storage": config.Storage,
		"full":    "1",
	}
	_, err = client.CloneQemuVm(sourceVmr, params)
	if err != nil {
		return
	}
	configParams := map[string]string{
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"memory":      strconv.Itoa(config.Memory),
		"description": config.Description,
	}
	_, err = client.SetVmConfig(vmr, configParams)
	return
}

func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
	config = &ConfigQemu{QemuVlanTag: -1}
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Println(config)
	return
}

var rxStorage = regexp.MustCompile("(.*?):.*?,size=(\\d+)G")
var rxIso = regexp.MustCompile("(.*?),media")
var rxNetwork = regexp.MustCompile("(.*?)=.*?,bridge=([^,]+)(?:,tag=)?(.*)")

func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
	vmConfig, err := client.GetVmConfig(vmr)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	// vmConfig Sample: map[ cpu:host
	// net0:virtio=62:DF:XX:XX:XX:XX,bridge=vmbr0
	// ide2:local:iso/xxx-xx.iso,media=cdrom memory:2048
	// smbios1:uuid=8b3bf833-aad8-4545-xxx-xxxxxxx digest:aa6ce5xxxxx1b9ce33e4aaeff564d4 sockets:1
	// name:terraform-ubuntu1404-template bootdisk:virtio0
	// virtio0:ProxmoxxxxISCSI:vm-1014-disk-2,size=4G
	// description:Base image
	// cores:2 ostype:l26 ]
	config = &ConfigQemu{
		Name:        vmConfig["name"].(string),
		Description: strings.TrimSpace(vmConfig["description"].(string)),
		QemuOs:      vmConfig["ostype"].(string),
		Memory:      int(vmConfig["memory"].(float64)),
		QemuCores:   int(vmConfig["cores"].(float64)),
		QemuSockets: int(vmConfig["sockets"].(float64)),
		QemuVlanTag: -1,
	}

	storageMatch := rxStorage.FindStringSubmatch(vmConfig["virtio0"].(string))
	config.Storage = storageMatch[1]
	config.DiskSize, _ = strconv.ParseFloat(storageMatch[2], 64)

	isoMatch := rxIso.FindStringSubmatch(vmConfig["ide2"].(string))
	config.QemuIso = isoMatch[1]

	netMatch := rxNetwork.FindStringSubmatch(vmConfig["net0"].(string))
	config.QemuNicModel = netMatch[1]
	config.QemuBrige = netMatch[2]
	if netMatch[3] != "" {
		config.QemuVlanTag, _ = strconv.Atoi(netMatch[3])
	}

	return
}

// Useful waiting for ISO install to complete
func WaitForShutdown(vmr *VmRef, client *Client) (err error) {
	for ii := 0; ii < 100; ii++ {
		vmState, err := client.GetVmState(vmr)
		if err != nil {
			log.Print("Wait error:")
			log.Println(err)
		} else if vmState["status"] == "stopped" {
			return nil
		}
		time.Sleep(5 * time.Second)
	}
	return errors.New("Not shutdown within wait time")
}

// This is because proxmox create/config API won't let us make usernet devices
func SshForwardUsernet(vmr *VmRef, client *Client) (sshPort string, err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return "", err
	}
	if vmState["status"] == "stopped" {
		return "", errors.New("VM must be running first")
	}
	sshPort = strconv.Itoa(vmr.VmId() + 22000)
	_, err = client.MonitorCmd(vmr, "netdev_add user,id=net1,hostfwd=tcp::"+sshPort+"-:22")
	if err != nil {
		return "", err
	}
	_, err = client.MonitorCmd(vmr, "device_add virtio-net-pci,id=net1,netdev=net1,addr=0x13")
	if err != nil {
		return "", err
	}
	return
}

func MaxVmId(client *Client) (max int, err error) {
	resp, err := client.GetVmList()
	vms := resp["data"].([]interface{})
	max = 0
	for vmii := range vms {
		vm := vms[vmii].(map[string]interface{})
		vmid := int(vm["vmid"].(float64))
		if vmid > max {
			max = vmid
		}
	}
	return
}
<|endoftext|>


<commit_before>//package phonecountry provide information about a phone number
package phonecountry

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/maknahar/phonecountry/internal/content"
)

var phoneMap map[string]string
var reversePhoneMap map[string]string

func init() {

	phoneMap = make(map[string]string)

	// Decode JSON into our map
	err := json.Unmarshal([]byte(content.PhoneCode), &phoneMap)
	if err != nil {
		println(err)
	}

	reversePhoneMap = make(map[string]string)
	for k, v := range phoneMap {
		reversePhoneMap[strings.ToUpper(v)] = k
	}
}

//GetCountryNameFromPhone returns country common name for given phone number
//Example +919445454528 - India
func GetCountryNameFromPhone(phoneNumber string) (countryName string, err error) {
	//strip + from starting
	phoneNumber = strings.TrimSpace(phoneNumber)
	phoneNumber = strings.TrimLeft(phoneNumber, "+")

	//check minimum length
	if len(phoneNumber) < 7 {
		return "", errors.New("Minimum length of a number should be atleast 7 digit")
	}

	//handle special cases
	switch phoneNumber[0:1] {
	case "1":
		c, ok := phoneMap[phoneNumber[0:4]]
		if ok {
			return c, nil
		}
		return "", fmt.Errorf("No country found with country code %s", phoneNumber[0:4])

	case "3":
		if phoneNumber[0:2] == "39" && phoneNumber[0:5] == "39066" {
			return phoneMap["39066"], nil
		}

	case "4":
		//Guernsey
		if phoneNumber[0:6] == "441481" || phoneNumber[0:6] == "447839" ||
			phoneNumber[0:6] == "447911" {
			return phoneMap["39066"], nil
		}
		//Isle of Man
		if phoneNumber[0:6] == "441624" || phoneNumber[0:6] == "447524" ||
			phoneNumber[0:6] == "447624" || phoneNumber[0:6] == "447924" {
			return phoneMap["447924"], nil
		}
		//Jersey
		if phoneNumber[0:6] == "441534" || phoneNumber[0:6] == "447509" ||
			phoneNumber[0:6] == "447797" || phoneNumber[0:6] == "447937" ||
			phoneNumber[0:6] == "447700" || phoneNumber[0:6] == "447829" {
			return phoneMap["447829"], nil
		}
	case "6":
		fmt.Println("here")
		if phoneNumber[0:4] == "6721" {
			return phoneMap["6721"], nil
		}
		if phoneNumber[0:4] == "6723" {
			return phoneMap["6723"], nil
		}
		//Christmas Island
		if phoneNumber[0:7] == "6189162" {
			return phoneMap["6189162"], nil
		}
		//Christmas Island
		if phoneNumber[0:7] == "6189164" {
			return phoneMap["6189164"], nil
		}
	case "7":
		if phoneNumber[0:2] == "76" || phoneNumber[0:2] == "77" {
			return phoneMap[phoneNumber[0:2]], nil
		}
		return phoneMap["7"], nil
	}
	c, ok := phoneMap[phoneNumber[0:2]]
	if ok {
		return c, nil
	}
	c, ok = phoneMap[phoneNumber[0:3]]
	if ok {
		return c, nil
	}
	return "", fmt.Errorf("No country code found for %s", phoneNumber)
}

//GetCountryISO2Code returns country iso2 code of given number
//For Example +919445454528 => IN
func GetCountryISO2Code(phoneNumber string) (countryCode string, err error) {
	countryName, err := GetCountryNameFromPhone(phoneNumber)
	if err != nil {
		return "", err
	}

	countryCode = content.CountryInfo[countryName].Cca2
	if countryCode == "" {
		return "", errors.New(fmt.Sprintf("country name %s not found", countryName))
	}
	return
}
<commit_msg>removed print<commit_after>//package phonecountry provide information about a phone number
package phonecountry

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/maknahar/phonecountry/internal/content"
)

var phoneMap map[string]string

func init() {

	phoneMap = make(map[string]string)

	// Decode JSON into our map
	err := json.Unmarshal([]byte(content.PhoneCode), &phoneMap)
	if err != nil {
		println(err)
	}
}

//GetCountryNameFromPhone returns country common name for given phone number
//Example +919445454528 => India
func GetCountryNameFromPhone(phoneNumber string) (countryName string, err error) {
	//strip + from starting
	phoneNumber = strings.TrimSpace(phoneNumber)
	phoneNumber = strings.TrimLeft(phoneNumber, "+")

	//check minimum length
	if len(phoneNumber) < 7 {
		return "", errors.New("Minimum length of a number should be atleast 7 digit")
	}

	//handle special cases
	switch phoneNumber[0:1] {
	case "1":
		c, ok := phoneMap[phoneNumber[0:4]]
		if ok {
			return c, nil
		}
		return "", fmt.Errorf("No country found with country code %s", phoneNumber[0:4])

	case "3":
		if phoneNumber[0:2] == "39" && phoneNumber[0:5] == "39066" {
			return phoneMap["39066"], nil
		}

	case "4":
		//Guernsey
		if phoneNumber[0:6] == "441481" || phoneNumber[0:6] == "447839" ||
			phoneNumber[0:6] == "447911" {
			return phoneMap["39066"], nil
		}
		//Isle of Man
		if phoneNumber[0:6] == "441624" || phoneNumber[0:6] == "447524" ||
			phoneNumber[0:6] == "447624" || phoneNumber[0:6] == "447924" {
			return phoneMap["447924"], nil
		}
		//Jersey
		if phoneNumber[0:6] == "441534" || phoneNumber[0:6] == "447509" ||
			phoneNumber[0:6] == "447797" || phoneNumber[0:6] == "447937" ||
			phoneNumber[0:6] == "447700" || phoneNumber[0:6] == "447829" {
			return phoneMap["447829"], nil
		}
	case "6":
		if phoneNumber[0:4] == "6721" {
			return phoneMap["6721"], nil
		}
		if phoneNumber[0:4] == "6723" {
			return phoneMap["6723"], nil
		}
		//Christmas Island
		if phoneNumber[0:7] == "6189162" {
			return phoneMap["6189162"], nil
		}
		//Christmas Island
		if phoneNumber[0:7] == "6189164" {
			return phoneMap["6189164"], nil
		}
	case "7":
		if phoneNumber[0:2] == "76" || phoneNumber[0:2] == "77" {
			return phoneMap[phoneNumber[0:2]], nil
		}
		return phoneMap["7"], nil
	}
	c, ok := phoneMap[phoneNumber[0:2]]
	if ok {
		return c, nil
	}
	c, ok = phoneMap[phoneNumber[0:3]]
	if ok {
		return c, nil
	}
	return "", fmt.Errorf("No country code found for %s", phoneNumber)
}

//GetCountryISO2Code returns country iso2 code of given number
//For Example +919445454528 => IN
func GetCountryISO2Code(phoneNumber string) (countryCode string, err error) {
	countryName, err := GetCountryNameFromPhone(phoneNumber)
	if err != nil {
		return "", err
	}

	countryCode = content.CountryInfo[countryName].Cca2
	if countryCode == "" {
		return "", errors.New(fmt.Sprintf("country name %s not found", countryName))
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"time"
)

const (
	redisConnectionTimeout = 200 * time.Millisecond
	redisReadWriteTimeout  = 300 * time.Second
)

var (
	errUnreachable = errors.New("endpoint unreachable")
)

type redisobj struct {
	pool *redis.Pool
}

func NewRedis() *redisobj {
	r := &redisobj{}

	r.pool = &redis.Pool{
		MaxIdle:     10,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			return r.connect()
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

	return r
}

func (r *redisobj) Close() {
	r.pool.Close()
}

func (r *redisobj) connect() (redis.Conn, error) {
	sentinels := GetConfig().RedisSentinels

	if len(sentinels) > 0 {

		if len(GetConfig().RedisSentinelMasterName) == 0 {
			log.Error("Config: RedisSentinelMasterName cannot be empty!")
			goto single
		}

		for _, s := range sentinels {
			log.Debug("Connecting to redis sentinel %s", s.Host)
			var master []string
			var masterhost string
			var cm redis.Conn

			c, err := r.connectTo(s.Host)
			if err != nil {
				log.Error("Sentinel: %s", err.Error())
				continue
			}
			//AUTH?
			role, err := r.askRole(c)
			if err != nil {
				log.Error("Sentinel: %s", err.Error())
				goto closeSentinel
			}
			if role != "sentinel" {
				log.Error("Sentinel: %s is not a sentinel but a %s", s.Host, role)
				goto closeSentinel
			}

			master, err = redis.Strings(c.Do("SENTINEL", "get-master-addr-by-name", GetConfig().RedisSentinelMasterName))
			if err == redis.ErrNil {
				log.Error("Sentinel: %s doesn't know the master-name %s", s.Host, GetConfig().RedisSentinelMasterName)
				goto closeSentinel
			} else if err != nil {
				log.Error("Sentinel: %s", err.Error())
				goto closeSentinel
			}

			masterhost = fmt.Sprintf("%s:%s", master[0], master[1])

			cm, err = r.connectTo(masterhost)
			if err != nil {
				log.Error("Redis master: %s", err.Error())
				goto closeSentinel
			}

			if r.auth(cm) != nil {
				log.Error("Redis master: auth failed")
				goto closeMaster
			}

			role, err = r.askRole(cm)
			if err != nil {
				log.Error("Redis master: %s", err.Error())
				goto closeMaster
			}
			if role != "master" {
				log.Error("Redis master: %s is not a master but a %s", masterhost, role)
				goto closeMaster
			}

			// Close the connection to the sentinel
			c.Close()

			log.Debug("Connected to redis master %s", masterhost)
			return cm, nil

		closeMaster:
			cm.Close()

		closeSentinel:
			c.Close()
		}
	}

single:

	if len(GetConfig().RedisAddress) == 0 {
		if len(sentinels) == 0 {
			log.Error("No redis master available")
		}
		return nil, errUnreachable
	}

	log.Warning("No redis master available, trying using the configured RedisAddress as fallback")

	c, err := r.connectTo(GetConfig().RedisAddress)
	if err != nil {
		return nil, err
	}
	if r.auth(c); err != nil {
		c.Close()
		return nil, err
	}
	role, err := r.askRole(c)
	if err != nil {
		log.Error("Redis master: %s", err.Error())
		return nil, errUnreachable
	}
	if role != "master" {
		log.Error("Redis master: %s is not a master but a %s", GetConfig().RedisAddress, role)
		return nil, errUnreachable
	}
	log.Debug("Connected to redis master %s", GetConfig().RedisAddress)
	return c, err

}

func (r *redisobj) connectTo(address string) (redis.Conn, error) {
	return redis.DialTimeout("tcp", address, redisConnectionTimeout, redisReadWriteTimeout, redisReadWriteTimeout)
}

func (r *redisobj) askRole(c redis.Conn) (string, error) {
	roleReply, err := redis.Values(c.Do("ROLE"))
	role, err := redis.String(roleReply[0], err)
	return role, err
}

func (r *redisobj) auth(c redis.Conn) (err error) {
	if GetConfig().RedisPassword != "" {
		_, err = c.Do("AUTH", GetConfig().RedisPassword)
	}
	return
}
<commit_msg>redis: don't spam the logs with connection errors<commit_after>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"time"
)

const (
	redisConnectionTimeout = 200 * time.Millisecond
	redisReadWriteTimeout  = 300 * time.Second
)

var (
	errUnreachable = errors.New("endpoint unreachable")
)

type redisobj struct {
	pool    *redis.Pool
	failure bool
}

func NewRedis() *redisobj {
	r := &redisobj{}

	r.pool = &redis.Pool{
		MaxIdle:     10,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			return r.connect()
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

	return r
}

func (r *redisobj) Close() {
	r.pool.Close()
}

func (r *redisobj) connect() (redis.Conn, error) {
	sentinels := GetConfig().RedisSentinels

	if len(sentinels) > 0 {

		if len(GetConfig().RedisSentinelMasterName) == 0 {
			r.logError("Config: RedisSentinelMasterName cannot be empty!")
			goto single
		}

		for _, s := range sentinels {
			log.Debug("Connecting to redis sentinel %s", s.Host)
			var master []string
			var masterhost string
			var cm redis.Conn

			c, err := r.connectTo(s.Host)
			if err != nil {
				r.logError("Sentinel: %s", err.Error())
				continue
			}
			//AUTH?
			role, err := r.askRole(c)
			if err != nil {
				r.logError("Sentinel: %s", err.Error())
				goto closeSentinel
			}
			if role != "sentinel" {
				r.logError("Sentinel: %s is not a sentinel but a %s", s.Host, role)
				goto closeSentinel
			}

			master, err = redis.Strings(c.Do("SENTINEL", "get-master-addr-by-name", GetConfig().RedisSentinelMasterName))
			if err == redis.ErrNil {
				r.logError("Sentinel: %s doesn't know the master-name %s", s.Host, GetConfig().RedisSentinelMasterName)
				goto closeSentinel
			} else if err != nil {
				r.logError("Sentinel: %s", err.Error())
				goto closeSentinel
			}

			masterhost = fmt.Sprintf("%s:%s", master[0], master[1])

			cm, err = r.connectTo(masterhost)
			if err != nil {
				r.logError("Redis master: %s", err.Error())
				goto closeSentinel
			}

			if r.auth(cm) != nil {
				r.logError("Redis master: auth failed")
				goto closeMaster
			}

			role, err = r.askRole(cm)
			if err != nil {
				r.logError("Redis master: %s", err.Error())
				goto closeMaster
			}
			if role != "master" {
				r.logError("Redis master: %s is not a master but a %s", masterhost, role)
				goto closeMaster
			}

			// Close the connection to the sentinel
			c.Close()
			r.failure = false

			log.Debug("Connected to redis master %s", masterhost)
			return cm, nil

		closeMaster:
			cm.Close()

		closeSentinel:
			c.Close()
		}
	}

single:

	if len(GetConfig().RedisAddress) == 0 {
		if len(sentinels) == 0 {
			log.Error("No redis master available")
		}
		r.failure = true
		return nil, errUnreachable
	}

	if r.failure == false {
		log.Warning("No redis master available, trying using the configured RedisAddress as fallback")
	}

	c, err := r.connectTo(GetConfig().RedisAddress)
	if err != nil {
		return nil, err
	}
	if r.auth(c); err != nil {
		c.Close()
		return nil, err
	}
	role, err := r.askRole(c)
	if err != nil {
		r.logError("Redis master: %s", err.Error())
		r.failure = true
		return nil, errUnreachable
	}
	if role != "master" {
		r.logError("Redis master: %s is not a master but a %s", GetConfig().RedisAddress, role)
		r.failure = true
		return nil, errUnreachable
	}
	r.failure = false
	log.Debug("Connected to redis master %s", GetConfig().RedisAddress)
	return c, err

}

func (r *redisobj) connectTo(address string) (redis.Conn, error) {
	return redis.DialTimeout("tcp", address, redisConnectionTimeout, redisReadWriteTimeout, redisReadWriteTimeout)
}

func (r *redisobj) askRole(c redis.Conn) (string, error) {
	roleReply, err := redis.Values(c.Do("ROLE"))
	role, err := redis.String(roleReply[0], err)
	return role, err
}

func (r *redisobj) auth(c redis.Conn) (err error) {
	if GetConfig().RedisPassword != "" {
		_, err = c.Do("AUTH", GetConfig().RedisPassword)
	}
	return
}

func (r *redisobj) logError(format string, args ...interface{}) {
	if r.failure {
		log.Debug(format, args...)
	} else {
		log.Error(format, args...)
	}
}
<|endoftext|>


<commit_before>package jwt

import (
	"errors"

	"golang.org/x/net/context"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/go-kit/kit/endpoint"
)

type contextKey string

const (
	// JWTTokenContextKey holds the key used to store a JWT Token in the context
	JWTTokenContextKey contextKey = "JWTToken"
	// JWTClaimsContxtKey holds the key used to store the JWT Claims in the context
	JWTClaimsContextKey contextKey = "JWTClaims"
)

var (
	ErrTokenContextMissing     = errors.New("Token up for parsing was not passed through the context")
	ErrTokenInvalid            = errors.New("JWT Token was invalid")
	ErrTokenExpired            = errors.New("JWT Token is expired")
	ErrTokenMalformed          = errors.New("JWT Token is malformed")
	ErrTokenNotActive          = errors.New("Token is not valid yet")
	ErrUnexpectedSigningMethod = errors.New("Unexpected signing method")
)

type Claims map[string]interface{}

type KeySet map[string]struct {
	Method jwt.SigningMethod
	Key    []byte
}

// Create a new JWT token generating middleware, specifying signing method and the claims
// you would like it to contain. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
			token.Header["kid"] = kid

			// Sign and get the complete encoded token as a string using the secret
			tokenString, err := token.SignedString(key)
			if err != nil {
				return nil, err
			}
			ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString)

			return next(ctx, request)
		}
	}
}

// Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
// signing method. Adds the resulting claims to endpoint context or returns error on invalid
// token. Particularly useful for servers.
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			// tokenString is stored in the context from the transport handlers
			tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
			if !ok {
				return nil, ErrTokenContextMissing
			}

			// Parse takes the token string and a function for looking up the key. The latter is especially
			// useful if you use multiple keys for your application.  The standard is to use 'kid' in the
			// head of the token to identify which key to use, but the parsed token (head and claims) is provided
			// to the callback, providing flexibility.
			token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
				// Don't forget to validate the alg is what you expect:
				if token.Method != method {
					return nil, ErrUnexpectedSigningMethod
				}

				return keyFunc(token)
			})
			if err != nil {
				if e, ok := err.(*jwt.ValidationError); ok && e.Inner != nil {
					if e.Errors&jwt.ValidationErrorMalformed != 0 {
						// Token is malformed
						return nil, ErrTokenMalformed
					} else if e.Errors&jwt.ValidationErrorExpired != 0 {
						// Token is expired
						return nil, ErrTokenExpired
					} else if e.Errors&jwt.ValidationErrorNotValidYet != 0 {
						// Token is not active yet
						return nil, ErrTokenNotActive
					}

					return nil, e.Inner
				}

				return nil, err
			}

			if !token.Valid {
				return nil, ErrTokenInvalid
			}

			if claims, ok := token.Claims.(jwt.MapClaims); ok {
				ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
			}

			return next(ctx, request)
		}
	}
}
<commit_msg>Remove unused keyset struct<commit_after>package jwt

import (
	"errors"

	"golang.org/x/net/context"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/go-kit/kit/endpoint"
)

type contextKey string

const (
	// JWTTokenContextKey holds the key used to store a JWT Token in the context
	JWTTokenContextKey contextKey = "JWTToken"
	// JWTClaimsContxtKey holds the key used to store the JWT Claims in the context
	JWTClaimsContextKey contextKey = "JWTClaims"
)

var (
	ErrTokenContextMissing     = errors.New("Token up for parsing was not passed through the context")
	ErrTokenInvalid            = errors.New("JWT Token was invalid")
	ErrTokenExpired            = errors.New("JWT Token is expired")
	ErrTokenMalformed          = errors.New("JWT Token is malformed")
	ErrTokenNotActive          = errors.New("Token is not valid yet")
	ErrUnexpectedSigningMethod = errors.New("Unexpected signing method")
)

type Claims map[string]interface{}

// Create a new JWT token generating middleware, specifying signing method and the claims
// you would like it to contain. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
			token.Header["kid"] = kid

			// Sign and get the complete encoded token as a string using the secret
			tokenString, err := token.SignedString(key)
			if err != nil {
				return nil, err
			}
			ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString)

			return next(ctx, request)
		}
	}
}

// Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
// signing method. Adds the resulting claims to endpoint context or returns error on invalid
// token. Particularly useful for servers.
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			// tokenString is stored in the context from the transport handlers
			tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
			if !ok {
				return nil, ErrTokenContextMissing
			}

			// Parse takes the token string and a function for looking up the key. The latter is especially
			// useful if you use multiple keys for your application.  The standard is to use 'kid' in the
			// head of the token to identify which key to use, but the parsed token (head and claims) is provided
			// to the callback, providing flexibility.
			token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
				// Don't forget to validate the alg is what you expect:
				if token.Method != method {
					return nil, ErrUnexpectedSigningMethod
				}

				return keyFunc(token)
			})
			if err != nil {
				if e, ok := err.(*jwt.ValidationError); ok && e.Inner != nil {
					if e.Errors&jwt.ValidationErrorMalformed != 0 {
						// Token is malformed
						return nil, ErrTokenMalformed
					} else if e.Errors&jwt.ValidationErrorExpired != 0 {
						// Token is expired
						return nil, ErrTokenExpired
					} else if e.Errors&jwt.ValidationErrorNotValidYet != 0 {
						// Token is not active yet
						return nil, ErrTokenNotActive
					}

					return nil, e.Inner
				}

				return nil, err
			}

			if !token.Valid {
				return nil, ErrTokenInvalid
			}

			if claims, ok := token.Claims.(jwt.MapClaims); ok {
				ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
			}

			return next(ctx, request)
		}
	}
}
<|endoftext|>


<commit_before>// Package picoborgrev allows users to control pico borg reverse motor controllers over I2C using the gobot.io robotic framework.
// See: https://www.piborg.org/, https://gobot.io/
package picoborgrev

import (
	"fmt"
	"sync"

	multierror "github.com/hashicorp/go-multierror"
	"gobot.io/x/gobot"
	"gobot.io/x/gobot/drivers/i2c"
)

var _ gobot.Driver = (*Driver)(nil)

const (
	picoborgAddress     = 0x44 // Default address
	pwmMax              = 255  // Max pwm value
	i2cMaxLen           = 4    // Max len of I2C message
	i2cIDPicoborgRev    = 0x15 // i2c id of picoborg rev board
	commandSetLED       = 0x1  // Set the LED status
	commandGetLED       = 0x2  // Get the LED status
	commandSetAFWD      = 0x3  // Set motor 2 PWM rate in a forwards direction
	commandSetAREV      = 0x4  // Set motor 2 PWM rate in a reverse direction
	commandGetA         = 0x5  // Get motor 2 direction and PWM rate
	commandSetBFWD      = 0x6  // Set motor 1 PWM rate in a forwards direction
	commandSetBREV      = 0x7  // Set motor 1 PWM rate in a reverse direction
	commandGetB         = 0x8  // Get motor 1 direction and PWM rate
	commandAllOFF       = 0x9  // Switch everything off
	commandResetEPO     = 0x10 // Resets the EPO flag, use after EPO has been tripped and switch is now clear
	commandGetEPO       = 0x11 // Get the EPO latched flag
	commandSetEPOIgnore = 0x12 // Set the EPO ignored flag, allows the system to run without an EPO
	commandGetEPOIgnore = 0x13 // Get the EPO ignored flag
	commadGetDriveFault = 0x14 // Get the drive fault flag, indicates faults such as short-circuits and under voltage
	commandSetAllFWD    = 0x15 // Set all motors PWM rate in a forwards direction
	commandSetAllREV    = 0x16 // Set all motors PWM rate in a reverse direction
	commandSetFailsafe  = 0x17 // Set the failsafe flag, turns the motors off if communication is interrupted
	commandGetFailsafe  = 0x18 // Get the failsafe flag
	commandSetENCMode   = 0x19 // Set the board into encoder or speed mode
	commandGetENCMode   = 0x20 // Get the boards current mode, encoder or speed
	commandMoveAFWD     = 0x21 // Move motor 2 forward by n encoder ticks
	commandMoveAREV     = 0x22 // Move motor 2 reverse by n encoder ticks
	commandMoveBFWD     = 0x23 // Move motor 1 forward by n encoder ticks
	commandMoveBREV     = 0x24 // Move motor 1 reverse by n encoder ticks
	commandMoveAllFWD   = 0x25 // Move all motors forward by n encoder ticks
	commandMoveAllREV   = 0x26 // Move all motors reverse by n encoder ticks
	commandGetENCMoving = 0x27 // Get the status of encoders moving
	commandENCSpeed     = 0x28 // Set the maximum PWM rate in encoder mode
	commandGetENCSpeed  = 0x29 // Get the maximum PWM rate in encoder mode
	commandGetID        = 0x99 // Get the board identifier
	commandSetI2cAddr   = 0xAA // Set a new I2C address

	commandValueFWD = 0x1 // I2C value representing forward
	commandValueREV = 0x2 // I2C value representing reverse

	commandValueOn  = 0x1 // I2C value representing on
	commandValueOff = 0x0 // I2C value representing off

)

// RevDriver pico borg rev driver interace
type RevDriver interface {
	Name() string
	Connection() gobot.Connection
	Start() error
	Halt() error
	ResetEPO() error
	GetEPO() (bool, error)
	SetMotorA(float32) error
	SetMotorB(float32) error
	StopAllMotors() error
}

// Driver struct
type Driver struct {
	name       string
	connector  i2c.Connector
	connection i2c.Connection
	i2c.Config
	lock sync.Mutex
}

// NewDriver creates a new driver with specified name and i2c interface
//
// Params:
//		conn Connector - the Adaptor to use with this Driver
//
// Optional params:
//		i2c.WithBus(int):	bus to use with this driver
//		i2c.WithAddress(int):	address to use with this driver
//
//
func NewDriver(a i2c.Connector, options ...func(i2c.Config)) *Driver {
	d := &Driver{
		name:      gobot.DefaultName("PicoBorg"),
		connector: a,
		Config:    i2c.NewConfig(),
		lock:      sync.Mutex{},
	}

	for _, option := range options {
		option(d)
	}
	return d
}

// Name returns the name of the device
func (h *Driver) Name() string { return h.name }

// SetName of the device
func (h *Driver) SetName(n string) { h.name = n }

// Connection returns the connection
func (h *Driver) Connection() gobot.Connection { return h.connection.(gobot.Connection) }

// Start initialized the picoborgrev
func (h *Driver) Start() (err error) {
	bus := h.GetBusOrDefault(h.connector.GetDefaultBus())
	address := h.GetAddressOrDefault(picoborgAddress)
	h.lock.Lock()
	defer h.lock.Unlock()
	h.connection, err = h.connector.GetConnection(address, bus)
	if err != nil {
		return err
	}

	h.connection.Write([]byte{commandGetID})
	data := []byte{0, 0, 0, 0}

	read, err := h.connection.Read(data)
	if err != nil {
		return err
	}

	if read == i2cMaxLen {
		if data[1] != i2cIDPicoborgRev {
			err := fmt.Errorf("Found a device but it is not a PicoBorg Revers (ID %X instead of %X)", data[1], i2cIDPicoborgRev)
			return err
		}
	} else {
		err := fmt.Errorf("Device not found")
		return err
	}
	return
}

// Halt stops all motors
func (h *Driver) Halt() (errs error) {
	err := h.StopAllMotors()
	if err != nil {
		multierror.Append(errs, err)
	}
	return nil
}

// StopAllMotors will stop all motors
func (h *Driver) StopAllMotors() error {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandAllOFF))
	return err
}

// ResetEPO latch state, use to allow movement again after the EPO has been tripped
func (h *Driver) ResetEPO() error {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandResetEPO))
	return err
}

// GetEPO Reads the system EPO latch state.
func (h *Driver) GetEPO() (bool, error) {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandGetEPO))
	if err != nil {
		return false, err
	}

	data, err := h.connection.ReadByte()
	if err != nil {
		return false, err
	}

	if int(data) == commandValueOff {
		return false, nil
	}
	return true, nil
}

// SetMotorA generic set motor speed function
func (h *Driver) SetMotorA(power float32) error {
	h.lock.Lock()
	defer h.lock.Unlock()

	var command byte
	var pwm int
	if power < 0 {
		command = commandSetAREV
		pwm = -int(pwmMax * power)
	} else {
		command = commandSetAFWD
		pwm = int(pwmMax * power)
	}

	err := h.connection.WriteByteData(byte(command), byte(pwm))
	if err != nil {
		return err
	}
	return nil
}

// SetMotorB generic set motor speed function
func (h *Driver) SetMotorB(power float32) error {
	h.lock.Lock()
	defer h.lock.Unlock()
	var command byte
	var pwm int
	if power < 0 {
		command = commandSetBREV
		pwm = -int(pwmMax * power)
	} else {
		command = commandSetBFWD
		pwm = int(pwmMax * power)
	}

	err := h.connection.WriteByteData(byte(command), byte(pwm))
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Minor changes<commit_after>// Package picoborgrev allows users to control pico borg reverse motor controllers over I2C using the gobot.io robotic framework.
// See: https://www.piborg.org/, https://gobot.io/
package picoborgrev

import (
	"fmt"
	"sync"

	multierror "github.com/hashicorp/go-multierror"
	"gobot.io/x/gobot"
	"gobot.io/x/gobot/drivers/i2c"
)

var _ gobot.Driver = (*Driver)(nil)

const (
	picoborgAddress     = 0x44 // Default address
	pwmMax              = 255  // Max pwm value
	i2cMaxLen           = 4    // Max len of I2C message
	i2cIDPicoborgRev    = 0x15 // i2c id of picoborg rev board
	commandSetLED       = 0x1  // Set the LED status
	commandGetLED       = 0x2  // Get the LED status
	commandSetAFWD      = 0x3  // Set motor 2 PWM rate in a forwards direction
	commandSetAREV      = 0x4  // Set motor 2 PWM rate in a reverse direction
	commandGetA         = 0x5  // Get motor 2 direction and PWM rate
	commandSetBFWD      = 0x6  // Set motor 1 PWM rate in a forwards direction
	commandSetBREV      = 0x7  // Set motor 1 PWM rate in a reverse direction
	commandGetB         = 0x8  // Get motor 1 direction and PWM rate
	commandAllOFF       = 0x9  // Switch everything off
	commandResetEPO     = 0x10 // Resets the EPO flag, use after EPO has been tripped and switch is now clear
	commandGetEPO       = 0x11 // Get the EPO latched flag
	commandSetEPOIgnore = 0x12 // Set the EPO ignored flag, allows the system to run without an EPO
	commandGetEPOIgnore = 0x13 // Get the EPO ignored flag
	commadGetDriveFault = 0x14 // Get the drive fault flag, indicates faults such as short-circuits and under voltage
	commandSetAllFWD    = 0x15 // Set all motors PWM rate in a forwards direction
	commandSetAllREV    = 0x16 // Set all motors PWM rate in a reverse direction
	commandSetFailsafe  = 0x17 // Set the failsafe flag, turns the motors off if communication is interrupted
	commandGetFailsafe  = 0x18 // Get the failsafe flag
	commandSetENCMode   = 0x19 // Set the board into encoder or speed mode
	commandGetENCMode   = 0x20 // Get the boards current mode, encoder or speed
	commandMoveAFWD     = 0x21 // Move motor 2 forward by n encoder ticks
	commandMoveAREV     = 0x22 // Move motor 2 reverse by n encoder ticks
	commandMoveBFWD     = 0x23 // Move motor 1 forward by n encoder ticks
	commandMoveBREV     = 0x24 // Move motor 1 reverse by n encoder ticks
	commandMoveAllFWD   = 0x25 // Move all motors forward by n encoder ticks
	commandMoveAllREV   = 0x26 // Move all motors reverse by n encoder ticks
	commandGetENCMoving = 0x27 // Get the status of encoders moving
	commandENCSpeed     = 0x28 // Set the maximum PWM rate in encoder mode
	commandGetENCSpeed  = 0x29 // Get the maximum PWM rate in encoder mode
	commandGetID        = 0x99 // Get the board identifier
	commandSetI2cAddr   = 0xAA // Set a new I2C address

	commandValueFWD = 0x1 // I2C value representing forward
	commandValueREV = 0x2 // I2C value representing reverse

	commandValueOn  = 0x1 // I2C value representing on
	commandValueOff = 0x0 // I2C value representing off

)

// RevDriver pico borg rev driver interace
type RevDriver interface {
	Name() string
	Connection() gobot.Connection
	Start() error
	Halt() error
	ResetEPO() error
	GetEPO() (bool, error)
	SetMotorA(float32) error
	SetMotorB(float32) error
	StopAllMotors() error
}

// Driver struct
type Driver struct {
	name       string
	connector  i2c.Connector
	connection i2c.Connection
	i2c.Config
	lock sync.Mutex
}

// NewDriver creates a new driver with specified name and i2c interface
//
// Params:
//		conn Connector - the Adaptor to use with this Driver
//
// Optional params:
//		i2c.WithBus(int):	bus to use with this driver
//		i2c.WithAddress(int):	address to use with this driver
//
//
func NewDriver(a i2c.Connector, options ...func(i2c.Config)) *Driver {
	d := &Driver{
		name:      gobot.DefaultName("PicoBorg"),
		connector: a,
		Config:    i2c.NewConfig(),
		lock:      sync.Mutex{},
	}

	for _, option := range options {
		option(d)
	}
	return d
}

// Name returns the name of the device
func (h *Driver) Name() string { return h.name }

// SetName of the device
func (h *Driver) SetName(n string) { h.name = n }

// Connection returns the connection
func (h *Driver) Connection() gobot.Connection { return h.connection.(gobot.Connection) }

// Start initialized the picoborgrev
func (h *Driver) Start() (err error) {
	bus := h.GetBusOrDefault(h.connector.GetDefaultBus())
	address := h.GetAddressOrDefault(picoborgAddress)
	h.connection, err = h.connector.GetConnection(address, bus)

	h.lock.Lock()
	defer h.lock.Unlock()
	if err != nil {
		return err
	}

	h.connection.Write([]byte{commandGetID})
	data := []byte{0, 0, 0, 0}

	read, err := h.connection.Read(data)
	if err != nil {
		return err
	}

	if read == i2cMaxLen {
		if data[1] != i2cIDPicoborgRev {
			err := fmt.Errorf("Found a device but it is not a PicoBorg Revers (ID %X instead of %X)", data[1], i2cIDPicoborgRev)
			return err
		}
	} else {
		err := fmt.Errorf("Device not found")
		return err
	}
	return
}

// Halt stops all motors
func (h *Driver) Halt() (errs error) {
	err := h.StopAllMotors()
	if err != nil {
		multierror.Append(errs, err)
	}
	return nil
}

// StopAllMotors will stop all motors
func (h *Driver) StopAllMotors() error {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandAllOFF))
	return err
}

// ResetEPO latch state, use to allow movement again after the EPO has been tripped
func (h *Driver) ResetEPO() error {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandResetEPO))
	return err
}

// GetEPO Reads the system EPO latch state.
func (h *Driver) GetEPO() (bool, error) {
	h.lock.Lock()
	defer h.lock.Unlock()

	err := h.connection.WriteByte(byte(commandGetEPO))
	if err != nil {
		return false, err
	}

	data, err := h.connection.ReadByte()
	if err != nil {
		return false, err
	}

	if int(data) == commandValueOff {
		return false, nil
	}
	return true, nil
}

// SetMotorA generic set motor speed function
func (h *Driver) SetMotorA(power float32) error {
	h.lock.Lock()
	defer h.lock.Unlock()

	var command byte
	var pwm int
	if power < 0 {
		command = commandSetAREV
		pwm = -int(pwmMax * power)
	} else {
		command = commandSetAFWD
		pwm = int(pwmMax * power)
	}

	err := h.connection.WriteByteData(byte(command), byte(pwm))
	if err != nil {
		return err
	}
	return nil
}

// SetMotorB generic set motor speed function
func (h *Driver) SetMotorB(power float32) error {
	h.lock.Lock()
	defer h.lock.Unlock()
	var command byte
	var pwm int
	if power < 0 {
		command = commandSetBREV
		pwm = -int(pwmMax * power)
	} else {
		command = commandSetBFWD
		pwm = int(pwmMax * power)
	}

	err := h.connection.WriteByteData(byte(command), byte(pwm))
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Package contentaddressable contains tools for writing content addressable files.
Files are written to a temporary location, and only renamed to the final
location after the file's OID (Object ID) has been verified.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
    file, err := contentaddressable.NewFile(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    file.Oid // 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b

    written, err := io.Copy(file, someReader)

    if err == nil {
    // Move file to final location if OID is verified.
      err = file.Accept()
    }

    if err != nil {
      panic(err)
    }

Currently SHA-256 is used for a file's OID.
*/
package contentaddressable
<commit_msg>update docs with proposed reader interface<commit_after>/*
Package contentaddressable contains tools for reading and writing content
addressable files. Files are written to a temporary location, and only renamed
to the final location after the file's OID (Object ID) has been verified.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
    file, err := contentaddressable.NewFile(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    file.Oid // 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b

    written, err := io.Copy(file, someReader)

    if err == nil {
    // Move file to final location if OID is verified.
      err = file.Accept()
    }

    if err != nil {
      panic(err)
    }

Currently SHA-256 is used for a file's OID.

You can also read files, while verifying that they are not corrupt.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"

    // get this from doing an os.Stat() or something
    expectedSize := 123

    // returns a contentaddressable.ReadCloser, with some extra functions on top
    // of io.ReadCloser.
    reader, err := contentaddressable.Open(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    written, err := io.Copy(ioutil.Discard, reader)
    if err != nil {
      panic(err)
    }

    seenBytes := reader.SeenBytes()

    if written != seenBytes {
      panic("reader is broken")
    }

    if seenBytes < expected {
      panic("partial read")
    }

    if reader.Oid() != "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b" {
      panic("SHA-256 signature doesn't match expected")
    }
*/
package contentaddressable
<|endoftext|>


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

// A tool to add missing fields to an existing GCS bucket, for #18.
//
// Input is on stdin, and is of the form "<SHA-1> <CRC32C> <MD5>", e.g.:
//
//     e04b25d650dee1dff6ab1743724fa7c184282e94 0x12e9bf88 2bad5bb78f17232ef8c727f59eb82325
//
package main

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/oauthutil"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var fKeyFile = flag.String("key_file", "", "")
var fBucket = flag.String("bucket", "", "")
var fToken = flag.String("token", "", "Initial continuation token. Be careful.")
var fUpdateParallelism = flag.Int("parallelism", 128, "")

type crc32cChecksum uint32
type md5Hash [md5.Size]byte
type sha1Hash [sha1.Size]byte

type checksums struct {
	crc32c crc32cChecksum
	md5    md5Hash
}

// A mapping from SHA-1 to CRC32C and MD5.
type checksumMap map[sha1Hash]checksums

const (
	// Cf. blob_store.go
	blobObjectNamePrefix = "blobs/"

	// Cf. gcs_store.go
	metadataKey_SHA1   = "comeback_sha1"
	metadataKey_CRC32C = "comeback_crc32c"
	metadataKey_MD5    = "comeback_md5"
)

var gInputLineRe = regexp.MustCompile(
	"^([0-9a-f]{40}) (0x[0-9a-f]{8}) ([0-9a-f]{32})$")

func parseInputLine(line []byte) (sha1 sha1Hash, c checksums, err error) {
	// Match against the regexp.
	matches := gInputLineRe.FindSubmatch(line)
	if matches == nil {
		err = errors.New("No match.")
		return
	}

	// Parse each component.
	_, err = hex.Decode(sha1[:], matches[1])
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[1], err))
	}

	crc32c64, err := strconv.ParseUint(string(matches[2]), 0, 32)
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[2], err))
	}

	c.crc32c = crc32cChecksum(crc32c64)

	_, err = hex.Decode(c.md5[:], matches[3])
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[3], err))
	}

	return
}

// Read the supplied input file, producing a checksum map.
func parseInput(in io.Reader) (m checksumMap, err error) {
	m = make(checksumMap)

	// Scan each line.
	scanner := bufio.NewScanner(in)
	for scanner.Scan() {
		var sha1 sha1Hash
		var c checksums
		sha1, c, err = parseInputLine(scanner.Bytes())
		if err != nil {
			err = fmt.Errorf("Parsing input line %q: %v", scanner.Text(), err)
			return
		}

		m[sha1] = c
	}

	// Was there an error scanning?
	if scanner.Err() != nil {
		err = fmt.Errorf("Scanning: %v", scanner.Err())
		return
	}

	return
}

// List all blob objects in the GCS bucket into the channel.
func listBlobObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	objects chan<- *gcs.Object) (err error) {
	req := &gcs.ListObjectsRequest{
		Prefix:            blobObjectNamePrefix,
		ContinuationToken: *fToken,
	}

	// List until we run out.
	for {
		// Fetch the next batch.
		var listing *gcs.Listing
		listing, err = bucket.ListObjects(ctx, req)
		if err != nil {
			err = fmt.Errorf("ListObjects: %v", err)
			return
		}

		// Pass on each object.
		for _, o := range listing.Objects {
			// Special case: for gcsfuse compatibility, we allow blobObjectNamePrefix
			// to exist as its own object name. Skip it.
			if o.Name == blobObjectNamePrefix {
				continue
			}

			select {
			case objects <- o:

				// Cancelled?
			case <-ctx.Done():
				err = ctx.Err()
				return
			}
		}

		// Are we done?
		if listing.ContinuationToken == "" {
			break
		}

		req.ContinuationToken = listing.ContinuationToken
		log.Printf("Continuation token: %q", req.ContinuationToken)
	}

	return
}

// Filter to names of objects that lack the appropriate metadata keys.
func filterToProblematicNames(
	ctx context.Context,
	objects <-chan *gcs.Object,
	names chan<- string) (err error) {
	for o := range objects {
		// Skip objects that already have all of the keys.
		_, ok0 := o.Metadata[metadataKey_SHA1]
		_, ok1 := o.Metadata[metadataKey_CRC32C]
		_, ok2 := o.Metadata[metadataKey_MD5]

		if ok0 && ok1 && ok2 {
			continue
		}

		// Pass on the names of others.
		select {
		case names <- o.Name:

			// Cancelled?
		case <-ctx.Done():
			err = ctx.Err()
			return
		}
	}

	return
}

// Parse the object name into its expected SHA-1 hash.
func parseObjectName(name string) (sha1 sha1Hash, err error) {
	if !strings.HasPrefix(name, blobObjectNamePrefix) {
		err = fmt.Errorf("Expected prefix")
		return
	}

	hexSha1 := strings.TrimPrefix(name, blobObjectNamePrefix)
	score, err := blob.ParseHexScore(hexSha1)
	if err != nil {
		err = fmt.Errorf("ParseHexScore: %v", err)
		return
	}

	sha1 = sha1Hash(score)
	return
}

// For each object name, issue a request to set the appropriate metadata keys
// based on the contents of the supplied map. Write out the names of the
// objects processed, and those for whom info wasn't available.
func fixProblematicObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	info checksumMap,
	names <-chan string,
	processed chan<- string,
	unknown chan<- string) (err error) {
	for name := range names {
		// Parse the name.
		var sha1 sha1Hash
		sha1, err = parseObjectName(name)
		if err != nil {
			err = fmt.Errorf("Parsing object name %q: %v", name, err)
			return
		}

		// Do we have info for this object?
		c, ok := info[sha1]
		if !ok {
			select {
			case unknown <- name:

				// Cancelled?
			case <-ctx.Done():
				err = ctx.Err()
				return
			}

			continue
		}

		// Fix it up.
		//
		// Formats cf. gcs_store.go.
		sha1Str := hex.EncodeToString(sha1[:])
		crc32cStr := fmt.Sprintf("%#08x", c.crc32c)
		md5Str := hex.EncodeToString(c.md5[:])

		req := &gcs.UpdateObjectRequest{
			Name: name,
			Metadata: map[string]*string{
				metadataKey_SHA1:   &sha1Str,
				metadataKey_CRC32C: &crc32cStr,
				metadataKey_MD5:    &md5Str,
			},
		}

		_, err = bucket.UpdateObject(ctx, req)
		if err != nil {
			err = fmt.Errorf("UpdateObject: %v", err)
			return
		}

		// Pass on the name as processed.
		select {
		case processed <- name:

			// Cancelled?
		case <-ctx.Done():
			err = ctx.Err()
			return
		}
	}

	return
}

// Log status updates, and at the end log the objects that were not
// processed, returning an error if non-zero.
func monitorProgress(
	ctx context.Context,
	processedChan <-chan string,
	unknownChan <-chan string) (err error) {
	var processed int
	var unknown int

	// Set up a ticker for logging status updates.
	const period = time.Second
	ticker := time.NewTicker(period)
	defer ticker.Stop()

	// Keep going until both channels are closed.
	for processedChan != nil || unknownChan != nil {
		select {
		case <-ticker.C:
			log.Printf("%v processed successfully, %v unknown", processed, unknown)

		case _, ok := <-processedChan:
			if ok {
				processed++
			} else {
				processedChan = nil
			}

		case name, ok := <-unknownChan:
			if ok {
				log.Printf("Unknown object: %q", name)
				unknown++
			} else {
				unknownChan = nil
			}
		}
	}

	// Return an error if any object was unknown.
	if unknown != 0 {
		err = fmt.Errorf("%v unknown objects", unknown)
		return
	}

	return
}

func run(
	bucket gcs.Bucket,
	info checksumMap) (err error) {
	b := syncutil.NewBundle(context.Background())
	defer func() { err = b.Join() }()

	// List all of the blob objects.
	objectRecords := make(chan *gcs.Object, 10000)
	b.Add(func(ctx context.Context) (err error) {
		defer close(objectRecords)
		err = listBlobObjects(ctx, bucket, objectRecords)
		if err != nil {
			err = fmt.Errorf("listBlobObjects: %v", err)
			return
		}

		return
	})

	// Filter to the ones we need to fix up.
	problematicNames := make(chan string, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(problematicNames)
		err = filterToProblematicNames(ctx, objectRecords, problematicNames)
		if err != nil {
			err = fmt.Errorf("filterToProblematicNames: %v", err)
			return
		}

		return
	})

	// Fix those objects with some parallelism.
	const parallelism = 128
	var wg sync.WaitGroup

	processed := make(chan string, 100)
	unknown := make(chan string, 100)

	for i := 0; i < *fUpdateParallelism; i++ {
		wg.Add(1)
		b.Add(func(ctx context.Context) (err error) {
			defer wg.Done()
			err = fixProblematicObjects(
				ctx,
				bucket,
				info,
				problematicNames,
				processed,
				unknown)

			if err != nil {
				err = fmt.Errorf("fixProblematicObjects: %v", err)
				return
			}

			return
		})
	}

	go func() {
		wg.Wait()
		close(processed)
		close(unknown)
	}()

	// Log status updates, and at the end log the objects that were not
	// processed, returning an error if non-zero.
	b.Add(func(ctx context.Context) (err error) {
		err = monitorProgress(ctx, processed, unknown)
		if err != nil {
			err = fmt.Errorf("monitorProgress: %v", err)
			return
		}

		return
	})

	return
}

func panicIf(err *error) {
	if *err != nil {
		panic(*err)
	}
}

func getHTTPClient() (client *http.Client, err error) {
	if *fKeyFile == "" {
		err = errors.New("You must set --key_file.")
		return
	}

	const scope = gcs.Scope_FullControl
	client, err = oauthutil.NewJWTHttpClient(*fKeyFile, []string{scope})
	if err != nil {
		err = fmt.Errorf("oauthutil.NewJWTHttpClient: %v", err)
		return
	}

	return
}

func bucketName() (name string, err error) {
	name = *fBucket
	if name == "" {
		err = errors.New("You must set --bucket.")
		return
	}

	return
}

func getBucket() (b gcs.Bucket) {
	var err error
	defer panicIf(&err)

	// Get the HTTP client,
	client, err := getHTTPClient()
	if err != nil {
		err = fmt.Errorf("IntegrationTestHTTPClient: %v", err)
		return
	}

	// Find the bucket name.
	name, err := bucketName()
	if err != nil {
		err = fmt.Errorf("bucketName: %v", err)
		return
	}

	// Create a connection.
	cfg := &gcs.ConnConfig{
		HTTPClient:      client,
		MaxBackoffSleep: 30 * time.Second,
	}

	conn, err := gcs.NewConn(cfg)
	if err != nil {
		err = fmt.Errorf("gcs.NewConn: %v", err)
		return
	}

	// Extract the bucket.
	b = conn.GetBucket(name)

	return
}

func main() {
	flag.Parse()
	bucket := getBucket()
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

	// Panic if anything below fails.
	var err error
	defer func() {
		if err != nil {
			log.Fatal(err)
		}
	}()

	// Parse the input.
	log.Println("Parsing input...")
	info, err := parseInput(os.Stdin)
	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	log.Println("Done parsing input.")

	// Run.
	err = run(bucket, info)
	if err != nil {
		err = fmt.Errorf("run: %v", err)
		return
	}
}
<commit_msg>Use for fix_metadata.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A tool to add missing fields to an existing GCS bucket, for #18.
//
// Input is on stdin, and is of the form "<SHA-1> <CRC32C> <MD5>", e.g.:
//
//     e04b25d650dee1dff6ab1743724fa7c184282e94 0x12e9bf88 2bad5bb78f17232ef8c727f59eb82325
//
package main

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/oauthutil"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var fKeyFile = flag.String("key_file", "", "")
var fBucket = flag.String("bucket", "", "")
var fToken = flag.String("token", "", "Initial continuation token. Be careful.")
var fUpdateParallelism = flag.Int("parallelism", 128, "")

type crc32cChecksum uint32
type md5Hash [md5.Size]byte
type sha1Hash [sha1.Size]byte

type checksums struct {
	crc32c crc32cChecksum
	md5    md5Hash
}

// A mapping from SHA-1 to CRC32C and MD5.
type checksumMap map[sha1Hash]checksums

const (
	// Cf. blob_store.go
	blobObjectNamePrefix = "blobs/"

	// Cf. gcs_store.go
	metadataKey_SHA1   = "comeback_sha1"
	metadataKey_CRC32C = "comeback_crc32c"
	metadataKey_MD5    = "comeback_md5"
)

var gInputLineRe = regexp.MustCompile(
	"^([0-9a-f]{40}) (0x[0-9a-f]{8}) ([0-9a-f]{32})$")

func parseInputLine(line []byte) (sha1 sha1Hash, c checksums, err error) {
	// Match against the regexp.
	matches := gInputLineRe.FindSubmatch(line)
	if matches == nil {
		err = errors.New("No match.")
		return
	}

	// Parse each component.
	_, err = hex.Decode(sha1[:], matches[1])
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[1], err))
	}

	crc32c64, err := strconv.ParseUint(string(matches[2]), 0, 32)
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[2], err))
	}

	c.crc32c = crc32cChecksum(crc32c64)

	_, err = hex.Decode(c.md5[:], matches[3])
	if err != nil {
		panic(fmt.Sprintf("Unexpected decode error for %q: %v", matches[3], err))
	}

	return
}

// Read the supplied input file, producing a checksum map.
func parseInput(in io.Reader) (m checksumMap, err error) {
	m = make(checksumMap)

	// Scan each line.
	scanner := bufio.NewScanner(in)
	for scanner.Scan() {
		var sha1 sha1Hash
		var c checksums
		sha1, c, err = parseInputLine(scanner.Bytes())
		if err != nil {
			err = fmt.Errorf("Parsing input line %q: %v", scanner.Text(), err)
			return
		}

		m[sha1] = c
	}

	// Was there an error scanning?
	if scanner.Err() != nil {
		err = fmt.Errorf("Scanning: %v", scanner.Err())
		return
	}

	return
}

// Filter to names of objects that lack the appropriate metadata keys.
func filterToProblematicNames(
	ctx context.Context,
	objects <-chan *gcs.Object,
	names chan<- string) (err error) {
	for o := range objects {
		// Skip objects that already have all of the keys.
		_, ok0 := o.Metadata[metadataKey_SHA1]
		_, ok1 := o.Metadata[metadataKey_CRC32C]
		_, ok2 := o.Metadata[metadataKey_MD5]

		if ok0 && ok1 && ok2 {
			continue
		}

		// Pass on the names of others.
		select {
		case names <- o.Name:

			// Cancelled?
		case <-ctx.Done():
			err = ctx.Err()
			return
		}
	}

	return
}

// Parse the object name into its expected SHA-1 hash.
func parseObjectName(name string) (sha1 sha1Hash, err error) {
	if !strings.HasPrefix(name, blobObjectNamePrefix) {
		err = fmt.Errorf("Expected prefix")
		return
	}

	hexSha1 := strings.TrimPrefix(name, blobObjectNamePrefix)
	score, err := blob.ParseHexScore(hexSha1)
	if err != nil {
		err = fmt.Errorf("ParseHexScore: %v", err)
		return
	}

	sha1 = sha1Hash(score)
	return
}

// For each object name, issue a request to set the appropriate metadata keys
// based on the contents of the supplied map. Write out the names of the
// objects processed, and those for whom info wasn't available.
func fixProblematicObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	info checksumMap,
	names <-chan string,
	processed chan<- string,
	unknown chan<- string) (err error) {
	for name := range names {
		// Parse the name.
		var sha1 sha1Hash
		sha1, err = parseObjectName(name)
		if err != nil {
			err = fmt.Errorf("Parsing object name %q: %v", name, err)
			return
		}

		// Do we have info for this object?
		c, ok := info[sha1]
		if !ok {
			select {
			case unknown <- name:

				// Cancelled?
			case <-ctx.Done():
				err = ctx.Err()
				return
			}

			continue
		}

		// Fix it up.
		//
		// Formats cf. gcs_store.go.
		sha1Str := hex.EncodeToString(sha1[:])
		crc32cStr := fmt.Sprintf("%#08x", c.crc32c)
		md5Str := hex.EncodeToString(c.md5[:])

		req := &gcs.UpdateObjectRequest{
			Name: name,
			Metadata: map[string]*string{
				metadataKey_SHA1:   &sha1Str,
				metadataKey_CRC32C: &crc32cStr,
				metadataKey_MD5:    &md5Str,
			},
		}

		_, err = bucket.UpdateObject(ctx, req)
		if err != nil {
			err = fmt.Errorf("UpdateObject: %v", err)
			return
		}

		// Pass on the name as processed.
		select {
		case processed <- name:

			// Cancelled?
		case <-ctx.Done():
			err = ctx.Err()
			return
		}
	}

	return
}

// Log status updates, and at the end log the objects that were not
// processed, returning an error if non-zero.
func monitorProgress(
	ctx context.Context,
	processedChan <-chan string,
	unknownChan <-chan string) (err error) {
	var processed int
	var unknown int

	// Set up a ticker for logging status updates.
	const period = time.Second
	ticker := time.NewTicker(period)
	defer ticker.Stop()

	// Keep going until both channels are closed.
	for processedChan != nil || unknownChan != nil {
		select {
		case <-ticker.C:
			log.Printf("%v processed successfully, %v unknown", processed, unknown)

		case _, ok := <-processedChan:
			if ok {
				processed++
			} else {
				processedChan = nil
			}

		case name, ok := <-unknownChan:
			if ok {
				log.Printf("Unknown object: %q", name)
				unknown++
			} else {
				unknownChan = nil
			}
		}
	}

	// Return an error if any object was unknown.
	if unknown != 0 {
		err = fmt.Errorf("%v unknown objects", unknown)
		return
	}

	return
}

func run(
	bucket gcs.Bucket,
	info checksumMap) (err error) {
	b := syncutil.NewBundle(context.Background())
	defer func() { err = b.Join() }()

	// List all of the blob objects.
	objectRecords := make(chan *gcs.Object, 10000)
	b.Add(func(ctx context.Context) (err error) {
		defer close(objectRecords)
		err = blob.ListBlobObjects(ctx, bucket, blobObjectNamePrefix, objectRecords)
		if err != nil {
			err = fmt.Errorf("ListBlobObjects: %v", err)
			return
		}

		return
	})

	// Filter to the ones we need to fix up.
	problematicNames := make(chan string, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(problematicNames)
		err = filterToProblematicNames(ctx, objectRecords, problematicNames)
		if err != nil {
			err = fmt.Errorf("filterToProblematicNames: %v", err)
			return
		}

		return
	})

	// Fix those objects with some parallelism.
	const parallelism = 128
	var wg sync.WaitGroup

	processed := make(chan string, 100)
	unknown := make(chan string, 100)

	for i := 0; i < *fUpdateParallelism; i++ {
		wg.Add(1)
		b.Add(func(ctx context.Context) (err error) {
			defer wg.Done()
			err = fixProblematicObjects(
				ctx,
				bucket,
				info,
				problematicNames,
				processed,
				unknown)

			if err != nil {
				err = fmt.Errorf("fixProblematicObjects: %v", err)
				return
			}

			return
		})
	}

	go func() {
		wg.Wait()
		close(processed)
		close(unknown)
	}()

	// Log status updates, and at the end log the objects that were not
	// processed, returning an error if non-zero.
	b.Add(func(ctx context.Context) (err error) {
		err = monitorProgress(ctx, processed, unknown)
		if err != nil {
			err = fmt.Errorf("monitorProgress: %v", err)
			return
		}

		return
	})

	return
}

func panicIf(err *error) {
	if *err != nil {
		panic(*err)
	}
}

func getHTTPClient() (client *http.Client, err error) {
	if *fKeyFile == "" {
		err = errors.New("You must set --key_file.")
		return
	}

	const scope = gcs.Scope_FullControl
	client, err = oauthutil.NewJWTHttpClient(*fKeyFile, []string{scope})
	if err != nil {
		err = fmt.Errorf("oauthutil.NewJWTHttpClient: %v", err)
		return
	}

	return
}

func bucketName() (name string, err error) {
	name = *fBucket
	if name == "" {
		err = errors.New("You must set --bucket.")
		return
	}

	return
}

func getBucket() (b gcs.Bucket) {
	var err error
	defer panicIf(&err)

	// Get the HTTP client,
	client, err := getHTTPClient()
	if err != nil {
		err = fmt.Errorf("IntegrationTestHTTPClient: %v", err)
		return
	}

	// Find the bucket name.
	name, err := bucketName()
	if err != nil {
		err = fmt.Errorf("bucketName: %v", err)
		return
	}

	// Create a connection.
	cfg := &gcs.ConnConfig{
		HTTPClient:      client,
		MaxBackoffSleep: 30 * time.Second,
	}

	conn, err := gcs.NewConn(cfg)
	if err != nil {
		err = fmt.Errorf("gcs.NewConn: %v", err)
		return
	}

	// Extract the bucket.
	b = conn.GetBucket(name)

	return
}

func main() {
	flag.Parse()
	bucket := getBucket()
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

	// Panic if anything below fails.
	var err error
	defer func() {
		if err != nil {
			log.Fatal(err)
		}
	}()

	// Parse the input.
	log.Println("Parsing input...")
	info, err := parseInput(os.Stdin)
	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	log.Println("Done parsing input.")

	// Run.
	err = run(bucket, info)
	if err != nil {
		err = fmt.Errorf("run: %v", err)
		return
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 grpcauth

import (
	"crypto/tls"
	"math/rand"
	"strings"
	"time"

	gContext "golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"

	"upspin.io/log"
	"upspin.io/upspin"
	"upspin.io/upspin/proto"
)

// GRPCCommon is an interface that all GRPC services implement for authentication and ping as part of upspin.Service.
type GRPCCommon interface {
	// Authenticate is the GRPC call for Authenticate.
	Authenticate(ctx gContext.Context, in *proto.AuthenticateRequest, opts ...grpc.CallOption) (*proto.AuthenticateResponse, error)
	// Ping is the GRPC call for Ping.
	Ping(ctx gContext.Context, in *proto.PingRequest, opts ...grpc.CallOption) (*proto.PingResponse, error)
}

// AuthClientService is a partial Service that uses GRPC as transport and implements Authentication.
type AuthClientService struct {
	grpcCommon       GRPCCommon
	grpcConn         *grpc.ClientConn
	context          upspin.Context
	authToken        string
	lastTokenRefresh time.Time

	keepAliveInterval time.Duration // interval of keep-alive packets.
	lastNetActivity   time.Time     // last known time of some network activity.
	closeKeepAlive    chan bool     // channel used to tell the keep-alive routine to exit.
	keepAliveRound    uint64        // counts iterations of the keep-alive routine. Mostly for tests.
}

const (
	// AllowSelfSignedCertificate is used for documenting the parameter with same name in NewGRPCClient.
	AllowSelfSignedCertificate = true

	// KeepAliveInterval is a suggested interval between keep-alive ping requests to the server.
	// A value of 0 means keep-alives are disabled. Google Cloud Platform (GCP) times out connections
	// every 10 minutes so a smaller values are recommended for talking to servers on GCP.
	KeepAliveInterval = 5 * time.Minute
)

// To be safe, we refresh the token 1 hour ahead of time.
var tokenFreshnessDuration = authTokenDuration - time.Hour

// NewGRPCClient returns new GRPC client connected securely (with TLS) to a GRPC server at a net address.
// The address is expected to be a raw network address with port number, as in domain.com:5580. However, for convenience,
// it is optionally accepted for the time being to use one of the following prefixes:
// https://, http://, grpc://. This may change in the future.
// A keep alive interval indicates the amount of time to send ping requests to the server. A duration of 0 disables
// keep-alive packets.
// If allowSelfSignedCertificates is true, the client will connect with a server with a self-signed certificate.
// Otherwise it will reject it. Mostly only useful for testing a local server.
func NewGRPCClient(context *upspin.Context, netAddr upspin.NetAddr, keepAliveInterval time.Duration, allowSelfSignedCertificate bool) (*AuthClientService, error) {
	if keepAliveInterval != 0 && keepAliveInterval < time.Minute {
		log.Info.Printf("Keep-alive interval too short. You may overload the server and be throttled")
	}
	addr := string(netAddr)
	isHTTP := strings.HasPrefix(addr, "http://")
	isHTTPS := strings.HasPrefix(addr, "https://")
	isGRPC := strings.HasPrefix(addr, "grpc://")
	skip := 0
	switch {
	case isHTTP, isGRPC:
		skip = 7
	case isHTTPS:
		skip = 8
	}
	conn, err := grpc.Dial(addr[skip:],
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: allowSelfSignedCertificate})),
		grpc.WithBlock(),
	)
	if err != nil {
		return nil, err
	}
	ac := &AuthClientService{
		grpcConn:          conn,
		context:           *context,
		keepAliveInterval: keepAliveInterval,
		closeKeepAlive:    make(chan bool, 1),
	}
	if keepAliveInterval != 0 {
		go ac.keepAlive()
	}
	return ac, nil
}

// keepAlive loops forever pinging the server every keepAliveInterval. It skips pings if there has been network
// activity more recently than the keep alive interval. It must run on a separate go routine.
func (ac *AuthClientService) keepAlive() {
	log.Debug.Printf("Starting keep alive client")
	sleepFor := ac.keepAliveInterval
	for {
		ac.keepAliveRound++
		select {
		case <-time.After(sleepFor):
			lastIdleness := time.Since(ac.lastNetActivity)
			if lastIdleness < ac.keepAliveInterval {
				sleepFor = ac.keepAliveInterval - lastIdleness
				log.Debug.Printf("New ping in %v", sleepFor)
				continue
			}
			sleepFor = ac.keepAliveInterval
			if !ac.Ping() {
				log.Error.Printf("grpcauth: keepAlive: ping failed")
			}
			log.Debug.Printf("grpcAuth: keepAlive: ping okay")
			ac.lastNetActivity = time.Now()
		case <-ac.closeKeepAlive:
			log.Debug.Printf("grpcauth: keepAlive: exiting keep alive routine")
			ac.keepAliveRound = 0
			return
		}
	}
}

// SetService sets the underlying RPC service which was obtained with proto.NewSERVICENAMEClient, where SERVICENAME is
// the RPC service definition from the proto file.
func (ac *AuthClientService) SetService(common GRPCCommon) {
	ac.grpcCommon = common
}

// GRPCConn returns the grpc client connection used to dial the server.
func (ac *AuthClientService) GRPCConn() *grpc.ClientConn {
	return ac.grpcConn
}

// Authenticate implements upspin.Service.
func (ac *AuthClientService) Authenticate(ctx *upspin.Context) error {
	req := &proto.AuthenticateRequest{
		UserName: string(ctx.UserName),
		Now:      time.Now().UTC().Format(time.ANSIC), // to discourage signature replay
	}
	sig, err := ctx.Factotum.UserSign([]byte(string(req.UserName) + " Authenticate " + req.Now))
	if err != nil {
		return err
	}
	req.Signature = &proto.Signature{
		R: sig.R.String(),
		S: sig.S.String(),
	}
	resp, err := ac.grpcCommon.Authenticate(gContext.Background(), req)
	if err != nil {
		return err
	}
	log.Debug.Printf("Authenticate: got authtoken for user %s: %s", req.UserName, resp.Token)
	ac.authToken = resp.Token
	now := time.Now()
	ac.lastTokenRefresh = now
	ac.lastNetActivity = now
	return nil
}

// Ping implements uspin.Service.
func (ac *AuthClientService) Ping() bool {
	seq := rand.Int31()
	req := &proto.PingRequest{
		PingSequence: seq,
	}
	gctx, _ := gContext.WithTimeout(gContext.Background(), 3*time.Second) // ignore the cancel function.
	resp, err := ac.grpcCommon.Ping(gctx, req)
	if err != nil {
		log.Printf("Ping error: %s", err)
	}
	ac.lastNetActivity = time.Now()
	return err == nil && resp.PingSequence == seq
}

func (ac *AuthClientService) isAuthTokenExpired() bool {
	return ac.authToken == "" || ac.lastTokenRefresh.Add(tokenFreshnessDuration).Before(time.Now())
}

// NewAuthContext creates a new RPC context with the required authentication tokens set and ensures re-authentication
// is done if necessary.
func (ac *AuthClientService) NewAuthContext() (gContext.Context, error) {
	var err error
	if ac.isAuthTokenExpired() {
		err = ac.Authenticate(&ac.context)
		if err != nil {
			return nil, err
		}
	}
	log.Debug.Printf("SetAuthContext: set auth token: %s", ac.authToken)
	return metadata.NewContext(gContext.Background(), metadata.Pairs(authTokenKey, ac.authToken)), nil
}

// Close implements upspin.Service.
func (ac *AuthClientService) Close() {
	select { // prevents blocking if Close is called more than once.
	case ac.closeKeepAlive <- true:
	default:
	}
	// The only error returned is ErrClientConnClosing, meaning something else has already caused it to close.
	_ = ac.grpcConn.Close() // explicitly ignore the error as there's nothing we can do.
}
<commit_msg>auth/grpcauth: enable TCP keepalives on GRPC connections<commit_after>// Copyright 2016 The Upspin 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 grpcauth

import (
	"crypto/tls"
	"math/rand"
	"net"
	"strings"
	"time"

	gContext "golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"

	"upspin.io/log"
	"upspin.io/upspin"
	"upspin.io/upspin/proto"
)

// GRPCCommon is an interface that all GRPC services implement for authentication and ping as part of upspin.Service.
type GRPCCommon interface {
	// Authenticate is the GRPC call for Authenticate.
	Authenticate(ctx gContext.Context, in *proto.AuthenticateRequest, opts ...grpc.CallOption) (*proto.AuthenticateResponse, error)
	// Ping is the GRPC call for Ping.
	Ping(ctx gContext.Context, in *proto.PingRequest, opts ...grpc.CallOption) (*proto.PingResponse, error)
}

// AuthClientService is a partial Service that uses GRPC as transport and implements Authentication.
type AuthClientService struct {
	grpcCommon       GRPCCommon
	grpcConn         *grpc.ClientConn
	context          upspin.Context
	authToken        string
	lastTokenRefresh time.Time

	keepAliveInterval time.Duration // interval of keep-alive packets.
	lastNetActivity   time.Time     // last known time of some network activity.
	closeKeepAlive    chan bool     // channel used to tell the keep-alive routine to exit.
	keepAliveRound    uint64        // counts iterations of the keep-alive routine. Mostly for tests.
}

const (
	// AllowSelfSignedCertificate is used for documenting the parameter with same name in NewGRPCClient.
	AllowSelfSignedCertificate = true

	// KeepAliveInterval is a suggested interval between keep-alive ping requests to the server.
	// A value of 0 means keep-alives are disabled. Google Cloud Platform (GCP) times out connections
	// every 10 minutes so a smaller values are recommended for talking to servers on GCP.
	KeepAliveInterval = 5 * time.Minute
)

// To be safe, we refresh the token 1 hour ahead of time.
var tokenFreshnessDuration = authTokenDuration - time.Hour

// NewGRPCClient returns new GRPC client connected securely (with TLS) to a GRPC server at a net address.
// The address is expected to be a raw network address with port number, as in domain.com:5580. However, for convenience,
// it is optionally accepted for the time being to use one of the following prefixes:
// https://, http://, grpc://. This may change in the future.
// A keep alive interval indicates the amount of time to send ping requests to the server. A duration of 0 disables
// keep-alive packets.
// If allowSelfSignedCertificates is true, the client will connect with a server with a self-signed certificate.
// Otherwise it will reject it. Mostly only useful for testing a local server.
func NewGRPCClient(context *upspin.Context, netAddr upspin.NetAddr, keepAliveInterval time.Duration, allowSelfSignedCertificate bool) (*AuthClientService, error) {
	if keepAliveInterval != 0 && keepAliveInterval < time.Minute {
		log.Info.Printf("Keep-alive interval too short. You may overload the server and be throttled")
	}
	addr := string(netAddr)
	isHTTP := strings.HasPrefix(addr, "http://")
	isHTTPS := strings.HasPrefix(addr, "https://")
	isGRPC := strings.HasPrefix(addr, "grpc://")
	skip := 0
	switch {
	case isHTTP, isGRPC:
		skip = 7
	case isHTTPS:
		skip = 8
	}
	conn, err := grpc.Dial(addr[skip:],
		grpc.WithBlock(),
		grpc.WithDialer(dialWithKeepAlive),
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: allowSelfSignedCertificate})),
	)
	if err != nil {
		return nil, err
	}
	ac := &AuthClientService{
		grpcConn:          conn,
		context:           *context,
		keepAliveInterval: keepAliveInterval,
		closeKeepAlive:    make(chan bool, 1),
	}
	if keepAliveInterval != 0 {
		go ac.keepAlive()
	}
	return ac, nil
}

// keepAlive loops forever pinging the server every keepAliveInterval. It skips pings if there has been network
// activity more recently than the keep alive interval. It must run on a separate go routine.
func (ac *AuthClientService) keepAlive() {
	log.Debug.Printf("Starting keep alive client")
	sleepFor := ac.keepAliveInterval
	for {
		ac.keepAliveRound++
		select {
		case <-time.After(sleepFor):
			lastIdleness := time.Since(ac.lastNetActivity)
			if lastIdleness < ac.keepAliveInterval {
				sleepFor = ac.keepAliveInterval - lastIdleness
				log.Debug.Printf("New ping in %v", sleepFor)
				continue
			}
			sleepFor = ac.keepAliveInterval
			if !ac.Ping() {
				log.Error.Printf("grpcauth: keepAlive: ping failed")
			}
			log.Debug.Printf("grpcAuth: keepAlive: ping okay")
			ac.lastNetActivity = time.Now()
		case <-ac.closeKeepAlive:
			log.Debug.Printf("grpcauth: keepAlive: exiting keep alive routine")
			ac.keepAliveRound = 0
			return
		}
	}
}

// SetService sets the underlying RPC service which was obtained with proto.NewSERVICENAMEClient, where SERVICENAME is
// the RPC service definition from the proto file.
func (ac *AuthClientService) SetService(common GRPCCommon) {
	ac.grpcCommon = common
}

// GRPCConn returns the grpc client connection used to dial the server.
func (ac *AuthClientService) GRPCConn() *grpc.ClientConn {
	return ac.grpcConn
}

func dialWithKeepAlive(target string, timeout time.Duration) (net.Conn, error) {
	c, err := net.DialTimeout("tcp", target, timeout)
	if err != nil {
		return nil, err
	}
	if tc, ok := c.(*net.TCPConn); ok {
		if err := tc.SetKeepAlive(true); err != nil {
			return nil, err
		}
		if err := tc.SetKeepAlivePeriod(KeepAliveInterval); err != nil {
			return nil, err
		}
	}
	return c, nil
}

// Authenticate implements upspin.Service.
func (ac *AuthClientService) Authenticate(ctx *upspin.Context) error {
	req := &proto.AuthenticateRequest{
		UserName: string(ctx.UserName),
		Now:      time.Now().UTC().Format(time.ANSIC), // to discourage signature replay
	}
	sig, err := ctx.Factotum.UserSign([]byte(string(req.UserName) + " Authenticate " + req.Now))
	if err != nil {
		return err
	}
	req.Signature = &proto.Signature{
		R: sig.R.String(),
		S: sig.S.String(),
	}
	resp, err := ac.grpcCommon.Authenticate(gContext.Background(), req)
	if err != nil {
		return err
	}
	log.Debug.Printf("Authenticate: got authtoken for user %s: %s", req.UserName, resp.Token)
	ac.authToken = resp.Token
	now := time.Now()
	ac.lastTokenRefresh = now
	ac.lastNetActivity = now
	return nil
}

// Ping implements uspin.Service.
func (ac *AuthClientService) Ping() bool {
	seq := rand.Int31()
	req := &proto.PingRequest{
		PingSequence: seq,
	}
	gctx, _ := gContext.WithTimeout(gContext.Background(), 3*time.Second) // ignore the cancel function.
	resp, err := ac.grpcCommon.Ping(gctx, req)
	if err != nil {
		log.Printf("Ping error: %s", err)
	}
	ac.lastNetActivity = time.Now()
	return err == nil && resp.PingSequence == seq
}

func (ac *AuthClientService) isAuthTokenExpired() bool {
	return ac.authToken == "" || ac.lastTokenRefresh.Add(tokenFreshnessDuration).Before(time.Now())
}

// NewAuthContext creates a new RPC context with the required authentication tokens set and ensures re-authentication
// is done if necessary.
func (ac *AuthClientService) NewAuthContext() (gContext.Context, error) {
	var err error
	if ac.isAuthTokenExpired() {
		err = ac.Authenticate(&ac.context)
		if err != nil {
			return nil, err
		}
	}
	log.Debug.Printf("SetAuthContext: set auth token: %s", ac.authToken)
	return metadata.NewContext(gContext.Background(), metadata.Pairs(authTokenKey, ac.authToken)), nil
}

// Close implements upspin.Service.
func (ac *AuthClientService) Close() {
	select { // prevents blocking if Close is called more than once.
	case ac.closeKeepAlive <- true:
	default:
	}
	// The only error returned is ErrClientConnClosing, meaning something else has already caused it to close.
	_ = ac.grpcConn.Close() // explicitly ignore the error as there's nothing we can do.
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/gamepkg"
	"github.com/jkomoros/boardgame/boardgame-util/lib/golden"
)

type Golden struct {
	Serve
}

func (g *Golden) Run(p writ.Path, positional []string) {
	g.Prod = false
	g.Storage = "filesystem"

	pkg, err := gamepkg.NewFromPath(".", "")

	if err != nil {
		g.Base().errAndQuit("Current directory is not a valid package. You must run this command sitting in the root of a valid package. " + err.Error())
	}

	fmt.Println("Creating golden structures with " + pkg.AbsolutePath())

	if err := golden.MakeGoldenTest(pkg); err != nil {
		g.Base().errAndQuit("Couldn't create golden directory: " + err.Error())
	}

	g.doServe(p, positional, []*gamepkg.Pkg{pkg}, `"`+golden.GameRecordsFolder+`"`)

	fmt.Println("Cleaning golden folder...")
	if err := golden.CleanGoldenTest(pkg); err != nil {
		g.Base().errAndQuit("Couldn't clean golden: " + err.Error())
	}
}

func (g *Golden) Name() string {
	return "golden"
}

func (g *Golden) Description() string {
	return "Helps create golden test files for the current package"
}

func (g *Golden) HelpText() string {
	return g.Name() + ` helps create golden example games to test the current game package.

You run it sittig in the root of a game package, and it will create a stub server (with similiar behavior to what you'd get with 'boardgame-util serve'), but with only one game, and the games will all be persisted to a testdata folder, with a golden_test.go created.

This is useful for saving runs of games that are known good so that you can ensure you don't mess with the game logic later.
`
}

func (s *Golden) WritOptions() []*writ.Option {
	//Skip the first two, which are not valid for us.
	return s.Serve.WritOptions()[2:]
}
<commit_msg>Switch the name of golden command to `create-golden` to make it stand out more as a weird mode. Part of #648.<commit_after>package main

import (
	"fmt"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/gamepkg"
	"github.com/jkomoros/boardgame/boardgame-util/lib/golden"
)

type Golden struct {
	Serve
}

func (g *Golden) Run(p writ.Path, positional []string) {
	g.Prod = false
	g.Storage = "filesystem"

	pkg, err := gamepkg.NewFromPath(".", "")

	if err != nil {
		g.Base().errAndQuit("Current directory is not a valid package. You must run this command sitting in the root of a valid package. " + err.Error())
	}

	fmt.Println("Creating golden structures with " + pkg.AbsolutePath())

	if err := golden.MakeGoldenTest(pkg); err != nil {
		g.Base().errAndQuit("Couldn't create golden directory: " + err.Error())
	}

	g.doServe(p, positional, []*gamepkg.Pkg{pkg}, `"`+golden.GameRecordsFolder+`"`)

	fmt.Println("Cleaning golden folder...")
	if err := golden.CleanGoldenTest(pkg); err != nil {
		g.Base().errAndQuit("Couldn't clean golden: " + err.Error())
	}
}

func (g *Golden) Name() string {
	return "create-golden"
}

func (g *Golden) Description() string {
	return "Helps create golden test files for the current package"
}

func (g *Golden) HelpText() string {
	return g.Name() + ` helps create golden example games to test the current game package.

You run it sitting in the root of a game package, and it will create a stub server (with similiar behavior to what you'd get with 'boardgame-util serve'), but with only one game, and the games will all be persisted to a testdata folder, with a golden_test.go created.

This is useful for saving runs of games that are known good so that you can ensure you don't mess with the game logic later.
`
}

func (s *Golden) WritOptions() []*writ.Option {
	//Skip the first two, which are not valid for us.
	return s.Serve.WritOptions()[2:]
}
<|endoftext|>