<commit_before>package models

import (
	"fmt"
	"time"

	"github.com/laincloud/hagrid/config"
	"github.com/mijia/sweb/log"
)

type User struct {
	ID               int    `gorm:"primary_key:true"`
	Name             string `gorm:"type:varchar(64);not null;unique"`
	EmailAddress     string `gorm:"type:varchar(64);not null"`
	PhoneNumber      string `gorm:"type:varchar(64);not null"`
	BearychatTeam    string `gorm:"type:varchar(64);not null"`
	BearychatToken   string `gorm:"type:varchar(64);not null"`
	BearychatChannel string `gorm:"type:varchar(64);not null"`
	PushbulletToken  string `gorm:"type:varchar(64);not null"`
	SlackTeam        string `gorm:"type:varchar(64);not null"`
	SlackToken       string `gorm:"type:varchar(64);not null"`
	SlackChannel     string `gorm:"type:varchar(64);not null"`
	CreatedAt        time.Time

	AdminedAlerts  []Alert `gorm:"many2many:alert_to_user_admin"`
	NotifiedAlerts []Alert `gorm:"many2many:alert_to_user_notify"`
}

func AddUserIfNotExists(name string) error {
	maybeNewUser := &User{
		Name: name,
	}
	syncLock.Lock()
	defer syncLock.Unlock()
	if err := db.FirstOrCreate(maybeNewUser, maybeNewUser).Error; err != nil {
		log.Errorf("Adding user failed: %s", err.Error())
		return fmt.Errorf("Adding user failed. Ask admin for help")
	}
	return nil
}

func UpdateUser(user *User) error {
	syncLock.Lock()
	defer syncLock.Unlock()
	if err := db.Save(user).Error; err != nil {
		log.Errorf("Saving user failed: %s", err.Error())
		return fmt.Errorf("Saving user failed. Ask admin for help")
	}
	if err := renderUsers(); err != nil {
		log.Errorf("Rendering users failed: %s", err.Error())
		return fmt.Errorf("Rendering user failed. Ask admin for help")
	}
	return nil
}

func GetUser(userName string, user *User) error {
	if err := db.First(user, "name = ?", userName).Error; err != nil {
		log.Errorf("Getting user failed: %s", err.Error())
		return fmt.Errorf("Getting user failed. Ask admin for help")
	}
	if err := db.Model(&user).Association("AdminedAlerts").Find(&(user.AdminedAlerts)).Error; err != nil {
		log.Errorf("Getting user's admined alerts failed: %s", err.Error())
		return fmt.Errorf("Getting user's admined alerts failed. Ask admin for help")
	}
	// if err := db.Model(&user).Association("NotifiedAlerts").Find(&(user.NotifiedAlerts)).Error; err != nil {
	// 	log.Errorf("Getting user's notified alerts failed: %s", err.Error())
	// 	return fmt.Errorf("Getting user's notified alerts failed. Ask admin for help")
	// }
	return nil
}

func GetAllUsers(users *[]User, namePattern string) error {
	if err := db.Select("id, name").Where("name LIKE ? AND name <> ?", namePattern, config.GetSuperUser()).Find(users).Error; err != nil {
		log.Errorf("Getting all users with pattern %s failed: %s", namePattern, err.Error())
		return fmt.Errorf("Getting all users failed. Ask admin for help")
	}
	return nil
}

func GetAllUsersDetails(users *[]User, namePattern string) error {
	if err := db.Where("name LIKE ?", namePattern).Find(users).Error; err != nil {
		log.Errorf("Getting all users' details with pattern %s failed: %s", namePattern, err.Error())
		return fmt.Errorf("Getting all users' details failed. Ask admin for help")
	}
	return nil
}

func GetNotifiersByAlertID(id int, notifiers *[]User) error {
	if err := db.Model(&Alert{ID: id}).Association("Notifiers").Find(notifiers).Error; err != nil {
		log.Errorf("Getting all notifiers of alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting all notifiers failed. Ask admin for help")
	}
	return nil
}

func DeleteNotifier(alertID, notifierID int) error {
	if err := db.Exec("DELETE FROM `alert_to_user_notify` where alert_id = ? and user_id = ?", alertID, notifierID).Error; err != nil {
		log.Errorf("Deleting notifier %d of %d failed: %s", notifierID, alertID, err.Error())
		return fmt.Errorf("Deleting notifier failed. Ask admin for help")
	}
	return nil
}

func AddNotifier(alertID, notifierID int) error {
	if err := db.Exec("INSERT INTO `alert_to_user_notify`(alert_id, user_id) values(?, ?)", alertID, notifierID).Error; err != nil {
		log.Errorf("Adding notifier %d of %d failed: %s", notifierID, alertID, err.Error())
		return fmt.Errorf("Adding notifier failed. Ask admin for help")
	}
	return nil
}

func IsNotifierDuplicated(alertID, notifierID int) bool {
	var count int
	err := db.Table("alert_to_user_notify").Where("alert_id = ? and user_id = ?", alertID, notifierID).Count(&count).Error
	return count != 0 || err != nil
}

func GetAdminsByAlertID(id int, admins *[]User) error {
	if err := db.Model(&Alert{ID: id}).Select("id, name").Association("Admins").Find(admins).Error; err != nil {
		log.Errorf("Getting admins of alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting admins failed. Ask admin for help")
	}
	return nil
}

func DeleteAdmin(alertID, adminID int) error {
	if err := db.Exec("DELETE FROM `alert_to_user_admin` where alert_id = ? and user_id = ?", alertID, adminID).Error; err != nil {
		log.Errorf("Deleting admin %d of alert %d failed: %s", alertID, adminID, err.Error())
		return fmt.Errorf("Deleting admin failed. Ask admin for help")
	}
	return nil
}

func AddAdmin(alertID, adminID int) error {
	if err := db.Exec("INSERT INTO `alert_to_user_admin`(alert_id, user_id) values(?, ?)", alertID, adminID).Error; err != nil {
		log.Errorf("Adding admin %d of alert %d failed: %s", alertID, adminID, err.Error())
		return fmt.Errorf("Adding admin failed. Ask admin for help")
	}
	return nil
}

func IsAdminDuplicated(alertID, adminID int) bool {
	var count int
	err := db.Table("alert_to_user_admin").Where("alert_id = ? and user_id = ?", alertID, adminID).Count(&count).Error
	return count != 0 || err != nil
}

func renderUsers() error {
	var (
		users          []User
		err            error
		data, newStage string
	)
	if err = GetAllUsersDetails(&users, "%%%%"); err != nil {
		return err
	}
	data, err = renderTemplate("users.tmpl", map[string][]User{"Users": users})
	if err != nil {
		return err
	}
	if newStage, err = icinga2Client.UploadFiles(userPackage,
		map[string]string{
			"conf.d/user.conf": data,
		}); err != nil {
		return err
	}
	pkg, _ := icinga2Client.GetPackage(userPackage)
	if pkg != nil {
		for _, stage := range pkg.Stages {
			if stage != newStage && stage != pkg.ActiveStage {
				icinga2Client.DeleteStage(userPackage, stage)
			}
		}
	}
	return nil
}
<commit_msg>Create hagrid_user package before rendering new one<commit_after>package models

import (
	"fmt"
	"time"

	"github.com/laincloud/hagrid/config"
	"github.com/mijia/sweb/log"
)

type User struct {
	ID               int    `gorm:"primary_key:true"`
	Name             string `gorm:"type:varchar(64);not null;unique"`
	EmailAddress     string `gorm:"type:varchar(64);not null"`
	PhoneNumber      string `gorm:"type:varchar(64);not null"`
	BearychatTeam    string `gorm:"type:varchar(64);not null"`
	BearychatToken   string `gorm:"type:varchar(64);not null"`
	BearychatChannel string `gorm:"type:varchar(64);not null"`
	PushbulletToken  string `gorm:"type:varchar(64);not null"`
	SlackTeam        string `gorm:"type:varchar(64);not null"`
	SlackToken       string `gorm:"type:varchar(64);not null"`
	SlackChannel     string `gorm:"type:varchar(64);not null"`
	CreatedAt        time.Time

	AdminedAlerts  []Alert `gorm:"many2many:alert_to_user_admin"`
	NotifiedAlerts []Alert `gorm:"many2many:alert_to_user_notify"`
}

func AddUserIfNotExists(name string) error {
	maybeNewUser := &User{
		Name: name,
	}
	syncLock.Lock()
	defer syncLock.Unlock()
	if err := db.FirstOrCreate(maybeNewUser, maybeNewUser).Error; err != nil {
		log.Errorf("Adding user failed: %s", err.Error())
		return fmt.Errorf("Adding user failed. Ask admin for help")
	}
	return nil
}

func UpdateUser(user *User) error {
	syncLock.Lock()
	defer syncLock.Unlock()
	if err := db.Save(user).Error; err != nil {
		log.Errorf("Saving user failed: %s", err.Error())
		return fmt.Errorf("Saving user failed. Ask admin for help")
	}
	if err := renderUsers(); err != nil {
		log.Errorf("Rendering users failed: %s", err.Error())
		return fmt.Errorf("Rendering user failed. Ask admin for help")
	}
	return nil
}

func GetUser(userName string, user *User) error {
	if err := db.First(user, "name = ?", userName).Error; err != nil {
		log.Errorf("Getting user failed: %s", err.Error())
		return fmt.Errorf("Getting user failed. Ask admin for help")
	}
	if err := db.Model(&user).Association("AdminedAlerts").Find(&(user.AdminedAlerts)).Error; err != nil {
		log.Errorf("Getting user's admined alerts failed: %s", err.Error())
		return fmt.Errorf("Getting user's admined alerts failed. Ask admin for help")
	}
	// if err := db.Model(&user).Association("NotifiedAlerts").Find(&(user.NotifiedAlerts)).Error; err != nil {
	// 	log.Errorf("Getting user's notified alerts failed: %s", err.Error())
	// 	return fmt.Errorf("Getting user's notified alerts failed. Ask admin for help")
	// }
	return nil
}

func GetAllUsers(users *[]User, namePattern string) error {
	if err := db.Select("id, name").Where("name LIKE ? AND name <> ?", namePattern, config.GetSuperUser()).Find(users).Error; err != nil {
		log.Errorf("Getting all users with pattern %s failed: %s", namePattern, err.Error())
		return fmt.Errorf("Getting all users failed. Ask admin for help")
	}
	return nil
}

func GetAllUsersDetails(users *[]User, namePattern string) error {
	if err := db.Where("name LIKE ?", namePattern).Find(users).Error; err != nil {
		log.Errorf("Getting all users' details with pattern %s failed: %s", namePattern, err.Error())
		return fmt.Errorf("Getting all users' details failed. Ask admin for help")
	}
	return nil
}

func GetNotifiersByAlertID(id int, notifiers *[]User) error {
	if err := db.Model(&Alert{ID: id}).Association("Notifiers").Find(notifiers).Error; err != nil {
		log.Errorf("Getting all notifiers of alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting all notifiers failed. Ask admin for help")
	}
	return nil
}

func DeleteNotifier(alertID, notifierID int) error {
	if err := db.Exec("DELETE FROM `alert_to_user_notify` where alert_id = ? and user_id = ?", alertID, notifierID).Error; err != nil {
		log.Errorf("Deleting notifier %d of %d failed: %s", notifierID, alertID, err.Error())
		return fmt.Errorf("Deleting notifier failed. Ask admin for help")
	}
	return nil
}

func AddNotifier(alertID, notifierID int) error {
	if err := db.Exec("INSERT INTO `alert_to_user_notify`(alert_id, user_id) values(?, ?)", alertID, notifierID).Error; err != nil {
		log.Errorf("Adding notifier %d of %d failed: %s", notifierID, alertID, err.Error())
		return fmt.Errorf("Adding notifier failed. Ask admin for help")
	}
	return nil
}

func IsNotifierDuplicated(alertID, notifierID int) bool {
	var count int
	err := db.Table("alert_to_user_notify").Where("alert_id = ? and user_id = ?", alertID, notifierID).Count(&count).Error
	return count != 0 || err != nil
}

func GetAdminsByAlertID(id int, admins *[]User) error {
	if err := db.Model(&Alert{ID: id}).Select("id, name").Association("Admins").Find(admins).Error; err != nil {
		log.Errorf("Getting admins of alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting admins failed. Ask admin for help")
	}
	return nil
}

func DeleteAdmin(alertID, adminID int) error {
	if err := db.Exec("DELETE FROM `alert_to_user_admin` where alert_id = ? and user_id = ?", alertID, adminID).Error; err != nil {
		log.Errorf("Deleting admin %d of alert %d failed: %s", alertID, adminID, err.Error())
		return fmt.Errorf("Deleting admin failed. Ask admin for help")
	}
	return nil
}

func AddAdmin(alertID, adminID int) error {
	if err := db.Exec("INSERT INTO `alert_to_user_admin`(alert_id, user_id) values(?, ?)", alertID, adminID).Error; err != nil {
		log.Errorf("Adding admin %d of alert %d failed: %s", alertID, adminID, err.Error())
		return fmt.Errorf("Adding admin failed. Ask admin for help")
	}
	return nil
}

func IsAdminDuplicated(alertID, adminID int) bool {
	var count int
	err := db.Table("alert_to_user_admin").Where("alert_id = ? and user_id = ?", alertID, adminID).Count(&count).Error
	return count != 0 || err != nil
}

func renderUsers() error {
	var (
		users          []User
		err            error
		data, newStage string
	)
	if err = GetAllUsersDetails(&users, "%%%%"); err != nil {
		return err
	}
	data, err = renderTemplate("users.tmpl", map[string][]User{"Users": users})
	if err != nil {
		return err
	}
	icinga2Client.CreatePackage(userPackage)
	if newStage, err = icinga2Client.UploadFiles(userPackage,
		map[string]string{
			"conf.d/user.conf": data,
		}); err != nil {
		return err
	}
	pkg, _ := icinga2Client.GetPackage(userPackage)
	if pkg != nil {
		for _, stage := range pkg.Stages {
			if stage != newStage && stage != pkg.ActiveStage {
				icinga2Client.DeleteStage(userPackage, stage)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"

	log "github.com/gophish/gophish/logger"
)

// ErrModifyingOnlyAdmin occurs when there is an attempt to modify the only
// user account with the Admin role in such a way that there will be no user
// accounts left in Gophish with that role.
var ErrModifyingOnlyAdmin = errors.New("Cannot remove the only administrator")

// User represents the user model for gophish.
type User struct {
	Id       int64  `json:"id"`
	Username string `json:"username" sql:"not null;unique"`
	Hash     string `json:"-"`
	ApiKey   string `json:"-" sql:"not null;unique"`
	Role     Role   `json:"role" gorm:"association_autoupdate:false;association_autocreate:false"`
	RoleID   int64  `json:"-"`
}

// GetUser returns the user that the given id corresponds to. If no user is found, an
// error is thrown.
func GetUser(id int64) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("id=?", id).First(&u).Error
	return u, err
}

// GetUsers returns the users registered in Gophish
func GetUsers() ([]User, error) {
	us := []User{}
	err := db.Preload("Role").Find(&us).Error
	return us, err
}

// GetUserByAPIKey returns the user that the given API Key corresponds to. If no user is found, an
// error is thrown.
func GetUserByAPIKey(key string) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("api_key = ?", key).First(&u).Error
	return u, err
}

// GetUserByUsername returns the user that the given username corresponds to. If no user is found, an
// error is thrown.
func GetUserByUsername(username string) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("username = ?", username).First(&u).Error
	return u, err
}

// PutUser updates the given user
func PutUser(u *User) error {
	err := db.Save(u).Error
	return err
}

// EnsureEnoughAdmins ensures that there is more than one user account in
// Gophish with the Admin role. This function is meant to be called before
// modifying a user account with the Admin role in a non-revokable way.
func EnsureEnoughAdmins() error {
	role, err := GetRoleBySlug(RoleAdmin)
	if err != nil {
		return err
	}
	var adminCount int
	err = db.Model(&User{}).Where("role_id=?", role.ID).Count(&adminCount).Error
	if err != nil {
		return err
	}
	if adminCount == 1 {
		return ErrModifyingOnlyAdmin
	}
	return nil
}

// DeleteUser deletes the given user. To ensure that there is always at least
// one user account with the Admin role, this function will refuse to delete
// the last Admin.
func DeleteUser(id int64) error {
	existing, err := GetUser(id)
	if err != nil {
		return err
	}
	// If the user is an admin, we need to verify that it's not the last one.
	if existing.Role.Slug == RoleAdmin {
		err = EnsureEnoughAdmins()
		if err != nil {
			return err
		}
	}
	campaigns, err := GetCampaigns(id)
	if err != nil {
		return err
	}
	// Delete the campaigns
	log.Infof("Deleting campaigns for user ID %d", id)
	for _, campaign := range campaigns {
		err = DeleteCampaign(campaign.Id)
		if err != nil {
			return err
		}
	}
	log.Infof("Deleting pages for user ID %d", id)
	// Delete the landing pages
	pages, err := GetPages(id)
	if err != nil {
		return err
	}
	for _, page := range pages {
		err = DeletePage(page.Id, id)
		if err != nil {
			return err
		}
	}
	// Delete the templates
	log.Infof("Deleting templates for user ID %d", id)
	templates, err := GetTemplates(id)
	if err != nil {
		return err
	}
	for _, template := range templates {
		err = DeleteTemplate(template.Id, id)
		if err != nil {
			return err
		}
	}
	// Delete the groups
	log.Infof("Deleting groups for user ID %d", id)
	groups, err := GetGroups(id)
	if err != nil {
		return err
	}
	for _, group := range groups {
		err = DeleteGroup(&group)
		if err != nil {
			return err
		}
	}
	// Delete the sending profiles
	log.Infof("Deleting sending profiles for user ID %d", id)
	profiles, err := GetSMTPs(id)
	if err != nil {
		return err
	}
	for _, profile := range profiles {
		err = DeleteSMTP(profile.Id, id)
		if err != nil {
			return err
		}
	}
	// Finally, delete the user
	err = db.Where("id=?", id).Delete(&User{}).Error
	return err
}
<commit_msg>1624 - Return API Key on Password Change (#1663)<commit_after>package models

import (
	"errors"

	log "github.com/gophish/gophish/logger"
)

// ErrModifyingOnlyAdmin occurs when there is an attempt to modify the only
// user account with the Admin role in such a way that there will be no user
// accounts left in Gophish with that role.
var ErrModifyingOnlyAdmin = errors.New("Cannot remove the only administrator")

// User represents the user model for gophish.
type User struct {
	Id       int64  `json:"id"`
	Username string `json:"username" sql:"not null;unique"`
	Hash     string `json:"-"`
	ApiKey   string `json:"api_key" sql:"not null;unique"`
	Role     Role   `json:"role" gorm:"association_autoupdate:false;association_autocreate:false"`
	RoleID   int64  `json:"-"`
}

// GetUser returns the user that the given id corresponds to. If no user is found, an
// error is thrown.
func GetUser(id int64) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("id=?", id).First(&u).Error
	return u, err
}

// GetUsers returns the users registered in Gophish
func GetUsers() ([]User, error) {
	us := []User{}
	err := db.Preload("Role").Find(&us).Error
	return us, err
}

// GetUserByAPIKey returns the user that the given API Key corresponds to. If no user is found, an
// error is thrown.
func GetUserByAPIKey(key string) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("api_key = ?", key).First(&u).Error
	return u, err
}

// GetUserByUsername returns the user that the given username corresponds to. If no user is found, an
// error is thrown.
func GetUserByUsername(username string) (User, error) {
	u := User{}
	err := db.Preload("Role").Where("username = ?", username).First(&u).Error
	return u, err
}

// PutUser updates the given user
func PutUser(u *User) error {
	err := db.Save(u).Error
	return err
}

// EnsureEnoughAdmins ensures that there is more than one user account in
// Gophish with the Admin role. This function is meant to be called before
// modifying a user account with the Admin role in a non-revokable way.
func EnsureEnoughAdmins() error {
	role, err := GetRoleBySlug(RoleAdmin)
	if err != nil {
		return err
	}
	var adminCount int
	err = db.Model(&User{}).Where("role_id=?", role.ID).Count(&adminCount).Error
	if err != nil {
		return err
	}
	if adminCount == 1 {
		return ErrModifyingOnlyAdmin
	}
	return nil
}

// DeleteUser deletes the given user. To ensure that there is always at least
// one user account with the Admin role, this function will refuse to delete
// the last Admin.
func DeleteUser(id int64) error {
	existing, err := GetUser(id)
	if err != nil {
		return err
	}
	// If the user is an admin, we need to verify that it's not the last one.
	if existing.Role.Slug == RoleAdmin {
		err = EnsureEnoughAdmins()
		if err != nil {
			return err
		}
	}
	campaigns, err := GetCampaigns(id)
	if err != nil {
		return err
	}
	// Delete the campaigns
	log.Infof("Deleting campaigns for user ID %d", id)
	for _, campaign := range campaigns {
		err = DeleteCampaign(campaign.Id)
		if err != nil {
			return err
		}
	}
	log.Infof("Deleting pages for user ID %d", id)
	// Delete the landing pages
	pages, err := GetPages(id)
	if err != nil {
		return err
	}
	for _, page := range pages {
		err = DeletePage(page.Id, id)
		if err != nil {
			return err
		}
	}
	// Delete the templates
	log.Infof("Deleting templates for user ID %d", id)
	templates, err := GetTemplates(id)
	if err != nil {
		return err
	}
	for _, template := range templates {
		err = DeleteTemplate(template.Id, id)
		if err != nil {
			return err
		}
	}
	// Delete the groups
	log.Infof("Deleting groups for user ID %d", id)
	groups, err := GetGroups(id)
	if err != nil {
		return err
	}
	for _, group := range groups {
		err = DeleteGroup(&group)
		if err != nil {
			return err
		}
	}
	// Delete the sending profiles
	log.Infof("Deleting sending profiles for user ID %d", id)
	profiles, err := GetSMTPs(id)
	if err != nil {
		return err
	}
	for _, profile := range profiles {
		err = DeleteSMTP(profile.Id, id)
		if err != nil {
			return err
		}
	}
	// Finally, delete the user
	err = db.Where("id=?", id).Delete(&User{}).Error
	return err
}
<|endoftext|>


<commit_before>package panda

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.net/proxy"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type HTTPMeetingPlace struct {
	TorAddress string
	URL        string
}

const payloadBytes = 1 << 15

func (hmp *HTTPMeetingPlace) Padding() int {
	return payloadBytes
}

func (hmp *HTTPMeetingPlace) attemptExchange(log func(string, ...interface{}), id, message []byte) (*http.Response, []byte, error) {
	serverURL, err := url.Parse(hmp.URL)
	if err != nil {
		return nil, nil, err
	}

	dialer, err := proxy.SOCKS5("tcp", hmp.TorAddress, nil, proxy.Direct)
	if err != nil {
		return nil, nil, err
	}

	host := serverURL.Host
	if strings.IndexRune(host, ':') == -1 {
		if serverURL.Scheme == "https" {
			host += ":443"
		} else {
			host += ":80"
		}
	}
	hostname, _, err := net.SplitHostPort(host)
	if err != nil {
		return nil, nil, err
	}
	log("Connecting to %s via Tor", host)
	rawConn, err := dialer.Dial("tcp", host)
	if err != nil {
		return nil, nil, err
	}

	var conn net.Conn
	if serverURL.Scheme == "https" {
		tlsConn := tls.Client(rawConn, &tls.Config{
			ServerName: hostname,
		})
		log("Starting TLS handshake with %s", host)
		if err := tlsConn.Handshake(); err != nil {
			rawConn.Close()
			log("TLS handshake to %s failed: %s", host, err)
			return nil, nil, err
		}
		conn = tlsConn
	} else {
		conn = rawConn
	}
	defer conn.Close()

	body := bytes.NewReader(message)
	request, err := http.NewRequest("POST", hmp.URL+"/"+hex.EncodeToString(id), body)
	if err != nil {
		return nil, nil, err
	}

	request.ContentLength = int64(len(message))
	request.Header.Add("Content-Type", "application/octet-stream")

	request.Write(conn)
	log("Request sent to %s. Awaiting reply", host)

	response, err := http.ReadResponse(bufio.NewReader(conn), request)

	var responseBody []byte
	if response.Body != nil {
		r := &io.LimitedReader{R: response.Body, N: payloadBytes + 1}
		responseBody, err = ioutil.ReadAll(r)
	}

	return response, responseBody, err
}

func (hmp *HTTPMeetingPlace) Exchange(log func(string, ...interface{}), id, message []byte, shutdown chan struct{}) ([]byte, error) {
	delay := 15 * time.Second
	for {
		response, body, err := hmp.attemptExchange(log, id, message)
		if err != nil {
			log("PANDA exchange failed. Will try again in %s: %s", delay, err)
			goto Sleep
		}

		switch response.StatusCode {
		case 409:
			return nil, errors.New("The transaction failed because this key has been used recently by two other parties")
		case 204:
			log("PANDA exchange waiting for other party. Will try again in %s", delay)
			goto Sleep
		case 200:
			if len(body) > payloadBytes {
				return nil, errors.New("Reply from server is too large")
			}
			return body, nil
		default:
			log("Request resulted in unexpected HTTP status %d. Will try again in %s", response.StatusCode, delay)
		}

	Sleep:
		select {
		case <-shutdown:
			return nil, ShutdownErr
		case <-time.After(delay):
			delay *= 2
			if delay > time.Hour {
				delay = time.Hour
			}
		}
	}

	panic("unreachable")
}
<commit_msg>Fix nil-pointer defer in the case of an HTTP error from the PANDA server.<commit_after>package panda

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.net/proxy"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type HTTPMeetingPlace struct {
	TorAddress string
	URL        string
}

const payloadBytes = 1 << 15

func (hmp *HTTPMeetingPlace) Padding() int {
	return payloadBytes
}

func (hmp *HTTPMeetingPlace) attemptExchange(log func(string, ...interface{}), id, message []byte) (*http.Response, []byte, error) {
	serverURL, err := url.Parse(hmp.URL)
	if err != nil {
		return nil, nil, err
	}

	dialer, err := proxy.SOCKS5("tcp", hmp.TorAddress, nil, proxy.Direct)
	if err != nil {
		return nil, nil, err
	}

	host := serverURL.Host
	if strings.IndexRune(host, ':') == -1 {
		if serverURL.Scheme == "https" {
			host += ":443"
		} else {
			host += ":80"
		}
	}
	hostname, _, err := net.SplitHostPort(host)
	if err != nil {
		return nil, nil, err
	}
	log("Connecting to %s via Tor", host)
	rawConn, err := dialer.Dial("tcp", host)
	if err != nil {
		return nil, nil, err
	}

	var conn net.Conn
	if serverURL.Scheme == "https" {
		tlsConn := tls.Client(rawConn, &tls.Config{
			ServerName: hostname,
		})
		log("Starting TLS handshake with %s", host)
		if err := tlsConn.Handshake(); err != nil {
			rawConn.Close()
			log("TLS handshake to %s failed: %s", host, err)
			return nil, nil, err
		}
		conn = tlsConn
	} else {
		conn = rawConn
	}
	defer conn.Close()

	body := bytes.NewReader(message)
	request, err := http.NewRequest("POST", hmp.URL+"/"+hex.EncodeToString(id), body)
	if err != nil {
		return nil, nil, err
	}

	request.ContentLength = int64(len(message))
	request.Header.Add("Content-Type", "application/octet-stream")

	request.Write(conn)
	log("Request sent to %s. Awaiting reply", host)

	response, err := http.ReadResponse(bufio.NewReader(conn), request)

	if err != nil {
		return nil, nil, err
	}

	var responseBody []byte
	if response.Body != nil {
		r := &io.LimitedReader{R: response.Body, N: payloadBytes + 1}
		responseBody, err = ioutil.ReadAll(r)
	}

	return response, responseBody, err
}

func (hmp *HTTPMeetingPlace) Exchange(log func(string, ...interface{}), id, message []byte, shutdown chan struct{}) ([]byte, error) {
	delay := 15 * time.Second
	for {
		response, body, err := hmp.attemptExchange(log, id, message)
		if err != nil {
			log("PANDA exchange failed. Will try again in %s: %s", delay, err)
			goto Sleep
		}

		switch response.StatusCode {
		case 409:
			return nil, errors.New("The transaction failed because this key has been used recently by two other parties")
		case 204:
			log("PANDA exchange waiting for other party. Will try again in %s", delay)
			goto Sleep
		case 200:
			if len(body) > payloadBytes {
				return nil, errors.New("Reply from server is too large")
			}
			return body, nil
		default:
			log("Request resulted in unexpected HTTP status %d. Will try again in %s", response.StatusCode, delay)
		}

	Sleep:
		select {
		case <-shutdown:
			return nil, ShutdownErr
		case <-time.After(delay):
			delay *= 2
			if delay > time.Hour {
				delay = time.Hour
			}
		}
	}

	panic("unreachable")
}
<|endoftext|>


<commit_before>package cluster

import (
	"flag"
	"fmt"
	"log"
	"sync"
	"testing"
	"time"
)

var (
	flagLocalPort  = 0
	flagRemoteHost = ""
	flagRemotePort = 0
)

func init() {
	flag.IntVar(&flagLocalPort, "lport", flagLocalPort, "Local port")
	flag.StringVar(&flagRemoteHost, "rhost", flagRemoteHost, "Remote host")
	flag.IntVar(&flagRemotePort, "rport", flagRemotePort, "Remote port")
	flag.Parse()
}

func ExampleBroadcast() {
	// Start two nodes on a randomly chosen port.
	n1, err := New("localhost:0")
	if err != nil {
		log.Fatal(err)
	}

	n2, err := New("localhost:0")
	if err != nil {
		log.Fatal(err)
	}

	// Make the two nodes aware of each other.
	if err := n1.Add(n2.Addr().String()); err != nil {
		log.Fatalf("Could not connect node 1 to node 2: %s", err)
	}

	// Wait for the remote to be added and then broadcast
	// a message from node 1.
	n1.RemoteAdded(func(_ Remote) {
		n1.Broadcast([]byte("Hello, world!"))
	})

	// Receive the message in node 2's inbox and print.
	m := <-n2.Inbox
	fmt.Println(string(m.Payload))

	// Output:
	// Hello, world!
}

// Tests ungraceful disconnection by killing the TCP listener for one of
// the nodes. The outcome should be that the other node (eventually) picks
// up on it and unlearns the bad remote.
func TestUngraceful(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1.tcp.Close()
	time.Sleep(6 * time.Second)
	if len(n2.remotes) > 0 {
		t.Fatalf("node '%s' still has remotes: %s", n2, n2.remotes)
	}
}

// Tests that two nodes can send and receive messages to each other.
func TestMessages(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1mesg, n2mesg := "from node1", "from node2"

	n1.Broadcast([]byte(n1mesg))
	n2.Broadcast([]byte(n2mesg))

	m1 := <-n1.Inbox
	m2 := <-n2.Inbox

	if string(m1.Payload) != n2mesg {
		t.Fatalf("node 1 should have received '%s' but got '%s'.",
			n2mesg, string(m1.Payload))
	}
	if string(m2.Payload) != n1mesg {
		t.Fatalf("node 2 should have received '%s' but got '%s'.",
			n1mesg, string(m2.Payload))
	}
}

// Tests the property that closing a remote on one node kills the
// connection on both sides.
func TestGracefulClose(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)
	n1.Close()

	time.Sleep(1 * time.Second)
	assertNoRemotes(t, n1)
	assertNoRemotes(t, n2)
}

// Tests the property that closing a remote on one node kills the
// connection on both sides.
func TestGracefulRemoteClose(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1ton2 := n1.Remotes()[0]
	n1.CloseRemote(n1ton2)

	time.Sleep(1 * time.Second)
	assertNoRemotes(t, n1)
	assertNoRemotes(t, n2)
}

// The test fails if either node has any remotes.
// This is intended to test cases when a connection has been closed.
func assertNoRemotes(t *testing.T, n *Node) {
	if len(n.remotes) > 0 {
		t.Fatalf("node '%s' still has remotes: %s", n, n.remotes)
	}
	if len(n.history) > 0 {
		t.Fatalf("node '%s' still has a history of remotes: %s", n, n.history)
	}
}

// twoNodes creates two end points, connects them and waits until each
// has acknowledged the other. Any deviation causes the test to fail.
func twoNodes(t *testing.T) (*Node, *Node) {
	n1, err := newNode("localhost:0", false,
		10*time.Second, 5*time.Second, 5*time.Second)
	if err != nil {
		t.Fatalf("Could not make first node: %s", err)
	}

	n2, err := newNode("localhost:0", false,
		10*time.Second, 5*time.Second, 5*time.Second)
	if err != nil {
		t.Fatalf("Could not make second node: %s", err)
	}

	// Use callbacks to tell us when n2 has been added to n1 and when
	// n1 has been added to n2.
	wg := new(sync.WaitGroup)
	wg.Add(2)
	waitForRemote := func(n *Node) func(Remote) {
		return func(r Remote) {
			if r.equal(remote(n.Addr())) {
				wg.Done()
			}
		}
	}
	n1.RemoteAdded(waitForRemote(n2))
	n2.RemoteAdded(waitForRemote(n1))

	if err := n1.Add(n2.Addr().String()); err != nil {
		t.Fatalf("Could not connect node 1 to node 2: %s", err)
	}

	// If the remotes don't sync after 1 second, then assume they never will.
	done := make(chan struct{})
	go func() {
		wg.Wait()
		done <- struct{}{}
	}()
	select {
	case <-done:
		// all is well!
	case <-time.After(1 * time.Second):
		t.Fatal("Nodes have not synced after 1 second.")
	}

	// Don't run those callbacks any more.
	n1.RemoteAdded(nil)
	n2.RemoteAdded(nil)
	return n1, n2
}

func isDebug() bool {
	return flagLocalPort > 0 && len(flagRemoteHost) > 0 && flagRemotePort > 0
}

// This is a special debugging test that is run when the right flags are given
// on the command line.
func TestCluster(t *testing.T) {
	if !isDebug() {
		return
	}

	node, err := New(fmt.Sprintf("localhost:%d", flagLocalPort))
	if err != nil {
		t.Fatal(err)
	}

	node.SetReconnectInterval(10 * time.Second)
	node.SetHealthyInterval(10 * time.Second)

	time.Sleep(1 * time.Second)

	raddr := fmt.Sprintf("%s:%d", flagRemoteHost, flagRemotePort)
	if err := node.Add(raddr); err != nil {
		log.Println(err)
	}

	go func() {
		for msg := range node.Inbox {
			fmt.Printf("received: %s\n", string(msg.Payload))
		}
	}()
	node.RemoteAdded(func(r Remote) {
		fmt.Println("Broadcasting...")
		node.Broadcast([]byte("wat wat in the butt. do it in my butt."))
	})

	select {}
}
<commit_msg>enable debugging in debug test<commit_after>package cluster

import (
	"flag"
	"fmt"
	"log"
	"sync"
	"testing"
	"time"
)

var (
	flagLocalPort  = 0
	flagRemoteHost = ""
	flagRemotePort = 0
)

func init() {
	flag.IntVar(&flagLocalPort, "lport", flagLocalPort, "Local port")
	flag.StringVar(&flagRemoteHost, "rhost", flagRemoteHost, "Remote host")
	flag.IntVar(&flagRemotePort, "rport", flagRemotePort, "Remote port")
	flag.Parse()
}

func ExampleBroadcast() {
	// Start two nodes on a randomly chosen port.
	n1, err := New("localhost:0")
	if err != nil {
		log.Fatal(err)
	}

	n2, err := New("localhost:0")
	if err != nil {
		log.Fatal(err)
	}

	// Make the two nodes aware of each other.
	if err := n1.Add(n2.Addr().String()); err != nil {
		log.Fatalf("Could not connect node 1 to node 2: %s", err)
	}

	// Wait for the remote to be added and then broadcast
	// a message from node 1.
	n1.RemoteAdded(func(_ Remote) {
		n1.Broadcast([]byte("Hello, world!"))
	})

	// Receive the message in node 2's inbox and print.
	m := <-n2.Inbox
	fmt.Println(string(m.Payload))

	// Output:
	// Hello, world!
}

// Tests ungraceful disconnection by killing the TCP listener for one of
// the nodes. The outcome should be that the other node (eventually) picks
// up on it and unlearns the bad remote.
func TestUngraceful(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1.tcp.Close()
	time.Sleep(6 * time.Second)
	if len(n2.remotes) > 0 {
		t.Fatalf("node '%s' still has remotes: %s", n2, n2.remotes)
	}
}

// Tests that two nodes can send and receive messages to each other.
func TestMessages(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1mesg, n2mesg := "from node1", "from node2"

	n1.Broadcast([]byte(n1mesg))
	n2.Broadcast([]byte(n2mesg))

	m1 := <-n1.Inbox
	m2 := <-n2.Inbox

	if string(m1.Payload) != n2mesg {
		t.Fatalf("node 1 should have received '%s' but got '%s'.",
			n2mesg, string(m1.Payload))
	}
	if string(m2.Payload) != n1mesg {
		t.Fatalf("node 2 should have received '%s' but got '%s'.",
			n1mesg, string(m2.Payload))
	}
}

// Tests the property that closing a remote on one node kills the
// connection on both sides.
func TestGracefulClose(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)
	n1.Close()

	time.Sleep(1 * time.Second)
	assertNoRemotes(t, n1)
	assertNoRemotes(t, n2)
}

// Tests the property that closing a remote on one node kills the
// connection on both sides.
func TestGracefulRemoteClose(t *testing.T) {
	if isDebug() {
		return
	}
	n1, n2 := twoNodes(t)

	n1ton2 := n1.Remotes()[0]
	n1.CloseRemote(n1ton2)

	time.Sleep(1 * time.Second)
	assertNoRemotes(t, n1)
	assertNoRemotes(t, n2)
}

// The test fails if either node has any remotes.
// This is intended to test cases when a connection has been closed.
func assertNoRemotes(t *testing.T, n *Node) {
	if len(n.remotes) > 0 {
		t.Fatalf("node '%s' still has remotes: %s", n, n.remotes)
	}
	if len(n.history) > 0 {
		t.Fatalf("node '%s' still has a history of remotes: %s", n, n.history)
	}
}

// twoNodes creates two end points, connects them and waits until each
// has acknowledged the other. Any deviation causes the test to fail.
func twoNodes(t *testing.T) (*Node, *Node) {
	n1, err := newNode("localhost:0", false,
		10*time.Second, 5*time.Second, 5*time.Second)
	if err != nil {
		t.Fatalf("Could not make first node: %s", err)
	}

	n2, err := newNode("localhost:0", false,
		10*time.Second, 5*time.Second, 5*time.Second)
	if err != nil {
		t.Fatalf("Could not make second node: %s", err)
	}

	// Use callbacks to tell us when n2 has been added to n1 and when
	// n1 has been added to n2.
	wg := new(sync.WaitGroup)
	wg.Add(2)
	waitForRemote := func(n *Node) func(Remote) {
		return func(r Remote) {
			if r.equal(remote(n.Addr())) {
				wg.Done()
			}
		}
	}
	n1.RemoteAdded(waitForRemote(n2))
	n2.RemoteAdded(waitForRemote(n1))

	if err := n1.Add(n2.Addr().String()); err != nil {
		t.Fatalf("Could not connect node 1 to node 2: %s", err)
	}

	// If the remotes don't sync after 1 second, then assume they never will.
	done := make(chan struct{})
	go func() {
		wg.Wait()
		done <- struct{}{}
	}()
	select {
	case <-done:
		// all is well!
	case <-time.After(1 * time.Second):
		t.Fatal("Nodes have not synced after 1 second.")
	}

	// Don't run those callbacks any more.
	n1.RemoteAdded(nil)
	n2.RemoteAdded(nil)
	return n1, n2
}

func isDebug() bool {
	return flagLocalPort > 0 && len(flagRemoteHost) > 0 && flagRemotePort > 0
}

// This is a special debugging test that is run when the right flags are given
// on the command line.
func TestCluster(t *testing.T) {
	if !isDebug() {
		return
	}

	node, err := New(fmt.Sprintf("localhost:%d", flagLocalPort))
	if err != nil {
		t.Fatal(err)
	}
	node.SetDebug(true)

	node.SetReconnectInterval(10 * time.Second)
	node.SetHealthyInterval(10 * time.Second)

	time.Sleep(1 * time.Second)

	raddr := fmt.Sprintf("%s:%d", flagRemoteHost, flagRemotePort)
	if err := node.Add(raddr); err != nil {
		log.Println(err)
	}

	go func() {
		for msg := range node.Inbox {
			fmt.Printf("received: %s\n", string(msg.Payload))
		}
	}()
	node.RemoteAdded(func(r Remote) {
		fmt.Println("Broadcasting...")
		node.Broadcast([]byte("wat wat in the butt. do it in my butt."))
	})

	select {}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"github.com/hashicorp/go-hclog"
	"go.opencensus.io/examples/exporter"
	"go.opencensus.io/stats/view"

	"github.com/seanpfeifer/coffeebeanbot"
	"github.com/seanpfeifer/coffeebeanbot/metrics"
)

const (
	defaultConfigFile  = "cfg.json"
	defaultSecretsFile = "./secrets/discord.json"

	// This should never be less than 60s, or you risk having issues with Stackdriver / a very large bill.
	stackdriverReportingInterval = 60 * time.Second
	stackdriverMetricPrefix      = "cbb"
)

type metricsOptions struct {
	StackdriverMetrics bool
	PrintMetrics       bool
}

func main() {
	logger := createLogger("cbb")
	defer logger.Info("------- BOT SHUTDOWN -------")

	// Parse config + secrets file paths
	configPath := flag.String("cfg", defaultConfigFile, "the config to start the bot with")
	secretsPath := flag.String("secrets", defaultSecretsFile, "the secrets file to load")
	// Also parse metrics options
	var opts metricsOptions
	flag.BoolVar(&opts.StackdriverMetrics, "stackdriver", false, "enables Stackdriver metrics output every "+stackdriverReportingInterval.String())
	flag.BoolVar(&opts.PrintMetrics, "stdoutMetrics", false, "enables printing metrics to stdout every 10s")
	flag.Parse()

	// Load config
	cfg, err := coffeebeanbot.LoadConfigFile(*configPath)
	if coffeebeanbot.LogIfError(logger, err, "Error loading config") {
		return
	}

	// Load secrets
	secrets, err := coffeebeanbot.LoadSecretsFile(*secretsPath)
	if coffeebeanbot.LogIfError(logger, err, "Error loading secrets") {
		return
	}

	// Set up metrics
	stopMetrics, err := setupMetricsExporter(opts)
	if coffeebeanbot.LogIfError(logger, err, "Error setting up metrics exporter") {
		return
	}
	defer stopMetrics()

	recorder, err := metrics.NewRecorder()
	if coffeebeanbot.LogIfError(logger, err, "Error creating metrics recorder") {
		return
	}

	// Start bot
	bot := coffeebeanbot.NewBot(*cfg, *secrets, logger, *recorder)
	err = bot.Start()
	coffeebeanbot.LogIfError(logger, err, "Error starting bot")
}

// setupMetricsExporter sets up the OpenCensus metrics exporter, returning a "stopMetrics" func and an error if one occurs.
// Users are expected to call the returned function when the metrics exporter should stop, eg on a "defer" prior to app exit.
func setupMetricsExporter(opts metricsOptions) (func(), error) {
	noop := func() {}

	if opts.PrintMetrics {
		view.RegisterExporter(&exporter.PrintExporter{})
		// No specific stopMetrics func to use here - either return the Stackdriver one or noop at end of func
	}

	if opts.StackdriverMetrics {
		exporter, err := stackdriver.NewExporter(stackdriver.Options{
			ReportingInterval: stackdriverReportingInterval,
			MetricPrefix:      stackdriverMetricPrefix,
		})
		if err != nil {
			return noop, err
		}

		if err = exporter.StartMetricsExporter(); err != nil {
			return noop, err
		}

		// No issues, let's return our stopMetrics func since there are no others to worry about
		return func() {
			exporter.StopMetricsExporter()
			exporter.Flush()
		}, nil
	}

	return noop, nil
}

type hclogWrapper struct {
	hclog.Logger
}

func (h *hclogWrapper) Named(name string) coffeebeanbot.Logger {
	return &hclogWrapper{h.Logger.Named(name)}
}

func createLogger(name string) coffeebeanbot.Logger {
	logger := hclog.New(&hclog.LoggerOptions{
		Name:  name,
		Level: hclog.Info,
	})

	return &hclogWrapper{logger}
}

// What follows is an example of replacing the logger with Uber's `zap` library.
/*
type zapWrapper struct {
	*zap.SugaredLogger
}

func (z *zapWrapper) Info(msg string, kvPairs ...interface{}) {
	z.SugaredLogger.Infow(msg, kvPairs...)
}

func (z *zapWrapper) Error(msg string, kvPairs ...interface{}) {
	z.SugaredLogger.Errorw(msg, kvPairs...)
}

func (z *zapWrapper) Named(name string) coffeebeanbot.Logger {
	return &zapWrapper{z.SugaredLogger.Named(name)}
}

func createZapLogger(name string) coffeebeanbot.Logger {
	logger, _ := zap.NewProduction()

	return &zapWrapper{logger.Named(name).Sugar()}
}*/
<commit_msg>Updated default cfg + secrets file in bot main<commit_after>package main

import (
	"flag"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"github.com/hashicorp/go-hclog"
	"go.opencensus.io/examples/exporter"
	"go.opencensus.io/stats/view"

	"github.com/seanpfeifer/coffeebeanbot"
	"github.com/seanpfeifer/coffeebeanbot/metrics"
)

const (
	defaultConfigFile  = "cfg.toml"
	defaultSecretsFile = "./secrets/discord.toml"

	// This should never be less than 60s, or you risk having issues with Stackdriver / a very large bill.
	stackdriverReportingInterval = 60 * time.Second
	stackdriverMetricPrefix      = "cbb"
)

type metricsOptions struct {
	StackdriverMetrics bool
	PrintMetrics       bool
}

func main() {
	logger := createLogger("cbb")
	defer logger.Info("------- BOT SHUTDOWN -------")

	// Parse config + secrets file paths
	configPath := flag.String("cfg", defaultConfigFile, "the config to start the bot with")
	secretsPath := flag.String("secrets", defaultSecretsFile, "the secrets file to load")
	// Also parse metrics options
	var opts metricsOptions
	flag.BoolVar(&opts.StackdriverMetrics, "stackdriver", false, "enables Stackdriver metrics output every "+stackdriverReportingInterval.String())
	flag.BoolVar(&opts.PrintMetrics, "stdoutMetrics", false, "enables printing metrics to stdout every 10s")
	flag.Parse()

	// Load config
	cfg, err := coffeebeanbot.LoadConfigFile(*configPath)
	if coffeebeanbot.LogIfError(logger, err, "Error loading config") {
		return
	}

	// Load secrets
	secrets, err := coffeebeanbot.LoadSecretsFile(*secretsPath)
	if coffeebeanbot.LogIfError(logger, err, "Error loading secrets") {
		return
	}

	// Set up metrics
	stopMetrics, err := setupMetricsExporter(opts)
	if coffeebeanbot.LogIfError(logger, err, "Error setting up metrics exporter") {
		return
	}
	defer stopMetrics()

	recorder, err := metrics.NewRecorder()
	if coffeebeanbot.LogIfError(logger, err, "Error creating metrics recorder") {
		return
	}

	// Start bot
	bot := coffeebeanbot.NewBot(*cfg, *secrets, logger, *recorder)
	err = bot.Start()
	coffeebeanbot.LogIfError(logger, err, "Error starting bot")
}

// setupMetricsExporter sets up the OpenCensus metrics exporter, returning a "stopMetrics" func and an error if one occurs.
// Users are expected to call the returned function when the metrics exporter should stop, eg on a "defer" prior to app exit.
func setupMetricsExporter(opts metricsOptions) (func(), error) {
	noop := func() {}

	if opts.PrintMetrics {
		view.RegisterExporter(&exporter.PrintExporter{})
		// No specific stopMetrics func to use here - either return the Stackdriver one or noop at end of func
	}

	if opts.StackdriverMetrics {
		exporter, err := stackdriver.NewExporter(stackdriver.Options{
			ReportingInterval: stackdriverReportingInterval,
			MetricPrefix:      stackdriverMetricPrefix,
		})
		if err != nil {
			return noop, err
		}

		if err = exporter.StartMetricsExporter(); err != nil {
			return noop, err
		}

		// No issues, let's return our stopMetrics func since there are no others to worry about
		return func() {
			exporter.StopMetricsExporter()
			exporter.Flush()
		}, nil
	}

	return noop, nil
}

type hclogWrapper struct {
	hclog.Logger
}

func (h *hclogWrapper) Named(name string) coffeebeanbot.Logger {
	return &hclogWrapper{h.Logger.Named(name)}
}

func createLogger(name string) coffeebeanbot.Logger {
	logger := hclog.New(&hclog.LoggerOptions{
		Name:  name,
		Level: hclog.Info,
	})

	return &hclogWrapper{logger}
}

// What follows is an example of replacing the logger with Uber's `zap` library.
/*
type zapWrapper struct {
	*zap.SugaredLogger
}

func (z *zapWrapper) Info(msg string, kvPairs ...interface{}) {
	z.SugaredLogger.Infow(msg, kvPairs...)
}

func (z *zapWrapper) Error(msg string, kvPairs ...interface{}) {
	z.SugaredLogger.Errorw(msg, kvPairs...)
}

func (z *zapWrapper) Named(name string) coffeebeanbot.Logger {
	return &zapWrapper{z.SugaredLogger.Named(name)}
}

func createZapLogger(name string) coffeebeanbot.Logger {
	logger, _ := zap.NewProduction()

	return &zapWrapper{logger.Named(name).Sugar()}
}*/
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
    "github.com/exercism/cli/debug"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if usrCfg.GetString("token") == "" {
		return fmt.Errorf(msgWelcomePleaseConfigure, config.SettingsURL(usrCfg.GetString("apibaseurl")), BinaryName)
	}
	if usrCfg.GetString("workspace") == "" || usrCfg.GetString("apibaseurl") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid != "" && slug != "" || uuid == slug {
        debug.Printf("need to go in exercism website ,open the track and choose the exercise, \nthen copy the available link for download.\n ")
		return errors.New("need an --exercise name or a solution --uuid")
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	metadata := workspace.ExerciseMetadata{
		AutoApprove: payload.Solution.Exercise.AutoApprove,
		Track:       payload.Solution.Exercise.Track.ID,
		Team:        payload.Solution.Team.Slug,
		Exercise:    payload.Solution.Exercise.ID,
		ID:          payload.Solution.ID,
		URL:         payload.Solution.URL,
		Handle:      payload.Solution.User.Handle,
		IsRequester: payload.Solution.User.IsRequester,
	}

	root := usrCfg.GetString("workspace")
	if metadata.Team != "" {
		root = filepath.Join(root, "teams", metadata.Team)
	}
	if !metadata.IsRequester {
		root = filepath.Join(root, "users", metadata.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: metadata.Track,
		Slug:  metadata.Exercise,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = metadata.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.

		// Work around a path bug due to an early design decision (later reversed) to
		// allow numeric suffixes for exercise directories, allowing people to have
		// multiple parallel versions of an exercise.
		pattern := fmt.Sprintf(`\A.*[/\\]%s-\d*/`, metadata.Exercise)
		rgxNumericSuffix := regexp.MustCompile(pattern)
		if rgxNumericSuffix.MatchString(file) {
			file = string(rgxNumericSuffix.ReplaceAll([]byte(file), []byte("")))
		}

		// Rewrite paths submitted with an older, buggy client where the Windows path is being treated as part of the filename.
		file = strings.Replace(file, "\\", "/", -1)

		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(metadata.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(metadata.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", metadata.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<commit_msg>Revert "Add gofmt failure to prove travis fails"<commit_after>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if usrCfg.GetString("token") == "" {
		return fmt.Errorf(msgWelcomePleaseConfigure, config.SettingsURL(usrCfg.GetString("apibaseurl")), BinaryName)
	}
	if usrCfg.GetString("workspace") == "" || usrCfg.GetString("apibaseurl") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid != "" && slug != "" || uuid == slug {
		return errors.New("need an --exercise name or a solution --uuid")
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	metadata := workspace.ExerciseMetadata{
		AutoApprove: payload.Solution.Exercise.AutoApprove,
		Track:       payload.Solution.Exercise.Track.ID,
		Team:        payload.Solution.Team.Slug,
		Exercise:    payload.Solution.Exercise.ID,
		ID:          payload.Solution.ID,
		URL:         payload.Solution.URL,
		Handle:      payload.Solution.User.Handle,
		IsRequester: payload.Solution.User.IsRequester,
	}

	root := usrCfg.GetString("workspace")
	if metadata.Team != "" {
		root = filepath.Join(root, "teams", metadata.Team)
	}
	if !metadata.IsRequester {
		root = filepath.Join(root, "users", metadata.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: metadata.Track,
		Slug:  metadata.Exercise,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = metadata.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.

		// Work around a path bug due to an early design decision (later reversed) to
		// allow numeric suffixes for exercise directories, allowing people to have
		// multiple parallel versions of an exercise.
		pattern := fmt.Sprintf(`\A.*[/\\]%s-\d*/`, metadata.Exercise)
		rgxNumericSuffix := regexp.MustCompile(pattern)
		if rgxNumericSuffix.MatchString(file) {
			file = string(rgxNumericSuffix.ReplaceAll([]byte(file), []byte("")))
		}

		// Rewrite paths submitted with an older, buggy client where the Windows path is being treated as part of the filename.
		file = strings.Replace(file, "\\", "/", -1)

		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(metadata.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(metadata.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", metadata.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<|endoftext|>


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

// Basic test cases for cgo.

package cgotest

/*
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>

#define SHIFT(x, y)  ((x)<<(y))
#define KILO SHIFT(1, 10)

enum E {
	Enum1 = 1,
	Enum2 = 2,
};

typedef unsigned char uuid_t[20];

void uuid_generate(uuid_t x) {
	x[0] = 0;
}

struct S {
	int x;
};

extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter);

enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; }

// issue 1222
typedef union {
	long align;
} xxpthread_mutex_t;

struct ibv_async_event {
	union {
		int x;
	} element;
};

struct ibv_context {
	xxpthread_mutex_t mutex;
};

int add(int x, int y) {
	return x+y;
};
*/
import "C"
import (
	"syscall"
	"testing"
	"unsafe"
)

const EINVAL = C.EINVAL /* test #define */

var KILO = C.KILO

func uuidgen() {
	var uuid C.uuid_t
	C.uuid_generate(&uuid[0])
}

func Strtol(s string, base int) (int, error) {
	p := C.CString(s)
	n, err := C.strtol(p, nil, C.int(base))
	C.free(unsafe.Pointer(p))
	return int(n), err
}

func Atol(s string) int {
	p := C.CString(s)
	n := C.atol(p)
	C.free(unsafe.Pointer(p))
	return int(n)
}

func testConst(t *testing.T) {
	C.myConstFunc(nil, 0, nil)
}

func testEnum(t *testing.T) {
	if C.Enum1 != 1 || C.Enum2 != 2 {
		t.Error("bad enum", C.Enum1, C.Enum2)
	}
}

func testAtol(t *testing.T) {
	l := Atol("123")
	if l != 123 {
		t.Error("Atol 123: ", l)
	}
}

func testErrno(t *testing.T) {
	p := C.CString("no-such-file")
	m := C.CString("r")
	f, err := C.fopen(p, m)
	C.free(unsafe.Pointer(p))
	C.free(unsafe.Pointer(m))
	if err == nil {
		C.fclose(f)
		t.Fatalf("C.fopen: should fail")
	}
	if err != syscall.ENOENT {
		t.Fatalf("C.fopen: unexpected error: %v", err)
	}
}

func testMultipleAssign(t *testing.T) {
	p := C.CString("234")
	n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
	if n != 0 || m != 234 {
		t.Fatal("Strtol x2: ", n, m)
	}
	C.free(unsafe.Pointer(p))
}

var (
	cuint  = (C.uint)(0)
	culong C.ulong
	cchar  C.char
)

type Context struct {
	ctx *C.struct_ibv_context
}

func benchCgoCall(b *testing.B) {
	const x = C.int(2)
	const y = C.int(3)
	for i := 0; i < b.N; i++ {
		C.add(x, y)
	}
}
<commit_msg>misc/cgo/test: fix darwin build<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Basic test cases for cgo.

package cgotest

/*
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>

#define SHIFT(x, y)  ((x)<<(y))
#define KILO SHIFT(1, 10)

enum E {
	Enum1 = 1,
	Enum2 = 2,
};

typedef unsigned char cgo_uuid_t[20];

void uuid_generate(cgo_uuid_t x) {
	x[0] = 0;
}

struct S {
	int x;
};

extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter);

enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; }

// issue 1222
typedef union {
	long align;
} xxpthread_mutex_t;

struct ibv_async_event {
	union {
		int x;
	} element;
};

struct ibv_context {
	xxpthread_mutex_t mutex;
};

int add(int x, int y) {
	return x+y;
};
*/
import "C"
import (
	"syscall"
	"testing"
	"unsafe"
)

const EINVAL = C.EINVAL /* test #define */

var KILO = C.KILO

func uuidgen() {
	var uuid C.cgo_uuid_t
	C.uuid_generate(&uuid[0])
}

func Strtol(s string, base int) (int, error) {
	p := C.CString(s)
	n, err := C.strtol(p, nil, C.int(base))
	C.free(unsafe.Pointer(p))
	return int(n), err
}

func Atol(s string) int {
	p := C.CString(s)
	n := C.atol(p)
	C.free(unsafe.Pointer(p))
	return int(n)
}

func testConst(t *testing.T) {
	C.myConstFunc(nil, 0, nil)
}

func testEnum(t *testing.T) {
	if C.Enum1 != 1 || C.Enum2 != 2 {
		t.Error("bad enum", C.Enum1, C.Enum2)
	}
}

func testAtol(t *testing.T) {
	l := Atol("123")
	if l != 123 {
		t.Error("Atol 123: ", l)
	}
}

func testErrno(t *testing.T) {
	p := C.CString("no-such-file")
	m := C.CString("r")
	f, err := C.fopen(p, m)
	C.free(unsafe.Pointer(p))
	C.free(unsafe.Pointer(m))
	if err == nil {
		C.fclose(f)
		t.Fatalf("C.fopen: should fail")
	}
	if err != syscall.ENOENT {
		t.Fatalf("C.fopen: unexpected error: %v", err)
	}
}

func testMultipleAssign(t *testing.T) {
	p := C.CString("234")
	n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
	if n != 0 || m != 234 {
		t.Fatal("Strtol x2: ", n, m)
	}
	C.free(unsafe.Pointer(p))
}

var (
	cuint  = (C.uint)(0)
	culong C.ulong
	cchar  C.char
)

type Context struct {
	ctx *C.struct_ibv_context
}

func benchCgoCall(b *testing.B) {
	const x = C.int(2)
	const y = C.int(3)
	for i := 0; i < b.N; i++ {
		C.add(x, y)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/spf13/cobra"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/util/i18n"
)

var (
	get_long = templates.LongDesc(i18n.T(`
	Display one or many resources.` + validResources))

	get_example = templates.Examples(i18n.T(`
	# Get all clusters in a state store
	kops get clusters

	# Get a cluster
	kops get cluster k8s-cluster.example.com

	# Get a cluster YAML cluster spec
	kops get cluster k8s-cluster.example.com -o yaml

	# Get an instancegroup
	kops get ig --name k8s-cluster.example.com nodes

	# Get a secret
	kops get secrets kube -oplaintext

	# Get the admin password for a cluster
	kops get secrets admin -oplaintext`))

	get_short = i18n.T(`Get one or many resources.`)
)

type GetOptions struct {
	output string
}

const (
	OutputYaml  = "yaml"
	OutputTable = "table"
	OutputJSON  = "json"
)

func NewCmdGet(f *util.Factory, out io.Writer) *cobra.Command {
	options := &GetOptions{}

	cmd := &cobra.Command{
		Use:        "get",
		SuggestFor: []string{"list"},
		Short:      get_short,
		Long:       get_long,
		Example:    get_example,
	}

	cmd.PersistentFlags().StringVarP(&options.output, "output", "o", OutputTable, "output format.  One of: table, yaml, json")

	// create subcommands
	cmd.AddCommand(NewCmdGetCluster(f, out, options))
	cmd.AddCommand(NewCmdGetFederations(f, out, options))
	cmd.AddCommand(NewCmdGetInstanceGroups(f, out, options))
	cmd.AddCommand(NewCmdGetSecrets(f, out, options))

	return cmd
}

type marshalFunc func(obj runtime.Object) ([]byte, error)

func marshalToWriter(obj runtime.Object, marshal marshalFunc, w io.Writer) error {
	b, err := marshal(obj)
	if err != nil {
		return err
	}
	_, err = w.Write(b)
	if err != nil {
		return fmt.Errorf("error writing to stdout: %v", err)
	}
	return nil
}

// obj must be a pointer to a marshalable object
func marshalYaml(obj runtime.Object) ([]byte, error) {
	y, err := api.ToVersionedYaml(obj)
	if err != nil {
		return nil, fmt.Errorf("error marshaling yaml: %v", err)
	}
	return y, nil
}

// obj must be a pointer to a marshalable object
func marshalJSON(obj runtime.Object) ([]byte, error) {
	j, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("error marshaling json: %v", err)
	}
	return j, nil
}
<commit_msg>updates<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/spf13/cobra"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/util/i18n"
)

var (
	get_long = templates.LongDesc(i18n.T(`
	Display one or many resources.` + validResources))

	get_example = templates.Examples(i18n.T(`
	# Get all clusters in a state store
	kops get clusters

	# Get a cluster
	kops get cluster k8s-cluster.example.com

	# Get a cluster YAML cluster spec
	kops get cluster k8s-cluster.example.com -o yaml

	# Get an instancegroup
	kops get ig --name k8s-cluster.example.com nodes

	# Get a secret
	kops get secrets kube -oplaintext

	# Get the admin password for a cluster
	kops get secrets admin -oplaintext`))

	get_short = i18n.T(`Get one or many resources.`)
)

type GetOptions struct {
	output      string
	clusterName string
}

const (
	OutputYaml  = "yaml"
	OutputTable = "table"
	OutputJSON  = "json"
)

func NewCmdGet(f *util.Factory, out io.Writer) *cobra.Command {
	options := &GetOptions{}

	cmd := &cobra.Command{
		Use:        "get",
		SuggestFor: []string{"list"},
		Short:      get_short,
		Long:       get_long,
		Example:    get_example,
	}

	cmd.PersistentFlags().StringVarP(&options.output, "output", "o", OutputTable, "output format.  One of: table, yaml, json")

	// create subcommands
	cmd.AddCommand(NewCmdGetCluster(f, out, options))
	cmd.AddCommand(NewCmdGetFederations(f, out, options))
	cmd.AddCommand(NewCmdGetInstanceGroups(f, out, options))
	cmd.AddCommand(NewCmdGetSecrets(f, out, options))

	return cmd
}

type marshalFunc func(obj runtime.Object) ([]byte, error)

func marshalToWriter(obj runtime.Object, marshal marshalFunc, w io.Writer) error {
	b, err := marshal(obj)
	if err != nil {
		return err
	}
	_, err = w.Write(b)
	if err != nil {
		return fmt.Errorf("error writing to stdout: %v", err)
	}
	return nil
}

// obj must be a pointer to a marshalable object
func marshalYaml(obj runtime.Object) ([]byte, error) {
	y, err := api.ToVersionedYaml(obj)
	if err != nil {
		return nil, fmt.Errorf("error marshaling yaml: %v", err)
	}
	return y, nil
}

// obj must be a pointer to a marshalable object
func marshalJSON(obj runtime.Object) ([]byte, error) {
	j, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("error marshaling json: %v", err)
	}
	return j, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/turingschool-examples/pie"
)

func main() {
	db := pie.NewDatabase()
}
<commit_msg>Basic command line tool setup.<commit_after>package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/turingschool-examples/pie"
)

func main() {
	// Parse command line flags.
	addr := flag.String("addr", ":19876", "bind address")
	flag.Parse()
	log.SetFlags(0)

	// Open database.
	db := pie.NewDatabase()

	// Initialize handler.
	h := pie.NewHandler(db)

	// Start HTTP handler.
	log.Printf("Listening on http://localhost%s", *addr)
	log.SetFlags(log.LstdFlags)
	http.ListenAndServe(*addr, h)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	. "github.com/gonium/gosdm630"
	. "github.com/gonium/gosdm630/meters"
	_ "github.com/gonium/gosdm630/meters/impl"
	latest "github.com/tcnksm/go-latest"
	cli "gopkg.in/urfave/cli.v1"
)

const (
	DEFAULT_METER_STORE_SECONDS = 120 * time.Second
)

func checkVersion() {
	githubTag := &latest.GithubTag{
		Owner:      "gonium",
		Repository: "gosdm630",
	}

	if res, err := latest.Check(githubTag, TAG); err == nil {
		if res.Outdated {
			log.Printf("updates available - please upgrade to ingress %s", res.Current)
		}
	}
}

func createMeters(deviceslice []string) map[uint8]*Meter {
	meters := make(map[uint8]*Meter)
	for _, meterdef := range deviceslice {
		splitdef := strings.Split(meterdef, ":")
		if len(splitdef) != 2 {
			log.Fatalf("Cannot parse device definition %s. See -h for help.", meterdef)
		}
		metertype, devid := splitdef[0], splitdef[1]
		id, err := strconv.Atoi(devid)
		if err != nil {
			log.Fatalf("Error parsing device id %s: %s. See -h for help.", meterdef, err.Error())
		}
		meter, err := NewMeterByType(metertype, uint8(id))
		if err != nil {
			log.Fatalf("Unknown meter type %s for device %d. See -h for help.", metertype, id)
		}
		meters[uint8(id)] = meter
	}
	return meters
}

func waitForSignal(signals ...os.Signal) {
	var wg sync.WaitGroup
	wg.Add(1)
	channel := make(chan os.Signal, 1)
	signal.Notify(channel, signals...)
	go func() {
		<-channel
		wg.Done()
	}()
	wg.Wait()
}

func meterHelp() string {
	var s string
	for _, c := range []ConnectionType{RS485, TCP} {
		s += fmt.Sprintf("\n\t\t\t\t%s", c.String())
		// s += fmt.Sprintf("\n\t\t\t\t%s", strings.Repeat("-", len(c.String())))

		types := make([]string, 0)
		for t, f := range Producers {
			p := f()
			if c != p.ConnectionType() {
				continue
			}

			types = append(types, t)
		}

		sort.Strings(types)

		for _, t := range types {
			f := Producers[t]
			p := f()
			s += fmt.Sprintf("\n\t\t\t\t %-9s%s", t, p.Description())
		}
	}
	return s
}

func main() {
	app := cli.NewApp()
	app.Name = "sdm"
	app.Usage = "SDM MODBUS daemon"
	app.Version = fmt.Sprintf("%s (https://github.com/gonium/gosdm/commit/%s)", TAG, HASH)
	app.HideVersion = true
	app.Flags = []cli.Flag{
		// general
		cli.StringFlag{
			Name:  "adapter, a",
			Value: "/dev/ttyUSB0",
			Usage: "MODBUS adapter - can be either serial RTU device (/dev/ttyUSB0) or TCP socket (localhost:502)",
		},
		cli.IntFlag{
			Name:  "comset, c",
			Value: ModbusComset9600_8N1,
			Usage: `Communication parameters:
			` + strconv.Itoa(ModbusComset2400_8N1) + `:  2400 baud, 8N1
			` + strconv.Itoa(ModbusComset9600_8N1) + `:  9600 baud, 8N1
			` + strconv.Itoa(ModbusComset19200_8N1) + `: 19200 baud, 8N1
			` + strconv.Itoa(ModbusComset2400_8E1) + `:  2400 baud, 8E1
			` + strconv.Itoa(ModbusComset9600_8E1) + `:  9600 baud, 8E1
			` + strconv.Itoa(ModbusComset19200_8E1) + `: 19200 baud, 8E1
			`,
		},
		cli.BoolFlag{
			Name:  "simulate, s",
			Usage: "Simulate MODBUS device for testing purposes",
		},
		cli.StringFlag{
			Name:  "devices, d",
			Value: "SDM:1",
			Usage: `MODBUS device type and ID to query, separated by comma.
			Valid types are:` + meterHelp() + `
			Example: -d JANITZA:1,SDM:22,DZG:23`,
		},
		cli.BoolFlag{
			Name:  "detect",
			Usage: "Detect MODBUS devices",
		},
		cli.StringFlag{
			Name:  "rate, r",
			Value: "1s",
			Usage: "Maximum update rate in seconds per message, 0 is unlimited",
			// Destination: &mqttRate,
		},
		cli.StringFlag{
			Name:  "idformat, f",
			Value: "Meter%d",
			Usage: `Meter id format. Determines meter id for REST and MQTT.
			Example: -f Meter%d
			The %d is replaced by the device ID`,
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "print verbose messages",
		},

		// http api
		cli.StringFlag{
			Name:  "url, u",
			Value: "localhost:8080",
			Usage: "REST API url. Use 0.0.0.0:8080 to accept incoming connections.",
		},

		// mqtt api
		cli.StringFlag{
			Name:  "broker, b",
			Value: "",
			Usage: "MQTT: Broker URI. ex: tcp://10.10.1.1:1883",
			// Destination: &mqttBroker,
		},
		cli.StringFlag{
			Name:  "topic, t",
			Value: "sdm",
			Usage: "MQTT: Base topic. Set empty to disable publishing.",
			// Destination: &mqttTopic,
		},
		cli.StringFlag{
			Name:  "user",
			Value: "",
			Usage: "MQTT: User (optional)",
			// Destination: &mqttUser,
		},
		cli.StringFlag{
			Name:  "password",
			Value: "",
			Usage: "MQTT: Password (optional)",
			// Destination: &mqttPassword,
		},
		cli.StringFlag{
			Name:  "clientid, i",
			Value: "sdm630",
			Usage: "MQTT: ClientID",
			// Destination: &mqttClientID,
		},
		cli.BoolFlag{
			Name:  "clean, l",
			Usage: "MQTT: Set Clean Session (default: false)",
			// Destination: &mqttCleanSession,
		},
		cli.IntFlag{
			Name:  "qos, q",
			Value: 0,
			Usage: "MQTT: Quality of Service 0,1,2",
			// Destination: &mqttQos,
		},
		cli.StringFlag{
			Name:  "homie",
			Value: "homie",
			Usage: "MQTT: Homie IoT discovery base topic (homieiot.github.io). Set empty to disable.",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.NArg() > 0 {
			log.Fatalf("Unexpected arguments: %v", c.Args())
		}

		log.Printf("sdm %s %s", TAG, HASH)
		go checkVersion()

		// Set unique ID format
		UniqueIdFormat = c.String("idformat")

		// Parse the devices parameter
		meters := createMeters(strings.Split(c.String("devices"), ","))

		rate, err := time.ParseDuration(c.String("rate"))
		if err != nil {
			log.Fatalf("Invalid rate %s", err)
		}

		// create ModbusEngine with status
		status := NewStatus(meters)
		qe := NewModbusEngine(
			c.String("adapter"),
			c.Int("comset"),
			c.Bool("simulate"),
			c.Bool("verbose"),
			status,
		)

		// detect command
		if c.Bool("detect") {
			qe.Scan()
			return
		}

		// scheduler and meter data channel
		scheduler, snips := SetupScheduler(meters, qe)

		// tee that broadcasts meter messages to multiple recipients
		tee := NewQuerySnipBroadcaster(snips)
		go tee.Run()

		// websocket hub
		hub := NewSocketHub(status)
		tee.AttachRunner(hub.Run)

		// MQTT client
		if c.String("broker") != "" {
			mqtt := NewMqttClient(
				c.String("broker"),
				c.String("topic"),
				c.String("user"),
				c.String("password"),
				c.String("clientid"),
				c.Int("qos"),
				c.Bool("clean"),
				c.Bool("verbose"))

			// homie needs to scan the bust, start it first
			if c.String("homie") != "" {
				homieRunner := HomieRunner{MqttClient: mqtt}
				homieRunner.Register(c.String("homie"), meters, qe)
				tee.AttachRunner(homieRunner.Run)
			}

			// start "normal" mqtt handler after homie setup
			if c.String("topic") != "" {
				mqttRunner := MqttRunner{MqttClient: mqtt}
				tee.AttachRunner(mqttRunner.Run)
			}
		}

		// MeasurementCache for REST API
		mc := NewMeasurementCache(
			meters,
			scheduler,
			DEFAULT_METER_STORE_SECONDS,
			c.Bool("verbose"),
		)
		tee.AttachRunner(mc.Run)

		// start the scheduler
		ctx, cancelScheduler := context.WithCancel(context.Background())
		go scheduler.Run(ctx, rate)

		// handle os signals and gracefully exit Run methods
		go func() {
			waitForSignal(os.Interrupt, os.Kill)
			log.Println("Received signal - stopping")
			cancelScheduler() // cancel scheduler
			select {          // wait for Run methods attached to tee to finish
			case <-tee.Done():
				log.Println("Stopped")
				os.Exit(0)
			}
		}()

		Run_httpd(
			mc,
			hub,
			status,
			c.String("url"),
		)
	}

	app.Run(os.Args)
}
<commit_msg>Log meter details (#116)<commit_after>package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	. "github.com/gonium/gosdm630"
	. "github.com/gonium/gosdm630/meters"
	_ "github.com/gonium/gosdm630/meters/impl"
	latest "github.com/tcnksm/go-latest"
	cli "gopkg.in/urfave/cli.v1"
)

const (
	DEFAULT_METER_STORE_SECONDS = 120 * time.Second
)

func checkVersion() {
	githubTag := &latest.GithubTag{
		Owner:      "gonium",
		Repository: "gosdm630",
	}

	if res, err := latest.Check(githubTag, TAG); err == nil {
		if res.Outdated {
			log.Printf("updates available - please upgrade to ingress %s", res.Current)
		}
	}
}

func createMeters(deviceslice []string) map[uint8]*Meter {
	meters := make(map[uint8]*Meter)
	for _, meterdef := range deviceslice {
		splitdef := strings.Split(meterdef, ":")
		if len(splitdef) != 2 {
			log.Fatalf("Cannot parse device definition %s. See -h for help.", meterdef)
		}
		metertype, devid := splitdef[0], splitdef[1]
		id, err := strconv.Atoi(devid)
		if err != nil {
			log.Fatalf("Error parsing device id %s: %s. See -h for help.", meterdef, err.Error())
		}
		meter, err := NewMeterByType(metertype, uint8(id))
		if err != nil {
			log.Fatalf("Unknown meter type %s for device %d. See -h for help.", metertype, id)
		}
		meters[uint8(id)] = meter
	}
	return meters
}

func waitForSignal(signals ...os.Signal) {
	var wg sync.WaitGroup
	wg.Add(1)
	channel := make(chan os.Signal, 1)
	signal.Notify(channel, signals...)
	go func() {
		<-channel
		wg.Done()
	}()
	wg.Wait()
}

func meterHelp() string {
	var s string
	for _, c := range []ConnectionType{RS485, TCP} {
		s += fmt.Sprintf("\n\t\t\t\t%s", c.String())
		// s += fmt.Sprintf("\n\t\t\t\t%s", strings.Repeat("-", len(c.String())))

		types := make([]string, 0)
		for t, f := range Producers {
			p := f()
			if c != p.ConnectionType() {
				continue
			}

			types = append(types, t)
		}

		sort.Strings(types)

		for _, t := range types {
			f := Producers[t]
			p := f()
			s += fmt.Sprintf("\n\t\t\t\t %-9s%s", t, p.Description())
		}
	}
	return s
}

func logMeterDetails(meters map[uint8]*Meter, qe *ModbusEngine) {
	for devid, meter := range meters {
		producer := meter.Producer
		log.Printf("#%d: %s (%s)", devid, producer.Description(), producer.ConnectionType())

		if sunspec, ok := producer.(SunSpecProducer); ok {
			op := sunspec.GetSunSpecCommonBlock()
			snip := QuerySnip{
				DeviceId:  meter.DeviceId,
				Operation: op,
			}

			if b, err := qe.Query(snip); err == nil {
				if descriptor, err := sunspec.DecodeSunSpecCommonBlock(b); err == nil {
					log.Printf("    Manufacturer: %s", descriptor.Manufacturer)
					log.Printf("    Model:        %s", descriptor.Model)
					log.Printf("    Version:      %s", descriptor.Version)
					log.Printf("    Serial:       %s", descriptor.Serial)
				} else {
					log.Println(err)
				}
			} else {
				log.Println(err)
			}
		}
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "sdm"
	app.Usage = "SDM MODBUS daemon"
	app.Version = fmt.Sprintf("%s (https://github.com/gonium/gosdm/commit/%s)", TAG, HASH)
	app.HideVersion = true
	app.Flags = []cli.Flag{
		// general
		cli.StringFlag{
			Name:  "adapter, a",
			Value: "/dev/ttyUSB0",
			Usage: "MODBUS adapter - can be either serial RTU device (/dev/ttyUSB0) or TCP socket (localhost:502)",
		},
		cli.IntFlag{
			Name:  "comset, c",
			Value: ModbusComset9600_8N1,
			Usage: `Communication parameters:
			` + strconv.Itoa(ModbusComset2400_8N1) + `:  2400 baud, 8N1
			` + strconv.Itoa(ModbusComset9600_8N1) + `:  9600 baud, 8N1
			` + strconv.Itoa(ModbusComset19200_8N1) + `: 19200 baud, 8N1
			` + strconv.Itoa(ModbusComset2400_8E1) + `:  2400 baud, 8E1
			` + strconv.Itoa(ModbusComset9600_8E1) + `:  9600 baud, 8E1
			` + strconv.Itoa(ModbusComset19200_8E1) + `: 19200 baud, 8E1
			`,
		},
		cli.BoolFlag{
			Name:  "simulate, s",
			Usage: "Simulate MODBUS device for testing purposes",
		},
		cli.StringFlag{
			Name:  "devices, d",
			Value: "SDM:1",
			Usage: `MODBUS device type and ID to query, separated by comma.
			Valid types are:` + meterHelp() + `
			Example: -d JANITZA:1,SDM:22,DZG:23`,
		},
		cli.BoolFlag{
			Name:  "detect",
			Usage: "Detect MODBUS devices",
		},
		cli.StringFlag{
			Name:  "rate, r",
			Value: "1s",
			Usage: "Maximum update rate in seconds per message, 0 is unlimited",
			// Destination: &mqttRate,
		},
		cli.StringFlag{
			Name:  "idformat, f",
			Value: "Meter%d",
			Usage: `Meter id format. Determines meter id for REST and MQTT.
			Example: -f Meter%d
			The %d is replaced by the device ID`,
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "print verbose messages",
		},

		// http api
		cli.StringFlag{
			Name:  "url, u",
			Value: "localhost:8080",
			Usage: "REST API url. Use 0.0.0.0:8080 to accept incoming connections.",
		},

		// mqtt api
		cli.StringFlag{
			Name:  "broker, b",
			Value: "",
			Usage: "MQTT: Broker URI. ex: tcp://10.10.1.1:1883",
			// Destination: &mqttBroker,
		},
		cli.StringFlag{
			Name:  "topic, t",
			Value: "sdm",
			Usage: "MQTT: Base topic. Set empty to disable publishing.",
			// Destination: &mqttTopic,
		},
		cli.StringFlag{
			Name:  "user",
			Value: "",
			Usage: "MQTT: User (optional)",
			// Destination: &mqttUser,
		},
		cli.StringFlag{
			Name:  "password",
			Value: "",
			Usage: "MQTT: Password (optional)",
			// Destination: &mqttPassword,
		},
		cli.StringFlag{
			Name:  "clientid, i",
			Value: "sdm630",
			Usage: "MQTT: ClientID",
			// Destination: &mqttClientID,
		},
		cli.BoolFlag{
			Name:  "clean, l",
			Usage: "MQTT: Set Clean Session (default: false)",
			// Destination: &mqttCleanSession,
		},
		cli.IntFlag{
			Name:  "qos, q",
			Value: 0,
			Usage: "MQTT: Quality of Service 0,1,2",
			// Destination: &mqttQos,
		},
		cli.StringFlag{
			Name:  "homie",
			Value: "homie",
			Usage: "MQTT: Homie IoT discovery base topic (homieiot.github.io). Set empty to disable.",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.NArg() > 0 {
			log.Fatalf("Unexpected arguments: %v", c.Args())
		}

		log.Printf("sdm %s %s", TAG, HASH)
		go checkVersion()

		// Set unique ID format
		UniqueIdFormat = c.String("idformat")

		// Parse the devices parameter
		meters := createMeters(strings.Split(c.String("devices"), ","))

		rate, err := time.ParseDuration(c.String("rate"))
		if err != nil {
			log.Fatalf("Invalid rate %s", err)
		}

		// create ModbusEngine with status
		status := NewStatus(meters)
		qe := NewModbusEngine(
			c.String("adapter"),
			c.Int("comset"),
			c.Bool("simulate"),
			c.Bool("verbose"),
			status,
		)

		// detect command
		if c.Bool("detect") {
			qe.Scan()
			return
		}

		// scheduler and meter data channel
		scheduler, snips := SetupScheduler(meters, qe)
		logMeterDetails(meters, qe)

		// tee that broadcasts meter messages to multiple recipients
		tee := NewQuerySnipBroadcaster(snips)
		go tee.Run()

		// websocket hub
		hub := NewSocketHub(status)
		tee.AttachRunner(hub.Run)

		// MQTT client
		if c.String("broker") != "" {
			mqtt := NewMqttClient(
				c.String("broker"),
				c.String("topic"),
				c.String("user"),
				c.String("password"),
				c.String("clientid"),
				c.Int("qos"),
				c.Bool("clean"),
				c.Bool("verbose"))

			// homie needs to scan the bust, start it first
			if c.String("homie") != "" {
				homieRunner := HomieRunner{MqttClient: mqtt}
				homieRunner.Register(c.String("homie"), meters, qe)
				tee.AttachRunner(homieRunner.Run)
			}

			// start "normal" mqtt handler after homie setup
			if c.String("topic") != "" {
				mqttRunner := MqttRunner{MqttClient: mqtt}
				tee.AttachRunner(mqttRunner.Run)
			}
		}

		// MeasurementCache for REST API
		mc := NewMeasurementCache(
			meters,
			scheduler,
			DEFAULT_METER_STORE_SECONDS,
			c.Bool("verbose"),
		)
		tee.AttachRunner(mc.Run)

		// start the scheduler
		ctx, cancelScheduler := context.WithCancel(context.Background())
		go scheduler.Run(ctx, rate)

		// handle os signals and gracefully exit Run methods
		go func() {
			waitForSignal(os.Interrupt, os.Kill)
			log.Println("Received signal - stopping")
			cancelScheduler() // cancel scheduler
			select {          // wait for Run methods attached to tee to finish
			case <-tee.Done():
				log.Println("Stopped")
				os.Exit(0)
			}
		}()

		Run_httpd(
			mc,
			hub,
			status,
			c.String("url"),
		)
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/user"
	"regexp"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/pressly/sup"
)

var (
	supfile          = flag.String("f", "./Supfile", "custom path to Supfile")
	showVersionShort = flag.Bool("v", false, "print version")
	showVersionLong  = flag.Bool("version", false, "print version")
	onlyHosts        = flag.String("only", "", "filter hosts with regexp")

	ErrUsage            = errors.New("Usage: sup [-f <Supfile>] [--only host1] <network> <target/command> [...]")
	ErrUnknownNetwork   = errors.New("Unknown network")
	ErrNetworkNoHosts   = errors.New("No hosts defined for a given network")
	ErrCmd              = errors.New("Unknown command/target")
	ErrTargetNoCommands = errors.New("No commands defined for a given target")
)

func networkUsage(conf *sup.Supfile) {
	w := &tabwriter.Writer{}
	w.Init(os.Stderr, 4, 4, 2, ' ', 0)
	defer w.Flush()

	// Print available networks/hosts.
	fmt.Fprintln(w, "Networks:\t")
	for name, network := range conf.Networks {
		fmt.Fprintf(w, "- %v\n", name)
		for _, host := range network.Hosts {
			fmt.Fprintf(w, "\t- %v\n", host)
		}
	}
	fmt.Fprintln(w)
}

func cmdUsage(conf *sup.Supfile) {
	w := &tabwriter.Writer{}
	w.Init(os.Stderr, 4, 4, 2, ' ', 0)
	defer w.Flush()

	// Print available targets/commands.
	fmt.Fprintln(w, "Targets:\t")
	for name, commands := range conf.Targets {
		fmt.Fprintf(w, "- %v\t%v\n", name, strings.Join(commands, ", "))
	}
	fmt.Fprintln(w, "\t")
	fmt.Fprintln(w, "Commands:\t")
	for name, cmd := range conf.Commands {
		fmt.Fprintf(w, "- %v\t%v\n", name, cmd.Desc)
	}
	fmt.Fprintln(w)
}

// parseArgs parses args and returns network and commands to be run.
// On error, it prints usage and exits.
func parseArgs(conf *sup.Supfile) (*sup.Network, []*sup.Command, error) {
	var commands []*sup.Command

	args := flag.Args()

	if len(args) < 1 {
		networkUsage(conf)
		return nil, nil, ErrUsage
	}

	// Does the <network> exist?
	network, ok := conf.Networks[args[0]]
	if !ok {
		networkUsage(conf)
		return nil, nil, ErrUnknownNetwork
	}

	// Does the <network> have at least one host?
	if len(network.Hosts) == 0 {
		networkUsage(conf)
		return nil, nil, ErrNetworkNoHosts
	}

	// Check for the second argument
	if len(args) < 2 {
		cmdUsage(conf)
		return nil, nil, ErrUsage
	}

	// In case of the network.Env needs an initialization
	if network.Env == nil {
		network.Env = make(sup.EnvList, 0)
	}

	// Add default env variable with current network
	network.Env.Set("SUP_NETWORK", args[0])

	// Add default nonce
	network.Env.Set("SUP_TIME", time.Now().UTC().Format(time.RFC3339))
	if os.Getenv("SUP_TIME") != "" {
		network.Env.Set("SUP_TIME", os.Getenv("SUP_TIME"))
	}

	// Add user
	if os.Getenv("SUP_USER") != "" {
		network.Env.Set("SUP_USER", os.Getenv("SUP_USER"))
	} else {
		u, err := user.Current()
		if err != nil {
			return nil, nil, err
		}
		network.Env.Set("SUP_USER", u.Username)
	}

	for _, cmd := range args[1:] {
		// Target?
		target, isTarget := conf.Targets[cmd]
		if isTarget {
			// Loop over target's commands.
			for _, cmd := range target {
				command, isCommand := conf.Commands[cmd]
				if !isCommand {
					cmdUsage(conf)
					return nil, nil, fmt.Errorf("%v: %v", ErrCmd, cmd)
				}
				command.Name = cmd
				commands = append(commands, &command)
			}
		}

		// Command?
		command, isCommand := conf.Commands[cmd]
		if isCommand {
			command.Name = cmd
			commands = append(commands, &command)
		}

		if !isTarget && !isCommand {
			cmdUsage(conf)
			return nil, nil, fmt.Errorf("%v: %v", ErrCmd, cmd)
		}
	}

	return &network, commands, nil
}

func main() {
	flag.Parse()

	if *showVersionShort || *showVersionLong {
		fmt.Println(sup.VERSION)
		return
	}

	conf, err := sup.NewSupfile(*supfile)
	if err != nil {
		log.Fatal(err)
	}

	// Parse network and commands to be run from args.
	network, commands, err := parseArgs(conf)
	if err != nil {
		log.Fatal(err)
	}

	// --only option to filter hosts
	if *onlyHosts != "" {
		expr, err := regexp.CompilePOSIX(*onlyHosts)
		if err != nil {
			log.Fatal(err)
		}

		var hosts []string
		for _, host := range network.Hosts {
			if expr.MatchString(host) {
				hosts = append(hosts, host)
			}
		}
		if len(hosts) == 0 {
			log.Fatal(fmt.Errorf("no hosts match '%v' regexp", *onlyHosts))
		}
		network.Hosts = hosts
	}

	// Create new Stackup app.
	app, err := sup.New(conf)
	if err != nil {
		log.Fatal(err)
	}

	// Run all the commands in the given network.
	err = app.Run(network, commands...)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fix $SUP_USER on cross-compiled binaries<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/pressly/sup"
)

var (
	supfile          = flag.String("f", "./Supfile", "custom path to Supfile")
	showVersionShort = flag.Bool("v", false, "print version")
	showVersionLong  = flag.Bool("version", false, "print version")
	onlyHosts        = flag.String("only", "", "filter hosts with regexp")

	ErrUsage            = errors.New("Usage: sup [-f <Supfile>] [--only host1] <network> <target/command> [...]")
	ErrUnknownNetwork   = errors.New("Unknown network")
	ErrNetworkNoHosts   = errors.New("No hosts defined for a given network")
	ErrCmd              = errors.New("Unknown command/target")
	ErrTargetNoCommands = errors.New("No commands defined for a given target")
)

func networkUsage(conf *sup.Supfile) {
	w := &tabwriter.Writer{}
	w.Init(os.Stderr, 4, 4, 2, ' ', 0)
	defer w.Flush()

	// Print available networks/hosts.
	fmt.Fprintln(w, "Networks:\t")
	for name, network := range conf.Networks {
		fmt.Fprintf(w, "- %v\n", name)
		for _, host := range network.Hosts {
			fmt.Fprintf(w, "\t- %v\n", host)
		}
	}
	fmt.Fprintln(w)
}

func cmdUsage(conf *sup.Supfile) {
	w := &tabwriter.Writer{}
	w.Init(os.Stderr, 4, 4, 2, ' ', 0)
	defer w.Flush()

	// Print available targets/commands.
	fmt.Fprintln(w, "Targets:\t")
	for name, commands := range conf.Targets {
		fmt.Fprintf(w, "- %v\t%v\n", name, strings.Join(commands, ", "))
	}
	fmt.Fprintln(w, "\t")
	fmt.Fprintln(w, "Commands:\t")
	for name, cmd := range conf.Commands {
		fmt.Fprintf(w, "- %v\t%v\n", name, cmd.Desc)
	}
	fmt.Fprintln(w)
}

// parseArgs parses args and returns network and commands to be run.
// On error, it prints usage and exits.
func parseArgs(conf *sup.Supfile) (*sup.Network, []*sup.Command, error) {
	var commands []*sup.Command

	args := flag.Args()

	if len(args) < 1 {
		networkUsage(conf)
		return nil, nil, ErrUsage
	}

	// Does the <network> exist?
	network, ok := conf.Networks[args[0]]
	if !ok {
		networkUsage(conf)
		return nil, nil, ErrUnknownNetwork
	}

	// Does the <network> have at least one host?
	if len(network.Hosts) == 0 {
		networkUsage(conf)
		return nil, nil, ErrNetworkNoHosts
	}

	// Check for the second argument
	if len(args) < 2 {
		cmdUsage(conf)
		return nil, nil, ErrUsage
	}

	// In case of the network.Env needs an initialization
	if network.Env == nil {
		network.Env = make(sup.EnvList, 0)
	}

	// Add default env variable with current network
	network.Env.Set("SUP_NETWORK", args[0])

	// Add default nonce
	network.Env.Set("SUP_TIME", time.Now().UTC().Format(time.RFC3339))
	if os.Getenv("SUP_TIME") != "" {
		network.Env.Set("SUP_TIME", os.Getenv("SUP_TIME"))
	}

	// Add user
	if os.Getenv("SUP_USER") != "" {
		network.Env.Set("SUP_USER", os.Getenv("SUP_USER"))
	} else {
		network.Env.Set("SUP_USER", os.Getenv("USER"))
	}

	for _, cmd := range args[1:] {
		// Target?
		target, isTarget := conf.Targets[cmd]
		if isTarget {
			// Loop over target's commands.
			for _, cmd := range target {
				command, isCommand := conf.Commands[cmd]
				if !isCommand {
					cmdUsage(conf)
					return nil, nil, fmt.Errorf("%v: %v", ErrCmd, cmd)
				}
				command.Name = cmd
				commands = append(commands, &command)
			}
		}

		// Command?
		command, isCommand := conf.Commands[cmd]
		if isCommand {
			command.Name = cmd
			commands = append(commands, &command)
		}

		if !isTarget && !isCommand {
			cmdUsage(conf)
			return nil, nil, fmt.Errorf("%v: %v", ErrCmd, cmd)
		}
	}

	return &network, commands, nil
}

func main() {
	flag.Parse()

	if *showVersionShort || *showVersionLong {
		fmt.Println(sup.VERSION)
		return
	}

	conf, err := sup.NewSupfile(*supfile)
	if err != nil {
		log.Fatal(err)
	}

	// Parse network and commands to be run from args.
	network, commands, err := parseArgs(conf)
	if err != nil {
		log.Fatal(err)
	}

	// --only option to filter hosts
	if *onlyHosts != "" {
		expr, err := regexp.CompilePOSIX(*onlyHosts)
		if err != nil {
			log.Fatal(err)
		}

		var hosts []string
		for _, host := range network.Hosts {
			if expr.MatchString(host) {
				hosts = append(hosts, host)
			}
		}
		if len(hosts) == 0 {
			log.Fatal(fmt.Errorf("no hosts match '%v' regexp", *onlyHosts))
		}
		network.Hosts = hosts
	}

	// Create new Stackup app.
	app, err := sup.New(conf)
	if err != nil {
		log.Fatal(err)
	}

	// Run all the commands in the given network.
	err = app.Run(network, commands...)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>