<commit_before>package main

import (
	"time"
)

// See https://github.com/heroku/api-doc#apps
type App struct {
	ID     string
	Name   string
	Stack  string
	WebURL string `json:"web_url"`
	GitURL string `json:"git_url"`
	Owner  struct {
		Id    string
		Email string
	}
	RepoSize    *int       `json:"repo_size"`
	SlugSize    *int       `json:"slug_size"`
	CreatedAt   time.Time  `json:"created_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
	ReleasedAt  *time.Time `json:"released_at"`
	Maintenance bool

	BuildpackProvidedDescription *NullString `json:"buildpack_provided_description"`
}

type Dyno struct {
	Name    string
	ID      string
	Type    string
	Command string
	AppName string `json:"app_name"`
	Release struct {
		ID      string
		Version int
	}
	Size      int
	State     string
	AttachURL *string   `json:"attach_url"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (d *Dyno) Age() time.Duration {
	return time.Now().Sub(d.CreatedAt)
}

type Release struct {
	ID   string
	User struct {
		ID    string
		Email string
	}
	Slug struct {
		ID string
	}
	Description string
	Version     int
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`

	Who    string // same as User.Email or abbreviated
	Commit string // deduced from Description, if possible
}

type Addon struct {
	ID   string
	Plan struct {
		ID   string
		Name string
	}
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

type Resource struct {
	URL   *NullString
	Price struct {
		Unit  string
		Cents int
	}
	TermsOfService    bool   `json:"terms_of_service"`
	GroupDescription  string `json:"group_description"`
	Configured        bool
	State             string
	SSOURL            *NullString `json:"sso_url"`
	Attachable        bool
	Description       string
	ConsumesDynoHours bool
	Selective         bool
	Beta              bool
	Name              string
	Slug              string
}

type Attachment struct {
	ConfigVar string `json:"config_var"`
	App       struct {
		Owner string
		ID    string
		Name  string
	}
	Resource struct {
		Name       string
		Type       string
		ID         string
		Value      string
		SSOURL     *NullString `json:"sso_url"`
		BillingApp struct {
			Name  string
			ID    string
			Owner string
		} `json:"billing_app"`
	}
}

type LogSession struct {
	LogplexURL string `json:"logplex_url"`
	CreatedAt  time.Time
}

type NullString string

func (s *NullString) String() string {
	if s == nil {
		return "(null)"
	}
	return string(*s)
}
<commit_msg>For dyno age, use UpdatedAt instead of CreatedAt.<commit_after>package main

import (
	"time"
)

// See https://github.com/heroku/api-doc#apps
type App struct {
	ID     string
	Name   string
	Stack  string
	WebURL string `json:"web_url"`
	GitURL string `json:"git_url"`
	Owner  struct {
		Id    string
		Email string
	}
	RepoSize    *int       `json:"repo_size"`
	SlugSize    *int       `json:"slug_size"`
	CreatedAt   time.Time  `json:"created_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
	ReleasedAt  *time.Time `json:"released_at"`
	Maintenance bool

	BuildpackProvidedDescription *NullString `json:"buildpack_provided_description"`
}

type Dyno struct {
	Name    string
	ID      string
	Type    string
	Command string
	AppName string `json:"app_name"`
	Release struct {
		ID      string
		Version int
	}
	Size      int
	State     string
	AttachURL *string   `json:"attach_url"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (d *Dyno) Age() time.Duration {
	return time.Now().Sub(d.UpdatedAt)
}

type Release struct {
	ID   string
	User struct {
		ID    string
		Email string
	}
	Slug struct {
		ID string
	}
	Description string
	Version     int
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`

	Who    string // same as User.Email or abbreviated
	Commit string // deduced from Description, if possible
}

type Addon struct {
	ID   string
	Plan struct {
		ID   string
		Name string
	}
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

type Resource struct {
	URL   *NullString
	Price struct {
		Unit  string
		Cents int
	}
	TermsOfService    bool   `json:"terms_of_service"`
	GroupDescription  string `json:"group_description"`
	Configured        bool
	State             string
	SSOURL            *NullString `json:"sso_url"`
	Attachable        bool
	Description       string
	ConsumesDynoHours bool
	Selective         bool
	Beta              bool
	Name              string
	Slug              string
}

type Attachment struct {
	ConfigVar string `json:"config_var"`
	App       struct {
		Owner string
		ID    string
		Name  string
	}
	Resource struct {
		Name       string
		Type       string
		ID         string
		Value      string
		SSOURL     *NullString `json:"sso_url"`
		BillingApp struct {
			Name  string
			ID    string
			Owner string
		} `json:"billing_app"`
	}
}

type LogSession struct {
	LogplexURL string `json:"logplex_url"`
	CreatedAt  time.Time
}

type NullString string

func (s *NullString) String() string {
	if s == nil {
		return "(null)"
	}
	return string(*s)
}
<|endoftext|>


<commit_before>package seabird

import (
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"strings"

	"bitbucket.org/belak/irc"
)

var diceRe = regexp.MustCompile(`(?:^|\b)(\d*)d(\d+)\b`)

func DiceHandler(c *irc.Client, e *irc.Event) {
	var rolls []string
	totalCount := 0

	matches := diceRe.FindAllStringSubmatch(e.Trailing(), -1)
	for _, match := range matches {
		if len(match) != 3 {
			continue
		}

		// Grab the count, otherwise 1
		count, _ := strconv.Atoi(match[1])
		if count == 0 {
			count = 1
		}

		// Clamp count
		if count < 0 {
			c.MentionReply(e, "You cannot request a negative number of rolls")
			return
		}

		totalCount += count
		if totalCount > 100 {
			c.MentionReply(e, "You cannot request more than 100 dice")
			return
		}

		// How big is the die?
		size, _ := strconv.Atoi(match[2])

		if size > 100 {
			c.MentionReply(e, "You cannot request dice larger than 100")
			return
		}

		// Clamp size
		if size < 1 {
			c.MentionReply(e, "You cannot request a non-positive die size")
			return
		}

		var dice []string
		for i := 0; i < count; i++ {
			dice = append(dice, fmt.Sprintf("%d", rand.Intn(size)+1))
		}

		rolls = append(rolls, fmt.Sprintf("%dd%d: %s", count, size, strings.Join(dice, ", ")))
	}

	if len(rolls) > 0 {
		c.MentionReply(e, "%s", strings.Join(rolls, " "))
	}
}
<commit_msg>Small fix for dwfreed<commit_after>package seabird

import (
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"strings"

	"bitbucket.org/belak/irc"
)

var diceRe = regexp.MustCompile(`(?:^|\b)(\d*)d(\d+)\b`)

func DiceHandler(c *irc.Client, e *irc.Event) {
	var rolls []string
	totalCount := 0

	matches := diceRe.FindAllStringSubmatch(e.Trailing(), -1)
	for _, match := range matches {
		if len(match) != 3 {
			continue
		}

		// Grab the count, otherwise 1
		count, _ := strconv.Atoi(match[1])
		if count == 0 {
			count = 1
		}

		// Clamp count
		if count < 0 {
			c.MentionReply(e, "You cannot request a negative number of rolls")
			return
		}

		totalCount += count
		if totalCount > 100 {
			c.MentionReply(e, "You cannot request more than 100 dice")
			return
		}

		// How big is the die?
		size, _ := strconv.Atoi(match[2])

		if size > 100 {
			c.MentionReply(e, "You cannot request dice larger than 100")
			return
		}

		// Clamp size
		if size < 1 {
			c.MentionReply(e, "You cannot request dice smaller than 1")
			return
		}

		var dice []string
		for i := 0; i < count; i++ {
			dice = append(dice, fmt.Sprintf("%d", rand.Intn(size)+1))
		}

		rolls = append(rolls, fmt.Sprintf("%dd%d: %s", count, size, strings.Join(dice, ", ")))
	}

	if len(rolls) > 0 {
		c.MentionReply(e, "%s", strings.Join(rolls, " "))
	}
}
<|endoftext|>


<commit_before>package xlsx

import (
	"fmt"
	"strconv"
)

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
	Name   string
	File   *File
	Rows   []*Row
	Cols   []*Col
	MaxRow int
	MaxCol int
	Hidden bool
}

// Add a new Row to a Sheet
func (s *Sheet) AddRow() *Row {
	row := &Row{Sheet: s}
	s.Rows = append(s.Rows, row)
	if len(s.Rows) > s.MaxRow {
		s.MaxRow = len(s.Rows)
	}
	return row
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) maybeAddCol(cellCount int) {
	if cellCount > s.MaxCol {
		col := &Col{
			Min:       cellCount,
			Max:       cellCount,
			Hidden:    false,
			Collapsed: false,
			// Style:     0,
			Width: ColWidth}
		s.Cols = append(s.Cols, col)
		s.MaxCol = cellCount
	}
}

// Get a Cell by passing it's cartesian coordinates (zero based) as
// row and column integer indexes.
//
// For example:
//
//    cell := sheet.Cell(0,0)
//
// ... would set the variable "cell" to contain a Cell struct
// containing the data from the field "A1" on the spreadsheet.
func (sh *Sheet) Cell(row, col int) *Cell {

	if len(sh.Rows) > row && sh.Rows[row] != nil && len(sh.Rows[row].Cells) > col {
		return sh.Rows[row].Cells[col]
	}
	return new(Cell)
}

// Dump sheet to it's XML representation, intended for internal use only
func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxWorksheet {
	worksheet := newXlsxWorksheet()
	xSheet := xlsxSheetData{}
	maxRow := 0
	maxCell := 0
	for r, row := range s.Rows {
		if r > maxRow {
			maxRow = r
		}
		xRow := xlsxRow{}
		xRow.R = r + 1
		for c, cell := range row.Cells {
			style := cell.GetStyle()
			xNumFmt, xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
			fontId := styles.addFont(xFont)
			fillId := styles.addFill(xFill)
			borderId := styles.addBorder(xBorder)
			styles.addNumFmt(xNumFmt)
			xCellStyleXf.FontId = fontId
			xCellStyleXf.FillId = fillId
			xCellStyleXf.BorderId = borderId
			xCellStyleXf.NumFmtId = xNumFmt.NumFmtId
			xCellXf.FontId = fontId
			xCellXf.FillId = fillId
			xCellXf.BorderId = borderId
			xCellXf.NumFmtId = xNumFmt.NumFmtId
			styles.addCellStyleXf(xCellStyleXf)
			XfId := styles.addCellXf(xCellXf)
			if c > maxCell {
				maxCell = c
			}
			xC := xlsxC{}
			xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r+1)
			switch cell.cellType {
			case CellTypeString:
				xC.V = strconv.Itoa(refTable.AddString(cell.Value))
				xC.T = "s"
				xC.S = XfId
			case CellTypeBool:
				xC.V = cell.Value
				xC.T = "b"
				xC.S = XfId
			case CellTypeNumeric:
				xC.V = cell.Value
				xC.S = XfId
			case CellTypeFormula:
				xC.V = cell.Value
				xC.F = cell.formula
				xC.S = XfId
			case CellTypeError:
				xC.V = cell.Value
				xC.F = cell.formula
				xC.T = "e"
				xC.S = XfId
			}
			xRow.C = append(xRow.C, xC)
		}
		xSheet.Row = append(xSheet.Row, xRow)
	}

	worksheet.Cols = xlsxCols{Col: []xlsxCol{}}
	for _, col := range s.Cols {
		if col.Width == 0 {
			col.Width = ColWidth
		}
		worksheet.Cols.Col = append(worksheet.Cols.Col,
			xlsxCol{Min: col.Min,
				Max:       col.Max,
				Hidden:    col.Hidden,
				Width:     col.Width,
				Collapsed: col.Collapsed,
				// Style:     col.Style
			})
	}
	worksheet.SheetData = xSheet
	dimension := xlsxDimension{}
	dimension.Ref = fmt.Sprintf("A1:%s%d",
		numericToLetters(maxCell), maxRow+1)
	if dimension.Ref == "A1:A1" {
		dimension.Ref = "A1"
	}
	worksheet.Dimension = dimension
	return worksheet
}
<commit_msg>Stop crashing if style is Nil<commit_after>package xlsx

import (
	"fmt"
	"strconv"
)

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
	Name   string
	File   *File
	Rows   []*Row
	Cols   []*Col
	MaxRow int
	MaxCol int
	Hidden bool
}

// Add a new Row to a Sheet
func (s *Sheet) AddRow() *Row {
	row := &Row{Sheet: s}
	s.Rows = append(s.Rows, row)
	if len(s.Rows) > s.MaxRow {
		s.MaxRow = len(s.Rows)
	}
	return row
}

// Make sure we always have as many Cols as we do cells.
func (s *Sheet) maybeAddCol(cellCount int) {
	if cellCount > s.MaxCol {
		col := &Col{
			Min:       cellCount,
			Max:       cellCount,
			Hidden:    false,
			Collapsed: false,
			// Style:     0,
			Width: ColWidth}
		s.Cols = append(s.Cols, col)
		s.MaxCol = cellCount
	}
}

// Get a Cell by passing it's cartesian coordinates (zero based) as
// row and column integer indexes.
//
// For example:
//
//    cell := sheet.Cell(0,0)
//
// ... would set the variable "cell" to contain a Cell struct
// containing the data from the field "A1" on the spreadsheet.
func (sh *Sheet) Cell(row, col int) *Cell {

	if len(sh.Rows) > row && sh.Rows[row] != nil && len(sh.Rows[row].Cells) > col {
		return sh.Rows[row].Cells[col]
	}
	return new(Cell)
}

// Dump sheet to it's XML representation, intended for internal use only
func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxWorksheet {
	worksheet := newXlsxWorksheet()
	xSheet := xlsxSheetData{}
	maxRow := 0
	maxCell := 0
	XfId := 0
	for r, row := range s.Rows {
		if r > maxRow {
			maxRow = r
		}
		xRow := xlsxRow{}
		xRow.R = r + 1
		for c, cell := range row.Cells {
			style := cell.GetStyle()
			if style != nil {
			xNumFmt, xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
			fontId := styles.addFont(xFont)
			fillId := styles.addFill(xFill)
			borderId := styles.addBorder(xBorder)
			styles.addNumFmt(xNumFmt)
			xCellStyleXf.FontId = fontId
			xCellStyleXf.FillId = fillId
			xCellStyleXf.BorderId = borderId
			xCellStyleXf.NumFmtId = xNumFmt.NumFmtId
			xCellXf.FontId = fontId
			xCellXf.FillId = fillId
			xCellXf.BorderId = borderId
			xCellXf.NumFmtId = xNumFmt.NumFmtId
			styles.addCellStyleXf(xCellStyleXf)
			XfId = styles.addCellXf(xCellXf)
			}
			if c > maxCell {
				maxCell = c
			}
			xC := xlsxC{}
			xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r+1)
			switch cell.cellType {
			case CellTypeString:
				xC.V = strconv.Itoa(refTable.AddString(cell.Value))
				xC.T = "s"
				xC.S = XfId
			case CellTypeBool:
				xC.V = cell.Value
				xC.T = "b"
				xC.S = XfId
			case CellTypeNumeric:
				xC.V = cell.Value
				xC.S = XfId
			case CellTypeFormula:
				xC.V = cell.Value
				xC.F = cell.formula
				xC.S = XfId
			case CellTypeError:
				xC.V = cell.Value
				xC.F = cell.formula
				xC.T = "e"
				xC.S = XfId
			}
			xRow.C = append(xRow.C, xC)
		}
		xSheet.Row = append(xSheet.Row, xRow)
	}

	worksheet.Cols = xlsxCols{Col: []xlsxCol{}}
	for _, col := range s.Cols {
		if col.Width == 0 {
			col.Width = ColWidth
		}
		worksheet.Cols.Col = append(worksheet.Cols.Col,
			xlsxCol{Min: col.Min,
				Max:       col.Max,
				Hidden:    col.Hidden,
				Width:     col.Width,
				Collapsed: col.Collapsed,
				// Style:     col.Style
			})
	}
	worksheet.SheetData = xSheet
	dimension := xlsxDimension{}
	dimension.Ref = fmt.Sprintf("A1:%s%d",
		numericToLetters(maxCell), maxRow+1)
	if dimension.Ref == "A1:A1" {
		dimension.Ref = "A1"
	}
	worksheet.Dimension = dimension
	return worksheet
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/fatih/color"
	"github.com/nlopes/slack"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
)

var lock = new(sync.Mutex)
var adminCommands = make(map[string]func(string, string, slack.Msg) error)

//Processes messages from Slack channel #puddle
//Ideally, this should be a router
func ProcessMessage(msg slack.Msg) error {
	if strings.HasPrefix(msg.Text, "!") {
		username, _ := GetUsername(msg)
		ProcessAdminCommand(username, msg.Text, msg)
	}
	return nil
}

//Retreive username from SLACK API
func GetUsername(msg slack.Msg) (string, error) {
	userInfo, err := slackAPI.GetUserInfo(msg.User)
	if err != nil {
		fmt.Println(color.RedString("warning"), "No user information: ", msg.Text)
		return "", err
	}

	return userInfo.Name, nil
}

//Parse message and search admin commands and feed message
func ProcessAdminCommand(username string, msgText string, msg slack.Msg) {
	fmt.Println("ADMIN COMMAND: <"+msg.User+">", color.RedString("warning"), msg.Text)

	words := strings.Split(msgText, " ")
	cmdString := strings.TrimPrefix(words[0], "!")

	if fn, ok := adminCommands[cmdString]; ok {
		go fn(username, msgText, msg)
	} else {
		rtm.SendMessage(rtm.NewOutgoingMessage(fmt.Sprintf("@%s Wat?"), username))
	}
}

//Simple send message to @username: <msg>
func SendMessage(username, text string) error {
	rtm.SendMessage(rtm.NewOutgoingMessage(fmt.Sprintf("@%s: %s", username, text), CHANNEL))

	return nil
}

//Pull latest git master and rebuild, restart puddlebot
func Build(username string, msgText string, msg slack.Msg) error {
	SendMessage(username, "pulling origin/develop...")
	out, err := exec.Command("git", "pull", "origin", "develop").Output()
	SendMessage(username, string(out))
	SendMessage(username, "...Restarting...")
	cmd := exec.Command("./installandrun.sh")
	cmd.Run()

	os.Exit(1)
	return err
}

//Register admin command
func RegisterAdminCommand(cmdString string, fn func(string, string, slack.Msg) error) error {
	log.Println(fmt.Sprintf("Registering new admin command <%s>", cmdString))

	lock.Lock()
	defer lock.Unlock()

	if _, ok := adminCommands["foo"]; !ok {
		adminCommands[cmdString] = fn
		return nil
	}

	return errors.New("Command exists")
}

func init() {
	RegisterAdminCommand("build", Build)
}
<commit_msg>fix cmd exec<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/fatih/color"
	"github.com/nlopes/slack"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
)

var lock = new(sync.Mutex)
var adminCommands = make(map[string]func(string, string, slack.Msg) error)

//Processes messages from Slack channel #puddle
//Ideally, this should be a router
func ProcessMessage(msg slack.Msg) error {
	if strings.HasPrefix(msg.Text, "!") {
		username, _ := GetUsername(msg)
		ProcessAdminCommand(username, msg.Text, msg)
	}
	return nil
}

//Retreive username from SLACK API
func GetUsername(msg slack.Msg) (string, error) {
	userInfo, err := slackAPI.GetUserInfo(msg.User)
	if err != nil {
		fmt.Println(color.RedString("warning"), "No user information: ", msg.Text)
		return "", err
	}

	return userInfo.Name, nil
}

//Parse message and search admin commands and feed message
func ProcessAdminCommand(username string, msgText string, msg slack.Msg) {
	fmt.Println("ADMIN COMMAND: <"+msg.User+">", color.RedString("warning"), msg.Text)

	words := strings.Split(msgText, " ")
	cmdString := strings.TrimPrefix(words[0], "!")

	if fn, ok := adminCommands[cmdString]; ok {
		go fn(username, msgText, msg)
	} else {
		rtm.SendMessage(rtm.NewOutgoingMessage(fmt.Sprintf("@%s Wat?"), username))
	}
}

//Simple send message to @username: <msg>
func SendMessage(username, text string) error {
	rtm.SendMessage(rtm.NewOutgoingMessage(fmt.Sprintf("@%s: %s", username, text), CHANNEL))

	return nil
}

//Pull latest git master and rebuild, restart puddlebot
func Build(username string, msgText string, msg slack.Msg) error {
	SendMessage(username, "pulling origin/develop...")
	out, err := exec.Command("git", "pull", "origin", "develop").Output()
	SendMessage(username, string(out))
	SendMessage(username, "...Restarting...")
	cmd := exec.Command("./installandrun.sh")
	cmd.Start()

	os.Exit(1)
	return err
}

//Register admin command
func RegisterAdminCommand(cmdString string, fn func(string, string, slack.Msg) error) error {
	log.Println(fmt.Sprintf("Registering new admin command <%s>", cmdString))

	lock.Lock()
	defer lock.Unlock()

	if _, ok := adminCommands["foo"]; !ok {
		adminCommands[cmdString] = fn
		return nil
	}

	return errors.New("Command exists")
}

func init() {
	RegisterAdminCommand("build", Build)
}
<|endoftext|>


<commit_before>package nopaste

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"time"
)

const SlackMaxBackOff = 3600

var (
	SlackThrottleWindow = 1 * time.Second
	SlackInitialBackOff = 30
	Epoch               = time.Unix(0, 0)
)

type SlackMessage struct {
	Channel   string `json:"channel"`
	Text      string `json:"text"`
	IconEmoji string `json:"icon_emoji,omitempty"`
	IconURL   string `json:"icon_url,omitempty"`
	Username  string `json:"username"`
	LinkNames int    `json:"link_names,omitempty"`
}

type SlackMessageChan chan SlackMessage

func (ch SlackMessageChan) PostNopaste(np nopasteContent, url string) {
	summary := np.Summary
	nick := np.Nick
	var text string
	if summary == "" {
		text = fmt.Sprintf("<%s|%s>", url, url)
	} else {
		text = fmt.Sprintf("%s <%s|show details>", summary, url)
	}
	msg := SlackMessage{
		Channel:   np.Channel,
		Username:  nick,
		Text:      text,
		IconEmoji: np.IconEmoji,
		IconURL:   np.IconURL,
	}
	select {
	case ch <- msg:
	default:
		log.Println("Can't send msg to Slack")
	}
}

func (ch SlackMessageChan) PostMsgr(req *http.Request) {
	username := req.FormValue("username")
	if username == "" {
		username = "msgr"
	}
	msg := SlackMessage{
		Channel:   req.FormValue("channel"),
		Text:      req.FormValue("msg"),
		Username:  username,
		IconEmoji: req.FormValue("icon_emoji"),
		IconURL:   req.FormValue("icon_url"),
		LinkNames: 0, // default notice as IRC
	}
	if _notice := req.FormValue("notice"); _notice == "0" {
		msg.LinkNames = 1
	}
	select {
	case ch <- msg:
	default:
		log.Println("Can't send msg to Slack")
	}
}

type SlackAgent struct {
	WebhookURL string
	client     *http.Client
}

func (a *SlackAgent) Post(m SlackMessage) error {
	payload, _ := json.Marshal(&m)
	v := url.Values{}
	v.Set("payload", string(payload))
	log.Println("post to slack", a, string(payload))
	resp, err := a.client.PostForm(a.WebhookURL, v)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusOK {
		return nil
	}
	if body, err := ioutil.ReadAll(resp.Body); err == nil {
		return fmt.Errorf("failed post to slack:%s", body)
	} else {
		return err
	}
}

func RunSlackAgent(c *Config, ch chan SlackMessage) {
	log.Println("runing slack agent")
	joined := make(map[string]chan SlackMessage)
	agent := &SlackAgent{
		WebhookURL: c.Slack.WebhookURL,
		client:     &http.Client{},
	}
	for {
		select {
		case msg := <-ch:
			if _, ok := joined[msg.Channel]; !ok {
				joined[msg.Channel] = make(chan SlackMessage, MsgBufferLen)
				go sendMsgToSlackChannel(agent, joined[msg.Channel])
			}
			select {
			case joined[msg.Channel] <- msg:
			default:
				log.Println("Can't send msg to Slack. Channel buffer flooding.")
			}
		}
	}
}

func sendMsgToSlackChannel(agent *SlackAgent, ch chan SlackMessage) {
	lastPostedAt := time.Now()
	ignoreUntil := Epoch
	backoff := SlackInitialBackOff
	for {
		select {
		case msg := <-ch:
			if time.Now().Before(ignoreUntil) {
				// ignored
				continue
			}
			throttle(lastPostedAt, SlackThrottleWindow)
			err := agent.Post(msg)
			lastPostedAt = time.Now()
			if err != nil {
				backoff = int(math.Min(float64(backoff)*2, SlackMaxBackOff))
				d, _ := time.ParseDuration(fmt.Sprintf("%ds", backoff))
				ignoreUntil = lastPostedAt.Add(d)
				log.Println(err, msg.Channel, "will be ignored until", ignoreUntil)
			} else if !ignoreUntil.Equal(Epoch) {
				ignoreUntil = Epoch
				backoff = SlackInitialBackOff
			}
		}
	}
}
<commit_msg>supports notice=0 for np<commit_after>package nopaste

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"time"
)

const SlackMaxBackOff = 3600

var (
	SlackThrottleWindow = 1 * time.Second
	SlackInitialBackOff = 30
	Epoch               = time.Unix(0, 0)
)

type SlackMessage struct {
	Channel   string `json:"channel"`
	Text      string `json:"text"`
	IconEmoji string `json:"icon_emoji,omitempty"`
	IconURL   string `json:"icon_url,omitempty"`
	Username  string `json:"username"`
	LinkNames int    `json:"link_names,omitempty"`
}

type SlackMessageChan chan SlackMessage

func (ch SlackMessageChan) PostNopaste(np nopasteContent, url string) {
	summary := np.Summary
	nick := np.Nick
	var text string
	if summary == "" {
		text = fmt.Sprintf("<%s|%s>", url, url)
	} else {
		text = fmt.Sprintf("%s <%s|show details>", summary, url)
	}
	msg := SlackMessage{
		Channel:   np.Channel,
		Username:  nick,
		Text:      text,
		IconEmoji: np.IconEmoji,
		IconURL:   np.IconURL,
		LinkNames: 0,
	}
	if _notice := req.FormValue("notice"); _notice == "0" {
		msg.LinkNames = 1
	}
	select {
	case ch <- msg:
	default:
		log.Println("Can't send msg to Slack")
	}
}

func (ch SlackMessageChan) PostMsgr(req *http.Request) {
	username := req.FormValue("username")
	if username == "" {
		username = "msgr"
	}
	msg := SlackMessage{
		Channel:   req.FormValue("channel"),
		Text:      req.FormValue("msg"),
		Username:  username,
		IconEmoji: req.FormValue("icon_emoji"),
		IconURL:   req.FormValue("icon_url"),
		LinkNames: 0, // default notice as IRC
	}
	if _notice := req.FormValue("notice"); _notice == "0" {
		msg.LinkNames = 1
	}
	select {
	case ch <- msg:
	default:
		log.Println("Can't send msg to Slack")
	}
}

type SlackAgent struct {
	WebhookURL string
	client     *http.Client
}

func (a *SlackAgent) Post(m SlackMessage) error {
	payload, _ := json.Marshal(&m)
	v := url.Values{}
	v.Set("payload", string(payload))
	log.Println("post to slack", a, string(payload))
	resp, err := a.client.PostForm(a.WebhookURL, v)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusOK {
		return nil
	}
	if body, err := ioutil.ReadAll(resp.Body); err == nil {
		return fmt.Errorf("failed post to slack:%s", body)
	} else {
		return err
	}
}

func RunSlackAgent(c *Config, ch chan SlackMessage) {
	log.Println("runing slack agent")
	joined := make(map[string]chan SlackMessage)
	agent := &SlackAgent{
		WebhookURL: c.Slack.WebhookURL,
		client:     &http.Client{},
	}
	for {
		select {
		case msg := <-ch:
			if _, ok := joined[msg.Channel]; !ok {
				joined[msg.Channel] = make(chan SlackMessage, MsgBufferLen)
				go sendMsgToSlackChannel(agent, joined[msg.Channel])
			}
			select {
			case joined[msg.Channel] <- msg:
			default:
				log.Println("Can't send msg to Slack. Channel buffer flooding.")
			}
		}
	}
}

func sendMsgToSlackChannel(agent *SlackAgent, ch chan SlackMessage) {
	lastPostedAt := time.Now()
	ignoreUntil := Epoch
	backoff := SlackInitialBackOff
	for {
		select {
		case msg := <-ch:
			if time.Now().Before(ignoreUntil) {
				// ignored
				continue
			}
			throttle(lastPostedAt, SlackThrottleWindow)
			err := agent.Post(msg)
			lastPostedAt = time.Now()
			if err != nil {
				backoff = int(math.Min(float64(backoff)*2, SlackMaxBackOff))
				d, _ := time.ParseDuration(fmt.Sprintf("%ds", backoff))
				ignoreUntil = lastPostedAt.Add(d)
				log.Println(err, msg.Channel, "will be ignored until", ignoreUntil)
			} else if !ignoreUntil.Equal(Epoch) {
				ignoreUntil = Epoch
				backoff = SlackInitialBackOff
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/pivotal-golang/archiver/compressor"
)

func main() {
	compressor := compressor.NewTgz()
	src, err := os.Getwd()
	if err != nil {
		fmt.Println("Couldn't get current directory...")
		os.Exit(1)
	}
	dest, err := ioutil.TempFile("", "smith")
	if err != nil {
		fmt.Println("Couldn't create temporary file...")
		os.Exit(1)
	}
	dest.Close()

	err = compressor.Compress(src, dest.Name())
	if err != nil {
		fmt.Printf("Couldn't create archive: %s\n", err.Error())
		os.Exit(1)
	}

	os.Rename(dest.Name(), "dir.tgz")

	fmt.Printf("Created archive of current directory.")
}
<commit_msg>spike on running a build<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/pivotal-golang/archiver/compressor"
)

type Source struct {
	Type string `json:"type"`
	URI  string `json:"uri"`
	Ref  string `json:"ref"`
}

type Build struct {
	Guid     string `json:"guid"`
	Image    string `json:"image"`
	Script   string `json:"script"`
	Callback string `json:"callback"`
	Source   Source `json:"source"`
}

func main() {
	compressor := compressor.NewTgz()
	src, err := os.Getwd()
	if err != nil {
		fmt.Println("Couldn't get current directory...")
		os.Exit(1)
	}
	dest, err := ioutil.TempFile("", "smith")
	if err != nil {
		fmt.Println("Couldn't create temporary file...")
		os.Exit(1)
	}
	dest.Close()

	err = compressor.Compress(src, dest.Name())
	if err != nil {
		fmt.Printf("Couldn't create archive: %s\n", err.Error())
		os.Exit(1)
	}

	fmt.Printf("Created archive of current directory: %s", dest.Name())

	http.HandleFunc("/file", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, dest.Name())
	})

	http.HandleFunc("/die", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		os.Exit(0)
	})

	// RAAAAACE CONDITION
	go http.ListenAndServe(":8080", nil)

	build := Build{
		Guid:     "abc",
		Image:    "ubuntu",
		Script:   "ls -l",
		Callback: "http://localhost:8080/die",
		Source: Source{
			Type: "raw",
			URI:  "http://localhost:8080/file",
			Ref:  "",
		},
	}

	client := &http.Client{}
	buffer := &bytes.Buffer{}
	encoder := json.NewEncoder(buffer)
	encoder.Encode(build)

	req, err := http.NewRequest("POST", "http://localhost:4637/builds", buffer)
	if err != nil {
		fmt.Println("Couldn't create request... %s", err)
		os.Exit(1)
	}
	req.Header.Add("Content-Type", "application/json")
	response, err := client.Do(req)
	if err != nil {
		fmt.Println("Request failed... %s", err)
		os.Exit(1)
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Body failed... %s", err)
		os.Exit(1)
	}
	fmt.Println("%s", string(body))

	select {}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const shutdownGraceTime = 3 * time.Second

var flagPort int
var flagConcurrency string
var flagRestart bool

var cmdStart = &Command{
	Run:   runStart,
	Usage: "start [process name] [-f procfile] [-e env] [-c concurrency] [-p port] [-r]",
	Short: "Start the application",
	Long: `
Start the application specified by a Procfile (defaults to ./Procfile)

Examples:

  forego start
  forego start web
  forego start -f Procfile.test -e .env.test
`,
}

func init() {
	cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile")
	cmdStart.Flag.StringVar(&flagEnv, "e", "", "env")
	cmdStart.Flag.IntVar(&flagPort, "p", 5000, "port")
	cmdStart.Flag.StringVar(&flagConcurrency, "c", "", "concurrency")
	cmdStart.Flag.BoolVar(&flagRestart, "r", false, "restart")
}

func parseConcurrency(value string) (map[string]int, error) {
	concurrency := map[string]int{}
	if strings.TrimSpace(value) == "" {
		return concurrency, nil
	}

	parts := strings.Split(value, ",")
	for _, part := range parts {
		if !strings.Contains(part, "=") {
			return concurrency, errors.New("Parsing concurency")
		}

		nameValue := strings.Split(part, "=")
		n, v := strings.TrimSpace(nameValue[0]), strings.TrimSpace(nameValue[1])
		if n == "" || v == "" {
			return concurrency, errors.New("Parsing concurency")
		}

		numProcs, err := strconv.ParseInt(v, 10, 16)
		if err != nil {
			return concurrency, err
		}

		concurrency[n] = int(numProcs)
	}
	return concurrency, nil
}

type Forego struct {
	outletFactory *OutletFactory

	teardown, teardownNow Barrier // signal shutting down

	wg sync.WaitGroup
}

func (f *Forego) monitorInterrupt() {
	handler := make(chan os.Signal, 1)
	signal.Notify(handler, os.Interrupt)

	first := true

	for sig := range handler {
		switch sig {
		case os.Interrupt:
			fmt.Println("      | ctrl-c detected")

			f.teardown.Fall()
			if !first {
				f.teardownNow.Fall()
			}
			first = false
		}
	}
}

func (f *Forego) startProcess(idx, procNum int, proc ProcfileEntry, env Env, of *OutletFactory) {
	port := flagPort + (idx * 100)

	const interactive = false
	workDir := filepath.Dir(flagProcfile)
	ps := NewProcess(workDir, proc.Command, env, interactive)
	procName := fmt.Sprint(proc.Name, ".", procNum+1)
	ps.Env["PORT"] = strconv.Itoa(port)

	ps.Stdin = nil

	stdout, err := ps.StdoutPipe()
	if err != nil {
		panic(err)
	}
	stderr, err := ps.StderrPipe()
	if err != nil {
		panic(err)
	}

	pipeWait := new(sync.WaitGroup)
	pipeWait.Add(2)
	go of.LineReader(pipeWait, procName, idx, stdout, false)
	go of.LineReader(pipeWait, procName, idx, stderr, true)

	of.SystemOutput(fmt.Sprintf("starting %s on port %d", procName, port))

	finished := make(chan struct{}) // closed on process exit

	ps.Start()

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()
		defer close(finished)
		pipeWait.Wait()
		ps.Wait()
	}()

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		// Prevent goroutine from exiting before process has finished.
		defer func() { <-finished }()
		defer f.teardown.Fall()

		select {
		case <-finished:
			if flagRestart {
				f.startProcess(idx, procNum, proc, env, of)
				return
			}

		case <-f.teardown.Barrier():
			// Forego tearing down

			if !osHaveSigTerm {
				of.SystemOutput(fmt.Sprintf("Killing %s", procName))
				ps.Process.Kill()
				return
			}

			of.SystemOutput(fmt.Sprintf("sending SIGTERM to %s", procName))
			ps.SendSigTerm()

			// Give the process a chance to exit, otherwise kill it.
			select {
			case <-f.teardownNow.Barrier():
				of.SystemOutput(fmt.Sprintf("Killing %s", procName))
				ps.SendSigKill()
			case <-finished:
			}
		}
	}()
}

func runStart(cmd *Command, args []string) {
	root := filepath.Dir(flagProcfile)

	if flagEnv == "" {
		flagEnv = filepath.Join(root, ".env")
	}

	pf, err := ReadProcfile(flagProcfile)
	handleError(err)

	env, err := ReadEnv(flagEnv)
	handleError(err)

	concurrency, err := parseConcurrency(flagConcurrency)
	handleError(err)

	of := NewOutletFactory()
	of.Padding = pf.LongestProcessName(concurrency)

	f := &Forego{
		outletFactory: of,
	}

	go f.monitorInterrupt()

	// When teardown fires, start the grace timer
	f.teardown.FallHook = func() {
		go func() {
			time.Sleep(shutdownGraceTime)
			of.SystemOutput("Grace time expired")
			f.teardownNow.Fall()
		}()
	}

	var singleton string = ""
	if len(args) > 0 {
		singleton = args[0]
		if !pf.HasProcess(singleton) {
			of.ErrorOutput(fmt.Sprintf("no such process: %s", singleton))
		}
	}

	for idx, proc := range pf.Entries {
		numProcs := 1
		if value, ok := concurrency[proc.Name]; ok {
			numProcs = value
		}
		for i := 0; i < numProcs; i++ {
			if (singleton == "") || (singleton == proc.Name) {
				f.startProcess(idx, i, proc, env, of)
			}
		}
	}

	<-f.teardown.Barrier()

	f.wg.Wait()
}
<commit_msg>Check ps.Start() error<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const shutdownGraceTime = 3 * time.Second

var flagPort int
var flagConcurrency string
var flagRestart bool

var cmdStart = &Command{
	Run:   runStart,
	Usage: "start [process name] [-f procfile] [-e env] [-c concurrency] [-p port] [-r]",
	Short: "Start the application",
	Long: `
Start the application specified by a Procfile (defaults to ./Procfile)

Examples:

  forego start
  forego start web
  forego start -f Procfile.test -e .env.test
`,
}

func init() {
	cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile")
	cmdStart.Flag.StringVar(&flagEnv, "e", "", "env")
	cmdStart.Flag.IntVar(&flagPort, "p", 5000, "port")
	cmdStart.Flag.StringVar(&flagConcurrency, "c", "", "concurrency")
	cmdStart.Flag.BoolVar(&flagRestart, "r", false, "restart")
}

func parseConcurrency(value string) (map[string]int, error) {
	concurrency := map[string]int{}
	if strings.TrimSpace(value) == "" {
		return concurrency, nil
	}

	parts := strings.Split(value, ",")
	for _, part := range parts {
		if !strings.Contains(part, "=") {
			return concurrency, errors.New("Parsing concurency")
		}

		nameValue := strings.Split(part, "=")
		n, v := strings.TrimSpace(nameValue[0]), strings.TrimSpace(nameValue[1])
		if n == "" || v == "" {
			return concurrency, errors.New("Parsing concurency")
		}

		numProcs, err := strconv.ParseInt(v, 10, 16)
		if err != nil {
			return concurrency, err
		}

		concurrency[n] = int(numProcs)
	}
	return concurrency, nil
}

type Forego struct {
	outletFactory *OutletFactory

	teardown, teardownNow Barrier // signal shutting down

	wg sync.WaitGroup
}

func (f *Forego) monitorInterrupt() {
	handler := make(chan os.Signal, 1)
	signal.Notify(handler, os.Interrupt)

	first := true

	for sig := range handler {
		switch sig {
		case os.Interrupt:
			fmt.Println("      | ctrl-c detected")

			f.teardown.Fall()
			if !first {
				f.teardownNow.Fall()
			}
			first = false
		}
	}
}

func (f *Forego) startProcess(idx, procNum int, proc ProcfileEntry, env Env, of *OutletFactory) {
	port := flagPort + (idx * 100)

	const interactive = false
	workDir := filepath.Dir(flagProcfile)
	ps := NewProcess(workDir, proc.Command, env, interactive)
	procName := fmt.Sprint(proc.Name, ".", procNum+1)
	ps.Env["PORT"] = strconv.Itoa(port)

	ps.Stdin = nil

	stdout, err := ps.StdoutPipe()
	if err != nil {
		panic(err)
	}
	stderr, err := ps.StderrPipe()
	if err != nil {
		panic(err)
	}

	pipeWait := new(sync.WaitGroup)
	pipeWait.Add(2)
	go of.LineReader(pipeWait, procName, idx, stdout, false)
	go of.LineReader(pipeWait, procName, idx, stderr, true)

	of.SystemOutput(fmt.Sprintf("starting %s on port %d", procName, port))

	finished := make(chan struct{}) // closed on process exit

	err = ps.Start()
	if err != nil {
		f.teardown.Fall()
		of.SystemOutput(fmt.Sprint("Failed to start ", procName, ": ", err))
		return
	}

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()
		defer close(finished)
		pipeWait.Wait()
		ps.Wait()
	}()

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		// Prevent goroutine from exiting before process has finished.
		defer func() { <-finished }()
		defer f.teardown.Fall()

		select {
		case <-finished:
			if flagRestart {
				f.startProcess(idx, procNum, proc, env, of)
				return
			}

		case <-f.teardown.Barrier():
			// Forego tearing down

			if !osHaveSigTerm {
				of.SystemOutput(fmt.Sprintf("Killing %s", procName))
				ps.Process.Kill()
				return
			}

			of.SystemOutput(fmt.Sprintf("sending SIGTERM to %s", procName))
			ps.SendSigTerm()

			// Give the process a chance to exit, otherwise kill it.
			select {
			case <-f.teardownNow.Barrier():
				of.SystemOutput(fmt.Sprintf("Killing %s", procName))
				ps.SendSigKill()
			case <-finished:
			}
		}
	}()
}

func runStart(cmd *Command, args []string) {
	root := filepath.Dir(flagProcfile)

	if flagEnv == "" {
		flagEnv = filepath.Join(root, ".env")
	}

	pf, err := ReadProcfile(flagProcfile)
	handleError(err)

	env, err := ReadEnv(flagEnv)
	handleError(err)

	concurrency, err := parseConcurrency(flagConcurrency)
	handleError(err)

	of := NewOutletFactory()
	of.Padding = pf.LongestProcessName(concurrency)

	f := &Forego{
		outletFactory: of,
	}

	go f.monitorInterrupt()

	// When teardown fires, start the grace timer
	f.teardown.FallHook = func() {
		go func() {
			time.Sleep(shutdownGraceTime)
			of.SystemOutput("Grace time expired")
			f.teardownNow.Fall()
		}()
	}

	var singleton string = ""
	if len(args) > 0 {
		singleton = args[0]
		if !pf.HasProcess(singleton) {
			of.ErrorOutput(fmt.Sprintf("no such process: %s", singleton))
		}
	}

	for idx, proc := range pf.Entries {
		numProcs := 1
		if value, ok := concurrency[proc.Name]; ok {
			numProcs = value
		}
		for i := 0; i < numProcs; i++ {
			if (singleton == "") || (singleton == proc.Name) {
				f.startProcess(idx, i, proc, env, of)
			}
		}
	}

	<-f.teardown.Barrier()

	f.wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/exec"
	"time"

	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

type stats struct {
	Total   int
	Last30d int
	Last7d  int
	Last24h int
	Lines   int
	Time    time.Time
}

func (s stats) FormattedTime() string {
	return s.Time.In(time.UTC).Format(time.RFC850)
}

type tokeiLanguageReport struct {
	Blanks   int `json:"blanks"`
	Code     int `json:"code"`
	Comments int `json:"comments"`
	Stats    []struct {
		Blanks   int    `json:"blanks"`
		Code     int    `json:"code"`
		Comments int    `json:"comments"`
		Lines    int    `json:"lines"`
		Name     string `json:"name"`
	} `json:"stats"`
	Lines int `json:"lines"`
}

type TokeiReport struct {
	CSS        tokeiLanguageReport `json:"Css"`
	Dockerfile tokeiLanguageReport `json:"Dockerfile"`
	Go         tokeiLanguageReport `json:"Go"`
	Hex        tokeiLanguageReport `json:"Hex"`
	HTML       tokeiLanguageReport `json:"Html"`
	Makefile   tokeiLanguageReport `json:"Makefile"`
	Markdown   tokeiLanguageReport `json:"Markdown"`
	Sh         tokeiLanguageReport `json:"Sh"`
	Text       tokeiLanguageReport `json:"Text"`
	Toml       tokeiLanguageReport `json:"Toml"`
	Yaml       tokeiLanguageReport `json:"Yaml"`
}

func getTokeiReport(path string) (*TokeiReport, error) {
	cmd := exec.Command("tokei", "-e", "vendor/", path, "-o", "json")
	buf := new(bytes.Buffer)
	cmd.Stdout = buf
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(buf)
	r := new(TokeiReport)
	if err := decoder.Decode(r); err != nil {
		return nil, err
	}
	return r, nil
}

func getStats(fetch bool) (*stats, error) {
	n := time.Now()
	last30Days := n.AddDate(0, 0, -30)
	last7Days := n.AddDate(0, 0, -7)
	last24h := n.AddDate(0, 0, -1)
	var total, last30DaysTotal, last7DaysTotal, last24hTotal, lines int
	for _, name := range []string{
		"stun", "turn", "sdp", "web", "stund", "tech-status", "ice", "rtc", "gortcd",
		"ansible-role-nginx", "ansible-go", "api",
	} {
		p := "/tmp/gortc-analyze/" + name
		r, err := git.PlainClone(p, false, &git.CloneOptions{
			URL: "https://github.com/gortc/" + name,
		})
		if err == git.ErrRepositoryAlreadyExists {
			r, err = git.PlainOpen(p)
			if err != nil {
				return nil, err
			}
			w, err := r.Worktree()
			if err != nil {
				return nil, err
			}
			if fetch {
				err = w.Pull(&git.PullOptions{
					Force:      true,
					RemoteName: "origin",
				})
				log.Println("pull", name, err)
				if err == git.NoErrAlreadyUpToDate {
					err = nil
				}
			}
		}
		if err != nil {
			return nil, err
		}
		rep, err := getTokeiReport(p)
		if err != nil {
			return nil, err
		}
		lines += rep.Go.Lines + rep.Yaml.Lines
		ref, err := r.Head()
		if err != nil {
			return nil, err
		}
		fmt.Println(name, "head", ref)
		b, err := r.Log(&git.LogOptions{
			From: ref.Hash(),
		})
		var commits int
		countAuthors := map[string]struct{}{
			"ar@cydev.ru":     {},
			"ernado@ya.ru":    {},
			"mail@backkem.me": {},
		}
		if err = b.ForEach(func(commit *object.Commit) error {
			if _, ok := countAuthors[commit.Author.Email]; !ok {
				return nil
			}
			commits++
			if commit.Author.When.After(last30Days) {
				last30DaysTotal++
			}
			if commit.Author.When.After(last7Days) {
				last7DaysTotal++
			}
			if commit.Author.When.After(last24h) {
				last24hTotal++
			}
			return nil
		}); err != nil {
			return nil, err
		}
		total += commits
	}
	return &stats{
		Time:    time.Now(),
		Total:   total,
		Last30d: last30DaysTotal,
		Last7d:  last7DaysTotal,
		Last24h: last24hTotal,
		Lines:   lines,
	}, nil
}
<commit_msg>stats: count Dockerfiles<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/exec"
	"time"

	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

type stats struct {
	Total   int
	Last30d int
	Last7d  int
	Last24h int
	Lines   int
	Time    time.Time
}

func (s stats) FormattedTime() string {
	return s.Time.In(time.UTC).Format(time.RFC850)
}

type tokeiLanguageReport struct {
	Blanks   int `json:"blanks"`
	Code     int `json:"code"`
	Comments int `json:"comments"`
	Stats    []struct {
		Blanks   int    `json:"blanks"`
		Code     int    `json:"code"`
		Comments int    `json:"comments"`
		Lines    int    `json:"lines"`
		Name     string `json:"name"`
	} `json:"stats"`
	Lines int `json:"lines"`
}

type TokeiReport struct {
	CSS        tokeiLanguageReport `json:"Css"`
	Dockerfile tokeiLanguageReport `json:"Dockerfile"`
	Go         tokeiLanguageReport `json:"Go"`
	Hex        tokeiLanguageReport `json:"Hex"`
	HTML       tokeiLanguageReport `json:"Html"`
	Makefile   tokeiLanguageReport `json:"Makefile"`
	Markdown   tokeiLanguageReport `json:"Markdown"`
	Sh         tokeiLanguageReport `json:"Sh"`
	Text       tokeiLanguageReport `json:"Text"`
	Toml       tokeiLanguageReport `json:"Toml"`
	Yaml       tokeiLanguageReport `json:"Yaml"`
}

func getTokeiReport(path string) (*TokeiReport, error) {
	cmd := exec.Command("tokei", "-e", "vendor/", path, "-o", "json")
	buf := new(bytes.Buffer)
	cmd.Stdout = buf
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(buf)
	r := new(TokeiReport)
	if err := decoder.Decode(r); err != nil {
		return nil, err
	}
	return r, nil
}

func getStats(fetch bool) (*stats, error) {
	n := time.Now()
	last30Days := n.AddDate(0, 0, -30)
	last7Days := n.AddDate(0, 0, -7)
	last24h := n.AddDate(0, 0, -1)
	var total, last30DaysTotal, last7DaysTotal, last24hTotal, lines int
	for _, name := range []string{
		"stun", "turn", "sdp", "web", "stund", "tech-status", "ice", "rtc", "gortcd",
		"ansible-role-nginx", "ansible-go", "api",
	} {
		p := "/tmp/gortc-analyze/" + name
		r, err := git.PlainClone(p, false, &git.CloneOptions{
			URL: "https://github.com/gortc/" + name,
		})
		if err == git.ErrRepositoryAlreadyExists {
			r, err = git.PlainOpen(p)
			if err != nil {
				return nil, err
			}
			w, err := r.Worktree()
			if err != nil {
				return nil, err
			}
			if fetch {
				err = w.Pull(&git.PullOptions{
					Force:      true,
					RemoteName: "origin",
				})
				log.Println("pull", name, err)
				if err == git.NoErrAlreadyUpToDate {
					err = nil
				}
			}
		}
		if err != nil {
			return nil, err
		}
		rep, err := getTokeiReport(p)
		if err != nil {
			return nil, err
		}
		lines += rep.Go.Lines + rep.Yaml.Lines + rep.Dockerfile.Lines
		ref, err := r.Head()
		if err != nil {
			return nil, err
		}
		fmt.Println(name, "head", ref)
		b, err := r.Log(&git.LogOptions{
			From: ref.Hash(),
		})
		var commits int
		countAuthors := map[string]struct{}{
			"ar@cydev.ru":     {},
			"ernado@ya.ru":    {},
			"mail@backkem.me": {},
		}
		if err = b.ForEach(func(commit *object.Commit) error {
			if _, ok := countAuthors[commit.Author.Email]; !ok {
				return nil
			}
			commits++
			if commit.Author.When.After(last30Days) {
				last30DaysTotal++
			}
			if commit.Author.When.After(last7Days) {
				last7DaysTotal++
			}
			if commit.Author.When.After(last24h) {
				last24hTotal++
			}
			return nil
		}); err != nil {
			return nil, err
		}
		total += commits
	}
	return &stats{
		Time:    time.Now(),
		Total:   total,
		Last30d: last30DaysTotal,
		Last7d:  last7DaysTotal,
		Last24h: last24hTotal,
		Lines:   lines,
	}, nil
}
<|endoftext|>


<commit_before>package httpcache

type Store interface {
	Get(key string) (*Resource, bool)
	Set(key string, r *Resource)
}

// BUG(lox): Needs a mutex for concurrency
type MapStore struct {
	resources map[string]*Resource
}

func NewMapStore() *MapStore {
	return &MapStore{map[string]*Resource{}}
}

func (m *MapStore) Get(k string) (*Resource, bool) {
	r, ok := m.resources[k]
	return r, ok
}

func (m *MapStore) Set(k string, r *Resource) {
	m.resources[k] = r
}
<commit_msg>MapStore is now safe for concurrent access<commit_after>package httpcache

import "sync"

type Store interface {
	Get(key string) (*Resource, bool)
	Set(key string, r *Resource)
}

type MapStore struct {
	mutex     sync.RWMutex
	resources map[string]*Resource
}

func NewMapStore() *MapStore {
	return &MapStore{resources: map[string]*Resource{}}
}

func (m *MapStore) Get(k string) (*Resource, bool) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()
	r, ok := m.resources[k]
	return r, ok
}

func (m *MapStore) Set(k string, r *Resource) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	m.resources[k] = r
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/url"
)

/*
 *

 *
 head 1	 head 2

 *
 data 1	 data 2

 *
 data 3	 data 4

 *

 */

// all output has to be supplied as struct to respective template
type Entry struct {
	Text string
	Link string
}

type Context struct {
	User     string
	Host     string
	Port     string
	CSS      string
	Database string
	Table    string
	Order    string
	Desc     string
	Back     string
	Head     []Entry
	Records  [][]Entry
	Counter  string
	Left     string
	Right    string
	Trail    []Entry
	Menu     []Entry
}

func makeBack(host string, db string, t string, o string, d string, k string) string {
	q := url.Values{}
	if db != "" {
		q.Add("db", db)
		if t != "" {
			q.Add("t", t)
		}
		if k != "" {
			q.Add("k", k)
		} else if o != "" {
			q.Add("o", o)
			if d != "" {
				q.Add("d", d)
			}
		}
		return q.Encode()
	} else {
		return "/logout"
	}
}

func makeTrail(host string, db string, t string, o string, d string, k string, w string, wq url.Values) []Entry {

	q := url.Values{}

	trail := []Entry{Entry{host, ""}}

	if db != "" {
		q.Add("db", db)
		trail = append(trail, Entry{Link: q.Encode(), Text: db})
	}
	if t != "" {
		q.Add("t", t)
		trail = append(trail, Entry{Link: q.Encode(), Text: t})
	}

	wq.Set("db", db)
	wq.Set("t", t)
	wq.Del("o")
	wq.Del("d")
	if w != "" {
		trail = append(trail, Entry{Link: wq.Encode(), Text: w})
	}
	if o != "" {
		wq.Add("o", o)
		if d != "" {
			trail = append(trail, Entry{Link: wq.Encode(), Text: o + "↓"})
		} else {
			wq.Add("d", "1")
			trail = append(trail, Entry{Link: wq.Encode(), Text: o + "↑"})
		}
	} else if k != "" {
		q.Add("k", k)
		trail = append(trail, Entry{Link: q.Encode(), Text: "# " + k})
	}
	return trail
}

func tableOutSimple(w http.ResponseWriter, cred Access, db string, t string, head []Entry, records [][]Entry, menu []Entry) {

	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    "",
		Desc:     "",
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  "",
		Left:     "",
		Right:    "",
		Trail:    makeTrail(cred.Host, db, t, "", "", "", "", url.Values{}),
		Menu:     menu,
	}
	if DEBUGFLAG {
		initTemplate()
	}
	err := templateTable.Execute(w, c)
	checkY(err)
}

func tableOutRows(w http.ResponseWriter, cred Access, db string, t string, o string, d string,
	n string, linkleft string, linkright string,
	head []Entry, records [][]Entry, menu []Entry, where string, whereQ url.Values) {

	initTemplate()
	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    o,
		Desc:     d,
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  n,
		Left:     linkleft,
		Right:    linkright,
		Trail:    makeTrail(cred.Host, db, t, o, d, "", where, whereQ),
		Menu:     menu,
	}

	err := templateTable.Execute(w, c)
	checkY(err)
}

func tableOutFields(w http.ResponseWriter, cred Access, db string, t string, o string, d string, k string, n string, linkleft string, linkright string, head []Entry, records [][]Entry, menu []Entry) {

	initTemplate()

	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    o,
		Desc:     d,
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  n,
		Left:     linkleft,
		Right:    linkright,
		Trail:    makeTrail(cred.Host, db, t, o, d, k, "", url.Values{}),
		Menu:     menu,
	}

	err := templateTable.Execute(w, c)
	checkY(err)
}
<commit_msg>Removed toggling of direction in trail. Was unexpected<commit_after>package main

import (
	"net/http"
	"net/url"
)

/*
 *

 *
 head 1	 head 2

 *
 data 1	 data 2

 *
 data 3	 data 4

 *

 */

// all output has to be supplied as struct to respective template
type Entry struct {
	Text string
	Link string
}

type Context struct {
	User     string
	Host     string
	Port     string
	CSS      string
	Database string
	Table    string
	Order    string
	Desc     string
	Back     string
	Head     []Entry
	Records  [][]Entry
	Counter  string
	Left     string
	Right    string
	Trail    []Entry
	Menu     []Entry
}

func makeBack(host string, db string, t string, o string, d string, k string) string {
	q := url.Values{}
	if db != "" {
		q.Add("db", db)
		if t != "" {
			q.Add("t", t)
		}
		if k != "" {
			q.Add("k", k)
		} else if o != "" {
			q.Add("o", o)
			if d != "" {
				q.Add("d", d)
			}
		}
		return q.Encode()
	} else {
		return "/logout"
	}
}

func makeTrail(host string, db string, t string, o string, d string, k string, w string, wq url.Values) []Entry {

	q := url.Values{}

	trail := []Entry{Entry{host, ""}}

	if db != "" {
		q.Add("db", db)
		trail = append(trail, Entry{Link: q.Encode(), Text: db})
	}
	if t != "" {
		q.Add("t", t)
		trail = append(trail, Entry{Link: q.Encode(), Text: t})
	}

	wq.Set("db", db)
	wq.Set("t", t)
	wq.Del("o")
	wq.Del("d")
	if w != "" {
		trail = append(trail, Entry{Link: wq.Encode(), Text: w})
	}
	if o != "" {
		wq.Add("o", o)
		if d != "" {
			wq.Add("d", "1")
			trail = append(trail, Entry{Link: wq.Encode(), Text: o + "↓"})
		} else {
			trail = append(trail, Entry{Link: wq.Encode(), Text: o + "↑"})
		}
	} else if k != "" {
		q.Add("k", k)
		trail = append(trail, Entry{Link: q.Encode(), Text: "# " + k})
	}
	return trail
}

func tableOutSimple(w http.ResponseWriter, cred Access, db string, t string, head []Entry, records [][]Entry, menu []Entry) {

	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    "",
		Desc:     "",
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  "",
		Left:     "",
		Right:    "",
		Trail:    makeTrail(cred.Host, db, t, "", "", "", "", url.Values{}),
		Menu:     menu,
	}
	if DEBUGFLAG {
		initTemplate()
	}
	err := templateTable.Execute(w, c)
	checkY(err)
}

func tableOutRows(w http.ResponseWriter, cred Access, db string, t string, o string, d string,
	n string, linkleft string, linkright string,
	head []Entry, records [][]Entry, menu []Entry, where string, whereQ url.Values) {

	initTemplate()
	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    o,
		Desc:     d,
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  n,
		Left:     linkleft,
		Right:    linkright,
		Trail:    makeTrail(cred.Host, db, t, o, d, "", where, whereQ),
		Menu:     menu,
	}

	err := templateTable.Execute(w, c)
	checkY(err)
}

func tableOutFields(w http.ResponseWriter, cred Access, db string, t string, o string, d string, k string, n string, linkleft string, linkright string, head []Entry, records [][]Entry, menu []Entry) {

	initTemplate()

	c := Context{
		User:     cred.User,
		Host:     cred.Host,
		Port:     cred.Port,
		CSS:      CSS_FILE,
		Database: db,
		Table:    t,
		Order:    o,
		Desc:     d,
		Records:  records,
		Head:     head,
		Back:     makeBack(cred.Host, db, t, "", "", ""),
		Counter:  n,
		Left:     linkleft,
		Right:    linkright,
		Trail:    makeTrail(cred.Host, db, t, o, d, k, "", url.Values{}),
		Menu:     menu,
	}

	err := templateTable.Execute(w, c)
	checkY(err)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"reflect"
)

const (
	DEFAULT_FILE_MODE = 0777
)

type Task func() error

type Constructor func(target *Target, args Object) (Task, error)

var tasksMap map[string]Constructor

func init() {
	tasksMap = map[string]Constructor{
		"go":     Go,
		"print":  Print,
		"cd":     Cd,
		"mkdir":  MkDir,
		"if":     If,
		"for":    For,
		"delete": Delete,
	}
}

// TASKS DEFINITIONS

func Go(target *Target, args Object) (Task, error) {
	source, ok := args["go"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task go must be a string")
	}
	return func() error {
		_, err := target.Build.Context.Evaluate(source)
		if err != nil {
			return fmt.Errorf("evaluating go source: %v", err)
		}
		return nil
	}, nil
}

func Print(target *Target, args Object) (Task, error) {
	message, ok := args["print"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task print must be a string")
	}
	return func() error {
		evaluated, err := target.Build.Context.ReplaceProperties(message)
		if err != nil {
			return fmt.Errorf("processing print argument: %v", err)
		}
		fmt.Println(evaluated)
		return nil
	}, nil
}

func Cd(target *Target, args Object) (Task, error) {
	dir, ok := args["cd"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task cd must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Changing to directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing cd argument: %v", err)
		}
		err = os.Chdir(directory)
		if err != nil {
			return fmt.Errorf("changing to directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func MkDir(target *Target, args Object) (Task, error) {
	dir, ok := args["mkdir"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task mkdir must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Making directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing mkdir argument: %v", err)
		}
		err = os.MkdirAll(directory, DEFAULT_FILE_MODE)
		if err != nil {
			return fmt.Errorf("making directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func If(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"if", "then", "else"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"if", "then"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	condition, err := args.GetString("if")
	if err != nil {
		return nil, fmt.Errorf("evaluating if construct: %v", err)
	}
	thenSteps, err := ParseSteps(target, args, "then")
	if err != nil {
		return nil, err
	}
	var elseSteps []Step
	if FieldInList("else", fields) {
		elseSteps, err = ParseSteps(target, args, "else")
		if err != nil {
			return nil, err
		}
	}
	return func() error {
		result, err := target.Build.Context.Evaluate(condition)
		if err != nil {
			return fmt.Errorf("evaluating if condition: %v", err)
		}
		boolean, ok := result.(bool)
		if !ok {
			return fmt.Errorf("evaluating if condition: must return a bool")
		}
		if boolean {
			err := RunSteps(thenSteps)
			if err != nil {
				return err
			}
		} else {
			err := RunSteps(elseSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func For(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	variable, err := args.GetString("for")
	if err != nil {
		return nil, fmt.Errorf("'for' field of a 'for' loop must be a string")
	}
	list, err := args.GetList("in")
	expression := ""
	if err != nil {
		expression, err = args.GetString("in")
		if err != nil {
			return nil, fmt.Errorf("'in' field of 'for' loop must be a list or string")
		}
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		if expression != "" {
			result, err := target.Build.Context.Evaluate(expression)
			if err != nil {
				return fmt.Errorf("evaluating in field of for loop: %v", err)
			}
			list, err = ToList(result)
			if err != nil {
				return fmt.Errorf("'in' field of 'for' loop must be an expression that returns a list")
			}
		}
		for _, value := range list {
			target.Build.Context.SetProperty(variable, value)
			if err != nil {
				return err
			}
			err := RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil

}

func Delete(target *Target, args Object) (Task, error) {
	directories, err := args.GetListStringsOrString("delete")
	if err != nil {
		return nil, fmt.Errorf("delete argument must be string or list of strings")
	}
	return func() error {
		for _, dir := range directories {
			directory, err := target.Build.Context.ReplaceProperties(dir)
			if err != nil {
				return fmt.Errorf("evaluating directory in task delete: %v", err)
			}
			if _, err := os.Stat(directory); err == nil {
				fmt.Printf("Deleting directory '%s'\n", directory)
				err = os.RemoveAll(directory)
				if err != nil {
					return fmt.Errorf("deleting directory '%s': %v", directory, err)
				}
			}
		}
		return nil
	}, nil
}

// UTILITY FUNCTIONS

func FieldsInList(fields, list []string) error {
	for _, field := range fields {
		found := false
		for _, e := range list {
			if e == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("unknown field '%s'", field)
		}
	}
	return nil
}

func FieldInList(field string, list []string) bool {
	for _, f := range list {
		if f == field {
			return true
		}
	}
	return false
}

func FieldsMandatory(fields, mandatory []string) error {
	for _, manda := range mandatory {
		found := false
		for _, field := range fields {
			if manda == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("mandatory field '%s' not found", manda)
		}
	}
	return nil
}

func ParseSteps(target *Target, object Object, field string) ([]Step, error) {
	list, err := object.GetList(field)
	if err != nil {
		return nil, err
	}
	var steps []Step
	for index, element := range list {
		target.Build.Log("Parsing step %v in %s field", index, field)
		step, err := NewStep(target, element)
		if err != nil {
			return nil, fmt.Errorf("parsing target '%s': %v", target.Name, err)
		}
		steps = append(steps, step)
	}
	return steps, nil
}

func RunSteps(steps []Step) error {
	for _, step := range steps {
		err := step.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func ToList(object interface{}) ([]interface{}, error) {
	slice := reflect.ValueOf(object)
	if slice.Kind() == reflect.Slice {
		result := make([]interface{}, slice.Len())
		for i := 0; i < slice.Len(); i++ {
			result[i] = slice.Index(i).Interface()
		}
		return result, nil
	} else {
		return nil, fmt.Errorf("must be a list")
	}
}
<commit_msg>Added while task<commit_after>package main

import (
	"fmt"
	"os"
	"reflect"
)

const (
	DEFAULT_FILE_MODE = 0777
)

type Task func() error

type Constructor func(target *Target, args Object) (Task, error)

var tasksMap map[string]Constructor

func init() {
	tasksMap = map[string]Constructor{
		"go":     Go,
		"print":  Print,
		"cd":     Cd,
		"mkdir":  MkDir,
		"if":     If,
		"for":    For,
		"while":  While,
		"delete": Delete,
	}
}

// TASKS DEFINITIONS

func Go(target *Target, args Object) (Task, error) {
	source, ok := args["go"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task go must be a string")
	}
	return func() error {
		_, err := target.Build.Context.Evaluate(source)
		if err != nil {
			return fmt.Errorf("evaluating go source: %v", err)
		}
		return nil
	}, nil
}

func Print(target *Target, args Object) (Task, error) {
	message, ok := args["print"].(string)
	if !ok {
		return nil, fmt.Errorf("argument of task print must be a string")
	}
	return func() error {
		evaluated, err := target.Build.Context.ReplaceProperties(message)
		if err != nil {
			return fmt.Errorf("processing print argument: %v", err)
		}
		fmt.Println(evaluated)
		return nil
	}, nil
}

func Cd(target *Target, args Object) (Task, error) {
	dir, ok := args["cd"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task cd must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Changing to directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing cd argument: %v", err)
		}
		err = os.Chdir(directory)
		if err != nil {
			return fmt.Errorf("changing to directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func MkDir(target *Target, args Object) (Task, error) {
	dir, ok := args["mkdir"].(string)
	if !ok {
		return nil, fmt.Errorf("argument to task mkdir must be a string")
	}
	return func() error {
		directory, err := target.Build.Context.ReplaceProperties(dir)
		fmt.Printf("Making directory '%s'\n", directory)
		if err != nil {
			return fmt.Errorf("processing mkdir argument: %v", err)
		}
		err = os.MkdirAll(directory, DEFAULT_FILE_MODE)
		if err != nil {
			return fmt.Errorf("making directory '%s': %s", directory, err)
		}
		return nil
	}, nil
}

func If(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"if", "then", "else"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"if", "then"}); err != nil {
		return nil, fmt.Errorf("building if condition: %v", err)
	}
	condition, err := args.GetString("if")
	if err != nil {
		return nil, fmt.Errorf("evaluating if construct: %v", err)
	}
	thenSteps, err := ParseSteps(target, args, "then")
	if err != nil {
		return nil, err
	}
	var elseSteps []Step
	if FieldInList("else", fields) {
		elseSteps, err = ParseSteps(target, args, "else")
		if err != nil {
			return nil, err
		}
	}
	return func() error {
		result, err := target.Build.Context.Evaluate(condition)
		if err != nil {
			return fmt.Errorf("evaluating 'if' condition: %v", err)
		}
		boolean, ok := result.(bool)
		if !ok {
			return fmt.Errorf("evaluating if condition: must return a bool")
		}
		if boolean {
			err := RunSteps(thenSteps)
			if err != nil {
				return err
			}
		} else {
			err := RunSteps(elseSteps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func For(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"for", "in", "do"}); err != nil {
		return nil, fmt.Errorf("building 'for' loop: %v", err)
	}
	variable, err := args.GetString("for")
	if err != nil {
		return nil, fmt.Errorf("'for' field of a 'for' loop must be a string")
	}
	list, err := args.GetList("in")
	expression := ""
	if err != nil {
		expression, err = args.GetString("in")
		if err != nil {
			return nil, fmt.Errorf("'in' field of 'for' loop must be a list or string")
		}
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		if expression != "" {
			result, err := target.Build.Context.Evaluate(expression)
			if err != nil {
				return fmt.Errorf("evaluating in field of for loop: %v", err)
			}
			list, err = ToList(result)
			if err != nil {
				return fmt.Errorf("'in' field of 'for' loop must be an expression that returns a list")
			}
		}
		for _, value := range list {
			target.Build.Context.SetProperty(variable, value)
			if err != nil {
				return err
			}
			err := RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil

}

func While(target *Target, args Object) (Task, error) {
	fields := args.Fields()
	if err := FieldsInList(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	if err := FieldsMandatory(fields, []string{"while", "do"}); err != nil {
		return nil, fmt.Errorf("building 'while' loop: %v", err)
	}
	condition, err := args.GetString("while")
	if err != nil {
		return nil, fmt.Errorf("'while' field of a 'while' loop must be a string")
	}
	steps, err := ParseSteps(target, args, "do")
	if err != nil {
		return nil, err
	}
	return func() error {
		for {
			result, err := target.Build.Context.Evaluate(condition)
			if err != nil {
				return fmt.Errorf("evaluating 'while' field of 'while' loop: %v", err)
			}
			loop, ok := result.(bool)
			if !ok {
				return fmt.Errorf("evaluating 'while' condition: must return a bool")
			}
			if !loop {
				break
			}
			err = RunSteps(steps)
			if err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func Delete(target *Target, args Object) (Task, error) {
	directories, err := args.GetListStringsOrString("delete")
	if err != nil {
		return nil, fmt.Errorf("delete argument must be string or list of strings")
	}
	return func() error {
		for _, dir := range directories {
			directory, err := target.Build.Context.ReplaceProperties(dir)
			if err != nil {
				return fmt.Errorf("evaluating directory in task delete: %v", err)
			}
			if _, err := os.Stat(directory); err == nil {
				fmt.Printf("Deleting directory '%s'\n", directory)
				err = os.RemoveAll(directory)
				if err != nil {
					return fmt.Errorf("deleting directory '%s': %v", directory, err)
				}
			}
		}
		return nil
	}, nil
}

// UTILITY FUNCTIONS

func FieldsInList(fields, list []string) error {
	for _, field := range fields {
		found := false
		for _, e := range list {
			if e == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("unknown field '%s'", field)
		}
	}
	return nil
}

func FieldInList(field string, list []string) bool {
	for _, f := range list {
		if f == field {
			return true
		}
	}
	return false
}

func FieldsMandatory(fields, mandatory []string) error {
	for _, manda := range mandatory {
		found := false
		for _, field := range fields {
			if manda == field {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("mandatory field '%s' not found", manda)
		}
	}
	return nil
}

func ParseSteps(target *Target, object Object, field string) ([]Step, error) {
	list, err := object.GetList(field)
	if err != nil {
		return nil, err
	}
	var steps []Step
	for index, element := range list {
		target.Build.Log("Parsing step %v in %s field", index, field)
		step, err := NewStep(target, element)
		if err != nil {
			return nil, fmt.Errorf("parsing target '%s': %v", target.Name, err)
		}
		steps = append(steps, step)
	}
	return steps, nil
}

func RunSteps(steps []Step) error {
	for _, step := range steps {
		err := step.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func ToList(object interface{}) ([]interface{}, error) {
	slice := reflect.ValueOf(object)
	if slice.Kind() == reflect.Slice {
		result := make([]interface{}, slice.Len())
		for i := 0; i < slice.Len(); i++ {
			result[i] = slice.Index(i).Interface()
		}
		return result, nil
	} else {
		return nil, fmt.Errorf("must be a list")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package configure

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strings"
)

// KafkaConfig holds the configuration for the Kafka client
type KafkaConfig struct {
	KafkaClusterConfigFile string `yaml:"kafkaClusterConfigFile"`
	ClustersConfig         ClustersConfig
}

// ClustersConfig holds the configuration for the Kafka clusters
type ClustersConfig struct {
	Clusters map[string]ClusterConfig `yaml:"clusters"`
}

// ClusterConfig holds the configuration for a single Kafka cluster
type ClusterConfig struct {
	Brokers    []string `yaml:"brokers"`
	Zookeepers []string `yaml:"zookeepers"`
	Chroot     string   `yaml:"chroot"`
}

// NewCommonKafkaConfig instantiates a Kafka config
func NewCommonKafkaConfig() *KafkaConfig {
	return &KafkaConfig{}
}

// GetKafkaClusters returns all kafka cluster names
func (r *KafkaConfig) GetKafkaClusters() []string {
	r.loadClusterConfigFileIfNecessary()

	ret := make([]string, 0, len(r.ClustersConfig.Clusters))
	for key := range r.ClustersConfig.Clusters {
		ret = append(ret, key)
	}
	return ret
}

// GetKafkaClusterConfig returns all kafka cluster names
func (r *KafkaConfig) GetKafkaClusterConfig(cluster string) (ClusterConfig, bool) {
	r.loadClusterConfigFileIfNecessary()

	val, ok := r.ClustersConfig.Clusters[cluster]
	return val, ok
}

func (r *KafkaConfig) loadClusterConfigFileIfNecessary() {
	if len(r.ClustersConfig.Clusters) > 0 {
		return
	}

	r.loadClusterConfigFile()
}

func (r *KafkaConfig) loadClusterConfigFile() {
	// TODO do we need to detect file change and reload on file change
	if len(r.KafkaClusterConfigFile) == 0 {
		log.Warnf("Could not load kafka config because kafka cluster config file is not configured")
		return
	}

	contents, err := ioutil.ReadFile(r.KafkaClusterConfigFile)
	if err != nil {
		log.Warnf("Failed to load kafka cluster config file %s: %v", r.KafkaClusterConfigFile, err)
	}

	log.Println("Parsing kafka cluster config file", r.KafkaClusterConfigFile, ":", string(contents))
	clusters := ClustersConfig{}
	if err := yaml.Unmarshal(contents, &clusters); err != nil {
		log.Warnf("Failed to parse kafka cluster config file %s: %v", r.KafkaClusterConfigFile, err)
	} else {
		r.ClustersConfig = clusters
	}
	r.addPortNumbers()
}

// addPortNumbers adds the default Kafka broker port number to all broker names
func (r *KafkaConfig) addPortNumbers() {
	for _, c := range r.ClustersConfig.Clusters {
		for i, b := range c.Brokers {
			if !strings.Contains(b, `:`) {
				c.Brokers[i] = b + `:9092`
			}
		}
	}
}
<commit_msg>fix race when updating kafka config (#179)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package configure

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strings"
	"sync/atomic"
)

// KafkaConfig holds the configuration for the Kafka client
type KafkaConfig struct {
	KafkaClusterConfigFile string `yaml:"kafkaClusterConfigFile"`
	ClustersConfig         atomic.Value
}

// ClustersConfig holds the configuration for the Kafka clusters
type ClustersConfig struct {
	Clusters map[string]ClusterConfig `yaml:"clusters"`
}

// ClusterConfig holds the configuration for a single Kafka cluster
type ClusterConfig struct {
	Brokers    []string `yaml:"brokers"`
	Zookeepers []string `yaml:"zookeepers"`
	Chroot     string   `yaml:"chroot"`
}

// NewCommonKafkaConfig instantiates a Kafka config
func NewCommonKafkaConfig() *KafkaConfig {
	return &KafkaConfig{}
}

// GetKafkaClusters returns all kafka cluster names
func (r *KafkaConfig) GetKafkaClusters() []string {
	r.loadConfig()

	clusters := r.ClustersConfig.Load().(ClustersConfig).Clusters
	ret := make([]string, 0, len(clusters))
	for key := range clusters {
		ret = append(ret, key)
	}
	return ret
}

// GetKafkaClusterConfig returns all kafka cluster names
func (r *KafkaConfig) GetKafkaClusterConfig(cluster string) (ClusterConfig, bool) {
	r.loadConfig()

	val, ok := r.ClustersConfig.Load().(ClustersConfig).Clusters[cluster]
	return val, ok
}

func (r *KafkaConfig) loadConfig() {
	// check if we have already loaded the cluster config file
	if cfg := r.ClustersConfig.Load(); cfg != nil && len(cfg.(ClustersConfig).Clusters) > 0 {
		return
	}

	// TODO do we need to detect file change and reload on file change
	if len(r.KafkaClusterConfigFile) == 0 {
		log.Warnf("Could not load kafka config because kafka cluster config file is not configured")
		return
	}

	contents, err := ioutil.ReadFile(r.KafkaClusterConfigFile)
	if err != nil {
		log.Warnf("Failed to load kafka cluster config file %s: %v", r.KafkaClusterConfigFile, err)
	}

	log.Println("Parsing kafka cluster config file", r.KafkaClusterConfigFile, ":", string(contents))
	clusters := ClustersConfig{}
	if err := yaml.Unmarshal(contents, &clusters); err != nil {
		log.Warnf("Failed to parse kafka cluster config file %s: %v", r.KafkaClusterConfigFile, err)
	} else {
		r.ClustersConfig.Store(clusters)
	}
	r.addPortNumbers()
}

// addPortNumbers adds the default Kafka broker port number to all broker names
func (r *KafkaConfig) addPortNumbers() {
	clusters := r.ClustersConfig.Load().(ClustersConfig).Clusters
	for _, c := range clusters {
		for i, b := range c.Brokers {
			if !strings.Contains(b, `:`) {
				c.Brokers[i] = b + `:9092`
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 ratelimiter

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

var testlimits = []int{1, 10, 50, 100, 1000}

func TestRateLimiterSingleThreaded(t *testing.T) {
	for i, limit := range testlimits {
		t.Run(fmt.Sprintf("%d ops/s", limit), func(t *testing.T) {
			t.Parallel()
			l := NewLimiter(limit)

			numOps := 3 * limit
			start := time.Now()
			// Need to call the limiter one extra time to ensure that the throughput
			// calculation is correct (because e.g. at 1 qps you can do 3 calls in
			// 2+epsilon seconds)
			for i := 0; i < numOps+1; i++ {
				l.Wait()
			}
			ds := float64(time.Since(start) / time.Second)
			qps := float64(numOps) / ds
			if qps > float64(limit) {
				t.Errorf("#%d: Too many operations per second. Expected ~%d, got %f", i, limit, qps)
			}
		})
	}
}

func TestRateLimiterGoroutines(t *testing.T) {
	for i, limit := range testlimits {
		t.Run(fmt.Sprintf("%d ops/s", limit), func(t *testing.T) {
			t.Parallel()
			l := NewLimiter(limit)

			numOps := 3 * limit
			var wg sync.WaitGroup
			start := time.Now()
			// Need to call the limiter one extra time to ensure that the throughput
			// calculation is correct (because e.g. at 1 qps you can do 3 calls in
			// 2+epsilon seconds)
			for i := 0; i < numOps+1; i++ {
				wg.Add(1)
				go func() {
					l.Wait()
					wg.Done()
				}()
			}
			wg.Wait()
			ds := float64(time.Since(start) / time.Second)
			qps := float64(numOps) / ds
			if qps > float64(limit) {
				t.Errorf("#%d: Too many operations per second. Expected ~%d, got %f", i, limit, qps)
			}
		})
	}
}
<commit_msg>Fix still flaking ratelimiter_test (#646)<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 ratelimiter

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

var testlimits = []int{1, 10, 50, 100, 1000}

func TestRateLimiterSingleThreaded(t *testing.T) {
	for i, limit := range testlimits {
		t.Run(fmt.Sprintf("%d ops/s", limit), func(t *testing.T) {
			t.Parallel()
			l := NewLimiter(limit)

			numOps := 3 * limit
			start := time.Now()
			// Need to call the limiter one extra time to ensure that the throughput
			// calculation is correct (because e.g. at 1 qps you can do 3 calls in
			// 2+epsilon seconds)
			for i := 0; i < numOps+1; i++ {
				l.Wait()
			}
			ds := float64(time.Since(start)) / float64(time.Second)
			qps := float64(numOps) / ds
			if qps > float64(limit)*1.01 {
				t.Errorf("#%d: Too many operations per second. Expected ~%d, got %f", i, limit, qps)
			}
		})
	}
}

func TestRateLimiterGoroutines(t *testing.T) {
	for i, limit := range testlimits {
		t.Run(fmt.Sprintf("%d ops/s", limit), func(t *testing.T) {
			t.Parallel()
			l := NewLimiter(limit)

			numOps := 3 * limit
			var wg sync.WaitGroup
			start := time.Now()
			// Need to call the limiter one extra time to ensure that the throughput
			// calculation is correct (because e.g. at 1 qps you can do 3 calls in
			// 2+epsilon seconds)
			for i := 0; i < numOps+1; i++ {
				wg.Add(1)
				go func() {
					l.Wait()
					wg.Done()
				}()
			}
			wg.Wait()
			ds := float64(time.Since(start)) / float64(time.Second)
			qps := float64(numOps) / ds
			if qps > float64(limit)*1.01 {
				t.Errorf("#%d: Too many operations per second. Expected ~%d, got %f", i, limit, qps)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxdclient

import (
	"fmt"
	"net"
	"net/url"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

// TODO(ericsnow) Address licensing.

// addServer adds the given remote info to the provided config.
// The implementation is copied from:
//  https://github.com/lxc/lxd/blob/master/lxc/remote.go
// TODO(ericsnow) Remove this once the lxd code exports it.
func addServer(config *lxd.Config, server string, addr string) error {
	addr, err := fixAddr(addr)
	if err != nil {
		return err
	}

	if config.Remotes == nil {
		config.Remotes = make(map[string]lxd.RemoteConfig)
	}

	/* Actually add the remote */
	config.Remotes[server] = lxd.RemoteConfig{Addr: addr}

	return nil
}

func fixAddr(addr string) (string, error) {
	var r_scheme string
	var r_host string
	var r_port string

	/* Complex remote URL parsing */
	remote_url, err := url.Parse(addr)
	if err != nil {
		return "", err
	}

	if remote_url.Scheme != "" {
		if remote_url.Scheme != "unix" && remote_url.Scheme != "https" {
			r_scheme = "https"
		} else {
			r_scheme = remote_url.Scheme
		}
	} else if addr[0] == '/' {
		r_scheme = "unix"
	} else {
		if !shared.PathExists(addr) {
			r_scheme = "https"
		} else {
			r_scheme = "unix"
		}
	}

	if remote_url.Host != "" {
		r_host = remote_url.Host
	} else {
		r_host = addr
	}

	host, port, err := net.SplitHostPort(r_host)
	if err == nil {
		r_host = host
		r_port = port
	} else {
		r_port = shared.DefaultPort
	}

	if r_scheme == "unix" {
		if addr[0:5] == "unix:" {
			if addr[0:7] == "unix://" {
				if len(addr) > 8 {
					r_host = addr[8:]
				} else {
					r_host = ""
				}
			} else {
				r_host = addr[6:]
			}
		}
		r_port = ""
	}

	if strings.Contains(r_host, ":") && !strings.HasPrefix(r_host, "[") {
		r_host = fmt.Sprintf("[%s]", r_host)
	}

	if r_port != "" {
		addr = r_scheme + "://" + r_host + ":" + r_port
	} else {
		addr = r_scheme + "://" + r_host
	}

	return addr, nil
}
<commit_msg>Fix up a TODO.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxdclient

import (
	"fmt"
	"net"
	"net/url"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

// TODO(ericsnow) This file should be removed as soon as possible.
// It is copied from https://github.com/lxc/lxd/blob/master/lxc/remote.go
// and the code there should instead be exported so we could use it here.

// TODO(ericsnow) Address licensing.

// addServer adds the given remote info to the provided config.
// The implementation is derived from:
//  https://github.com/lxc/lxd/blob/master/lxc/remote.go
// Note that some validation code was removed (we don't need it).
func addServer(config *lxd.Config, server string, addr string) error {
	addr, err := fixAddr(addr)
	if err != nil {
		return err
	}

	if config.Remotes == nil {
		config.Remotes = make(map[string]lxd.RemoteConfig)
	}

	/* Actually add the remote */
	config.Remotes[server] = lxd.RemoteConfig{Addr: addr}

	return nil
}

// fixAddr is the portion of the original addServer function that fixes
// up the provided addr to be a fully qualified URL.
func fixAddr(addr string) (string, error) {
	var r_scheme string
	var r_host string
	var r_port string

	/* Complex remote URL parsing */
	remote_url, err := url.Parse(addr)
	if err != nil {
		return "", err
	}

	if remote_url.Scheme != "" {
		if remote_url.Scheme != "unix" && remote_url.Scheme != "https" {
			r_scheme = "https"
		} else {
			r_scheme = remote_url.Scheme
		}
	} else if addr[0] == '/' {
		r_scheme = "unix"
	} else {
		if !shared.PathExists(addr) {
			r_scheme = "https"
		} else {
			r_scheme = "unix"
		}
	}

	if remote_url.Host != "" {
		r_host = remote_url.Host
	} else {
		r_host = addr
	}

	host, port, err := net.SplitHostPort(r_host)
	if err == nil {
		r_host = host
		r_port = port
	} else {
		r_port = shared.DefaultPort
	}

	if r_scheme == "unix" {
		if addr[0:5] == "unix:" {
			if addr[0:7] == "unix://" {
				if len(addr) > 8 {
					r_host = addr[8:]
				} else {
					r_host = ""
				}
			} else {
				r_host = addr[6:]
			}
		}
		r_port = ""
	}

	if strings.Contains(r_host, ":") && !strings.HasPrefix(r_host, "[") {
		r_host = fmt.Sprintf("[%s]", r_host)
	}

	if r_port != "" {
		addr = r_scheme + "://" + r_host + ":" + r_port
	} else {
		addr = r_scheme + "://" + r_host
	}

	return addr, nil
}
<|endoftext|>