<commit_before>package lzmadec

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeLayout = "2006-01-02 15:04:05"
)

var (
	// Err7zNotAvailable is returned if 7z executable is not available
	Err7zNotAvailable  = errors.New("7z executable not available")
	ErrNoEntries       = errors.New("no entries in 7z file")
	ErrUnexpectedLines = errors.New("unexpected number of lines")
	detectionStateOf7z int // 0 - not checked, 1 - checked and present, 2 - checked and not present
	mu                 sync.Mutex
)

// Archive describes a single .7z archive
type Archive struct {
	Path    string
	Entries []Entry
}

// Entry describes a single file inside .7z archive
type Entry struct {
	Path string
	Size int
	// -1 means "size unknown"
	PackedSize int
	Modified   time.Time
	Attributes string
	CRC        string
	Encrypted  string
	Method     string
	Block      int
}

func detect7zCached() error {
	mu.Lock()
	defer mu.Unlock()
	if detectionStateOf7z == 0 {
		cmd := exec.Command("7z")
		_, err := cmd.CombinedOutput()
		if err != nil {
			detectionStateOf7z = 2
		} else {
			detectionStateOf7z = 1
		}
	}
	if detectionStateOf7z == 1 {
		// checked and present
		return nil
	}
	// checked and not present
	return Err7zNotAvailable
}

/*
----------
Path = Badges.xml
Size = 4065633
Packed Size = 18990516
Modified = 2015-03-09 14:30:49
Attributes = ....A
CRC = 2C468F32
Encrypted = -
Method = BZip2
Block = 0
*/
func advanceToFirstEntry(scanner *bufio.Scanner) error {
	for scanner.Scan() {
		s := scanner.Text()
		if s == "----------" {
			return nil
		}
	}
	err := scanner.Err()
	if err == nil {
		err = ErrNoEntries
	}
	return err
}

func getEntryLines(scanner *bufio.Scanner) ([]string, error) {
	var res []string
	for scanner.Scan() {
		s := scanner.Text()
		s = strings.TrimSpace(s)
		if s == "" {
			break
		}
		res = append(res, s)
	}
	err := scanner.Err()
	if err != nil {
		return nil, err
	}
	if len(res) == 9 || len(res) == 0 {
		return res, nil
	}
	return nil, ErrUnexpectedLines
}

func parseEntryLines(lines []string) (Entry, error) {
	var e Entry
	var err error
	for _, s := range lines {
		parts := strings.SplitN(s, " =", 2)
		if len(parts) != 2 {
			return e, fmt.Errorf("unexpected line: '%s'", s)
		}
		name := strings.ToLower(parts[0])
		v := strings.TrimSpace(parts[1])
		switch name {
		case "path":
			e.Path = v
		case "size":
			e.Size, err = strconv.Atoi(v)
		case "packed size":
			e.PackedSize = -1
			if v != "" {
				e.PackedSize, err = strconv.Atoi(v)
			}
		case "modified":
			e.Modified, err = time.Parse(timeLayout, v)
		case "attributes":
			e.Attributes = v
		case "crc":
			e.CRC = v
		case "encrypted":
			e.Encrypted = v
		case "method":
			e.Method = v
		case "block":
			e.Block, err = strconv.Atoi(v)
		default:
			err = fmt.Errorf("unexpected entry line '%s'", name)
		}
		if err != nil {
			return e, err
		}
	}
	return e, nil
}

func parse7zListOutput(d []byte) ([]Entry, error) {
	var res []Entry
	r := bytes.NewBuffer(d)
	scanner := bufio.NewScanner(r)
	err := advanceToFirstEntry(scanner)
	if err != nil {
		return nil, err
	}
	for {
		lines, err := getEntryLines(scanner)
		if err != nil {
			return nil, err
		}
		if len(lines) == 0 {
			// last entry
			break
		}
		e, err := parseEntryLines(lines)
		if err != nil {
			return nil, err
		}
		res = append(res, e)
	}
	return res, nil
}

// NewArchive uses 7z to extract a list of files in .7z archive
func NewArchive(path string) (*Archive, error) {
	err := detect7zCached()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("7z", "l", "-slt", path)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	entries, err := parse7zListOutput(out)
	if err != nil {
		return nil, err
	}
	return &Archive{
		Path:    path,
		Entries: entries,
	}, nil
}

type readCloser struct {
	rc  io.ReadCloser
	cmd *exec.Cmd
}

func (rc *readCloser) Read(p []byte) (int, error) {
	return rc.rc.Read(p)
}

func (rc *readCloser) Close() error {
	// if we want to finish before reading all the data, we need to close
	// it all the data has already been read, or else rc.cmd.Wait() will hang
	// if it's already closed then Close() will return 'invalid argument',
	// which we can ignore
	rc.rc.Close()
	return rc.cmd.Wait()
}

func (a *Archive) ExtractReader(name string) (io.ReadCloser, error) {
	found := false
	for _, e := range a.Entries {
		if e.Path == name {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.New("File not in the archive")
	}
	cmd := exec.Command("7z", "x", "-so", a.Path, name)
	stdout, err := cmd.StdoutPipe()
	rc := &readCloser{
		rc:  stdout,
		cmd: cmd,
	}
	err = cmd.Start()
	if err != nil {
		stdout.Close()
		return nil, err
	}
	return rc, nil
}

func (a *Archive) ExtractToWriter(dst io.Writer, name string) error {
	r, err := a.ExtractReader(name)
	if err != nil {
		return err
	}
	_, err = io.Copy(dst, r)
	err2 := r.Close()
	if err != nil {
		return err
	}
	return err2
}

func (a *Archive) ExtractToFile(dstPath string, name string) error {
	f, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	return a.ExtractToWriter(f, name)
}
<commit_msg>add comments<commit_after>package lzmadec

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeLayout = "2006-01-02 15:04:05"
)

var (
	// Err7zNotAvailable is returned if 7z executable is not available
	Err7zNotAvailable = errors.New("7z executable not available")
	//ErrNoEntries is returned if the archive has no files
	ErrNoEntries       = errors.New("no entries in 7z file")
	errUnexpectedLines = errors.New("unexpected number of lines")
	detectionStateOf7z int // 0 - not checked, 1 - checked and present, 2 - checked and not present
	mu                 sync.Mutex
)

// Archive describes a single .7z archive
type Archive struct {
	Path    string
	Entries []Entry
}

// Entry describes a single file inside .7z archive
type Entry struct {
	Path string
	Size int
	// -1 means "size unknown"
	PackedSize int
	Modified   time.Time
	Attributes string
	CRC        string
	Encrypted  string
	Method     string
	Block      int
}

func detect7zCached() error {
	mu.Lock()
	defer mu.Unlock()
	if detectionStateOf7z == 0 {
		cmd := exec.Command("7z")
		_, err := cmd.CombinedOutput()
		if err != nil {
			detectionStateOf7z = 2
		} else {
			detectionStateOf7z = 1
		}
	}
	if detectionStateOf7z == 1 {
		// checked and present
		return nil
	}
	// checked and not present
	return Err7zNotAvailable
}

/*
----------
Path = Badges.xml
Size = 4065633
Packed Size = 18990516
Modified = 2015-03-09 14:30:49
Attributes = ....A
CRC = 2C468F32
Encrypted = -
Method = BZip2
Block = 0
*/
func advanceToFirstEntry(scanner *bufio.Scanner) error {
	for scanner.Scan() {
		s := scanner.Text()
		if s == "----------" {
			return nil
		}
	}
	err := scanner.Err()
	if err == nil {
		err = ErrNoEntries
	}
	return err
}

func getEntryLines(scanner *bufio.Scanner) ([]string, error) {
	var res []string
	for scanner.Scan() {
		s := scanner.Text()
		s = strings.TrimSpace(s)
		if s == "" {
			break
		}
		res = append(res, s)
	}
	err := scanner.Err()
	if err != nil {
		return nil, err
	}
	if len(res) == 9 || len(res) == 0 {
		return res, nil
	}
	return nil, errUnexpectedLines
}

func parseEntryLines(lines []string) (Entry, error) {
	var e Entry
	var err error
	for _, s := range lines {
		parts := strings.SplitN(s, " =", 2)
		if len(parts) != 2 {
			return e, fmt.Errorf("unexpected line: '%s'", s)
		}
		name := strings.ToLower(parts[0])
		v := strings.TrimSpace(parts[1])
		switch name {
		case "path":
			e.Path = v
		case "size":
			e.Size, err = strconv.Atoi(v)
		case "packed size":
			e.PackedSize = -1
			if v != "" {
				e.PackedSize, err = strconv.Atoi(v)
			}
		case "modified":
			e.Modified, err = time.Parse(timeLayout, v)
		case "attributes":
			e.Attributes = v
		case "crc":
			e.CRC = v
		case "encrypted":
			e.Encrypted = v
		case "method":
			e.Method = v
		case "block":
			e.Block, err = strconv.Atoi(v)
		default:
			err = fmt.Errorf("unexpected entry line '%s'", name)
		}
		if err != nil {
			return e, err
		}
	}
	return e, nil
}

func parse7zListOutput(d []byte) ([]Entry, error) {
	var res []Entry
	r := bytes.NewBuffer(d)
	scanner := bufio.NewScanner(r)
	err := advanceToFirstEntry(scanner)
	if err != nil {
		return nil, err
	}
	for {
		lines, err := getEntryLines(scanner)
		if err != nil {
			return nil, err
		}
		if len(lines) == 0 {
			// last entry
			break
		}
		e, err := parseEntryLines(lines)
		if err != nil {
			return nil, err
		}
		res = append(res, e)
	}
	return res, nil
}

// NewArchive uses 7z to extract a list of files in .7z archive
func NewArchive(path string) (*Archive, error) {
	err := detect7zCached()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("7z", "l", "-slt", path)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	entries, err := parse7zListOutput(out)
	if err != nil {
		return nil, err
	}
	return &Archive{
		Path:    path,
		Entries: entries,
	}, nil
}

type readCloser struct {
	rc  io.ReadCloser
	cmd *exec.Cmd
}

func (rc *readCloser) Read(p []byte) (int, error) {
	return rc.rc.Read(p)
}

func (rc *readCloser) Close() error {
	// if we want to finish before reading all the data, we need to close
	// it all the data has already been read, or else rc.cmd.Wait() will hang
	// if it's already closed then Close() will return 'invalid argument',
	// which we can ignore
	rc.rc.Close()
	return rc.cmd.Wait()
}

// GetFileReader returns a reader for reading a given file
func (a *Archive) GetFileReader(name string) (io.ReadCloser, error) {
	found := false
	for _, e := range a.Entries {
		if e.Path == name {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.New("File not in the archive")
	}
	cmd := exec.Command("7z", "x", "-so", a.Path, name)
	stdout, err := cmd.StdoutPipe()
	rc := &readCloser{
		rc:  stdout,
		cmd: cmd,
	}
	err = cmd.Start()
	if err != nil {
		stdout.Close()
		return nil, err
	}
	return rc, nil
}

// ExtractToWriter writes the content of a given file inside the archive to dst
func (a *Archive) ExtractToWriter(dst io.Writer, name string) error {
	r, err := a.GetFileReader(name)
	if err != nil {
		return err
	}
	_, err = io.Copy(dst, r)
	err2 := r.Close()
	if err != nil {
		return err
	}
	return err2
}

// ExtractToFile extracts a given file from the archive to a file on disk
func (a *Archive) ExtractToFile(dstPath string, name string) error {
	f, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	return a.ExtractToWriter(f, name)
}
<|endoftext|>


<commit_before>package mailgun

import (
	"fmt"
	"net/http"
)

const (
	apiBase          = "https://api.mailgun.net/v2"
	messagesEndpoint = "messages"
	basicAuthUser    = "key"
)

type Mailgun interface {
	Domain() string
	ApiKey() string
	SendMessage(m *MailgunMessage) error
}

type mailgunImpl struct {
	domain string
	apiKey string
}

func NewMailgun(domain, apiKey string) Mailgun {
	m := mailgunImpl{domain: domain, apiKey: apiKey}
	return &m
}

func (m *mailgunImpl) Domain() string {
	return m.domain
}

func (m *mailgunImpl) ApiKey() string {
	return m.apiKey
}

func (m *mailgunImpl) SendMessage(message *MailgunMessage) error {
	// TODO
	req, err := http.NewRequest("POST", generateApiUrl(m, messagesEndpoint), nil)
	if err != nil {
		return err
	}

	req.SetBasicAuth(basicAuthUser, m.ApiKey())

	return nil
}

func generateApiUrl(m Mailgun, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint)
}
<commit_msg>Sending mail<commit_after>package mailgun

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

const (
	apiBase          = "https://api.mailgun.net/v2"
	messagesEndpoint = "messages"
	basicAuthUser    = "key"
)

type Mailgun interface {
	Domain() string
	ApiKey() string
	SendMessage(m *MailgunMessage) error
}

type mailgunImpl struct {
	domain string
	apiKey string
}

func NewMailgun(domain, apiKey string) Mailgun {
	m := mailgunImpl{domain: domain, apiKey: apiKey}
	return &m
}

func (m *mailgunImpl) Domain() string {
	return m.domain
}

func (m *mailgunImpl) ApiKey() string {
	return m.apiKey
}

func (m *mailgunImpl) SendMessage(message *MailgunMessage) error {
	if !message.validateMessage() {
		return errors.New("Message not valid")
	}

	req, err := http.NewRequest("POST", generateApiUrl(m, messagesEndpoint), nil)
	if err != nil {
		return err
	}

	req.SetBasicAuth(basicAuthUser, m.ApiKey())

	data := url.Values{}
	data.Add("from", message.From.String())
	data.Add("subject", message.Subject)
	data.Add("text", message.Text)

	for _, to := range message.To {
		data.Add("to", to.String())
	}
	for _, cc := range message.Cc {
		data.Add("cc", cc.String())
	}
	for _, bcc := range message.Bcc {
		data.Add("bcc", bcc.String())
	}

	if message.Html != "" {
		data.Add("html", message.Html)
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return errors.New("Status is not 200")
	}

	return nil
}

func generateApiUrl(m Mailgun, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", apiBase, m.Domain(), endpoint)
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/golang/glog"
)

type Manager struct {
	hasLocalState bool
	procs         []*DockerProcess
}

var uidParser = regexp.MustCompile(`(^[a-f0-9]{64})`)

func (manager *Manager) getProcesses() ([]string, error) {
	glog.V(5).Info("Getting processes")
	cmd := exec.Command(config.DockerPath, "ps", "--no-trunc")
	out, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var result []string
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		uid := uidParser.FindString(line)
		if uid != "" {
			result = append(result, uid)
		}
	}

	return result, nil
}

func (manager *Manager) startPolling() {
	glog.Info("Starting polling...")
	manager.performPoll()
	for _ = range time.Tick(config.PollInterval) {
		manager.performPoll()
	}
}

func (manager *Manager) performPoll() {
	glog.V(5).Info("Performing polling action")
	uids, err := manager.getProcesses()
	if err != nil {
		// log the error
		glog.Error(err.Error())
	}

	// is this the first run (or a )
	if manager.hasLocalState {
		glog.V(5).Info("Gocker has local state saved")

		// now that we have the uids, find them and update them
		for _, uid := range uids {
			ps := manager.findProcessByUid(uid)
			// is it new?, add it
			if ps == nil {
				glog.V(5).Infof("Found a new process %s", uid)
				process := DockerProcess{uid: uid, lastObservedAt: time.Now()}

				// notify
				_, err := config.Notifier.notify("new", &process)
				if err != nil {
					glog.Errorf("Notification failed: %s", err.Error())
				}

				manager.procs = append(manager.procs, &process)
			} else {
				// we had it before. update it
				glog.V(5).Infof("Process %s is still alive", ps.uid)
				ps.lastObservedAt = time.Now()
			}
		}

	} else {

		glog.V(5).Info("Gocker does not have local state saved")

		// reset the manager state
		manager.procs = nil
		manager.hasLocalState = true

		// now that we have the uids, find them and update them
		for _, uid := range uids {
			process := DockerProcess{uid: uid, lastObservedAt: time.Now()}
			manager.procs = append(manager.procs, &process)
		}

		// notify
		_, err := config.Notifier.notifyAll(manager.procs)
		if err != nil {
			glog.Errorf("Notification failed: %s", err.Error())
		}

	}

}

func (manager *Manager) startScavenger() {
	glog.Info("Starting scavenger loop...")
	for _ = range time.Tick(config.ScavengeInterval) {
		glog.V(5).Info("Scavenging")
		for idx, ps := range manager.procs {
			if time.Since(ps.lastObservedAt) > config.ScavengeInterval {
				// we got a skipper here
				glog.Infof("Process %s missing", ps.uid)
				// take it out
				copy(manager.procs[idx:], manager.procs[idx+1:])
				manager.procs = manager.procs[:len(manager.procs)-1]

				// notify
				_, err := config.Notifier.notify("missing", ps)
				if err != nil {
					glog.Errorf("Notification failed: %s", err.Error())
				}

			}
		}
	}
}

func (manager *Manager) findProcessByUid(uid string) *DockerProcess {
	for _, ps := range manager.procs {
		if ps.uid == uid {
			return ps
		}
	}

	return nil
}

func (manager *Manager) startRefresher() {
	glog.Info("Starting refresher loop ...")
	for _ = range time.Tick(15 * time.Minute) {
		manager.procs = nil
		manager.hasLocalState = false
	}
}
<commit_msg>logging<commit_after>package main

import (
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/golang/glog"
)

type Manager struct {
	hasLocalState bool
	procs         []*DockerProcess
}

var uidParser = regexp.MustCompile(`(^[a-f0-9]{64})`)

func (manager *Manager) getProcesses() ([]string, error) {
	glog.V(5).Info("Getting processes")
	cmd := exec.Command(config.DockerPath, "ps", "--no-trunc")
	out, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var result []string
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		uid := uidParser.FindString(line)
		if uid != "" {
			result = append(result, uid)
		}
	}

	return result, nil
}

func (manager *Manager) startPolling() {
	glog.Info("Starting polling...")
	manager.performPoll()
	for _ = range time.Tick(config.PollInterval) {
		manager.performPoll()
	}
}

func (manager *Manager) performPoll() {
	glog.V(5).Info("Performing polling action")
	uids, err := manager.getProcesses()
	if err != nil {
		// log the error
		glog.Error(err.Error())
	}

	// is this the first run (or a )
	if manager.hasLocalState {
		glog.V(5).Info("Gocker has local state saved")

		// now that we have the uids, find them and update them
		for _, uid := range uids {
			ps := manager.findProcessByUid(uid)
			// is it new?, add it
			if ps == nil {
				glog.V(5).Infof("Found a new process %s", uid)
				process := DockerProcess{uid: uid, lastObservedAt: time.Now()}

				// notify
				_, err := config.Notifier.notify("new", &process)
				if err != nil {
					glog.Errorf("Notification failed: %s", err.Error())
				}

				manager.procs = append(manager.procs, &process)
			} else {
				// we had it before. update it
				glog.V(5).Infof("Process %s is still alive", ps.uid)
				ps.lastObservedAt = time.Now()
			}
		}

	} else {

		glog.V(5).Info("Gocker does not have local state saved")

		// reset the manager state
		manager.procs = nil
		manager.hasLocalState = true

		// now that we have the uids, find them and update them
		for _, uid := range uids {
			process := DockerProcess{uid: uid, lastObservedAt: time.Now()}
			manager.procs = append(manager.procs, &process)
		}

		// notify
		glog.V(5).Infof("Notifying about %d", len(manager.procs))
		_, err := config.Notifier.notifyAll(manager.procs)
		if err != nil {
			glog.Errorf("Notification failed: %s", err.Error())
		}

	}

}

func (manager *Manager) startScavenger() {
	glog.Info("Starting scavenger loop...")
	for _ = range time.Tick(config.ScavengeInterval) {
		glog.V(5).Info("Scavenging")
		for idx, ps := range manager.procs {
			if time.Since(ps.lastObservedAt) > config.ScavengeInterval {
				// we got a skipper here
				glog.Infof("Process %s missing", ps.uid)
				// take it out
				copy(manager.procs[idx:], manager.procs[idx+1:])
				manager.procs = manager.procs[:len(manager.procs)-1]

				// notify
				_, err := config.Notifier.notify("missing", ps)
				if err != nil {
					glog.Errorf("Notification failed: %s", err.Error())
				}

			}
		}
	}
}

func (manager *Manager) findProcessByUid(uid string) *DockerProcess {
	for _, ps := range manager.procs {
		if ps.uid == uid {
			return ps
		}
	}

	return nil
}

func (manager *Manager) startRefresher() {
	glog.Info("Starting refresher loop ...")
	for _ = range time.Tick(15 * time.Minute) {
		manager.procs = nil
		manager.hasLocalState = false
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
)

func (c *Context) Run() {
	c.startup()
	if c.RunOnce {
		// Renew certificate if it's about to expire
		if time.Now().UTC().After(c.getRenewalDate()) {
			c.renew()
		} else {
			logrus.Infof("Certificate %s expires on %s", c.CertificateName,
				c.ExpiryDate.UTC().Format(time.UnixDate))
		}
		return
	}

	for {
		<-c.timer()
		c.renew()
	}
}

func (c *Context) startup() {
	var storedLocally, storedInRancher bool
	ok, acmeCert := c.Acme.GetStoredCertificate(c.CertificateName, c.Domains)
	if ok {
		storedLocally = true
		c.ExpiryDate = acmeCert.ExpiryDate
		logrus.Infof("Found locally stored certificate '%s'", c.CertificateName)
	}

	rancherCert, err := c.Rancher.FindCertByName(c.CertificateName)
	if err != nil {
		logrus.Fatalf("Could not lookup certificate in Rancher API: %v", err)
	}

	if rancherCert != nil {
		storedInRancher = true
		c.RancherCertId = rancherCert.Id
		logrus.Infof("Found existing certificate '%s' in Rancher", c.CertificateName)
	}

	if storedLocally && storedInRancher {
		if rancherCert.SerialNumber == acmeCert.SerialNumber {
			logrus.Infof("Managing renewal of certificate '%s'", c.CertificateName)
			return
		}
		logrus.Infof("Serial number mismatch between Rancher and local certificate '%s'", c.CertificateName)
		c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	if storedLocally && !storedInRancher {
		logrus.Debugf("Adding certificate '%s' to Rancher", c.CertificateName)
		c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	if c.Acme.ProviderName() == "HTTP" {
		logrus.Info("Using HTTP challenge: Sleeping for 120 seconds before requesting certificate")
		logrus.Info("Make sure that HTTP requests for '/.well-known/acme-challenge' for all certificate " +
			"domains are forwarded to the container running this application")
		time.Sleep(120 * time.Second)
	}

	logrus.Infof("Trying to obtain SSL certificate (%s) from Let's Encrypt %s CA", strings.Join(c.Domains, ","), c.Acme.ApiVersion())

	acmeCert, failures := c.Acme.Issue(c.CertificateName, c.Domains)
	if len(failures) > 0 {
		for k, v := range failures {
			logrus.Errorf("[%s] Error obtaining certificate: %s", k, v.Error())
		}
		os.Exit(1)
	}

	logrus.Infof("Certificate obtained successfully")

	c.ExpiryDate = acmeCert.ExpiryDate

	if storedInRancher {
		logrus.Debugf("Overwriting Rancher certificate '%s'", c.CertificateName)
		c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}

func (c *Context) addRancherCert(privateKey, cert []byte) {
	rancherCert, err := c.Rancher.AddCertificate(c.CertificateName, CERT_DESCRIPTION, privateKey, cert)
	if err != nil {
		logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.CertificateName, err)
	}
	c.RancherCertId = rancherCert.Id
	logrus.Infof("Certificate '%s' added to Rancher", c.CertificateName)
}

func (c *Context) updateRancherCert(privateKey, cert []byte) {
	err := c.Rancher.UpdateCertificate(c.RancherCertId, CERT_DESCRIPTION, privateKey, cert)
	if err != nil {
		logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.CertificateName, err)
	}
	logrus.Infof("Updated Rancher certificate '%s'", c.CertificateName)

	err = c.Rancher.UpdateLoadBalancers(c.RancherCertId)
	if err != nil {
		logrus.Fatalf("Failed to upgrade load balancers: %v", err)
	}
}

func (c *Context) renew() {
	logrus.Infof("Trying to obtain renewed SSL certificate (%s) from Let's Encrypt %s CA", strings.Join(c.Domains, ","), c.Acme.ApiVersion())

	acmeCert, err := c.Acme.Renew(c.CertificateName)
	if err != nil {
		logrus.Fatalf("Failed to renew certificate: %v", err)
	}

	logrus.Infof("Certificate renewed successfully")

	c.ExpiryDate = acmeCert.ExpiryDate
	c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}

func (c *Context) timer() <-chan time.Time {
	now := time.Now().UTC()
	next := c.getRenewalDate()
	left := next.Sub(now)
	if left <= 0 {
		left = 10 * time.Second
	}

	logrus.Infof("Certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))

	// test mode forces renewal
	if c.TestMode {
		logrus.Debug("Test mode: Forced certificate renewal in 120 seconds")
		left = 120 * time.Second
	}

	return time.After(left)
}

func (c *Context) getRenewalDate() time.Time {
	if c.ExpiryDate.IsZero() {
		logrus.Fatalf("Could not determine expiry date for certificate: %s", c.CertificateName)
	}
	date := c.ExpiryDate.AddDate(0, 0, -c.RenewalPeriodDays)
	dYear, dMonth, dDay := date.Date()
	return time.Date(dYear, dMonth, dDay, c.RenewalDayTime, 0, 0, 0, time.UTC)
}
<commit_msg>Log nits<commit_after>package main

import (
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
)

func (c *Context) Run() {
	c.startup()
	if c.RunOnce {
		// Renew certificate if it's about to expire
		if time.Now().UTC().After(c.getRenewalDate()) {
			c.renew()
		} else {
			logrus.Infof("Not renewing certificate %s which expires on %s", c.CertificateName,
				c.ExpiryDate.UTC().Format(time.UnixDate))
		}
		logrus.Info("Run once: Finished")
		return
	}

	for {
		<-c.timer()
		c.renew()
	}
}

func (c *Context) startup() {
	var storedLocally, storedInRancher bool
	ok, acmeCert := c.Acme.GetStoredCertificate(c.CertificateName, c.Domains)
	if ok {
		storedLocally = true
		c.ExpiryDate = acmeCert.ExpiryDate
		logrus.Infof("Found locally stored certificate '%s'", c.CertificateName)
	}

	rancherCert, err := c.Rancher.FindCertByName(c.CertificateName)
	if err != nil {
		logrus.Fatalf("Could not lookup certificate in Rancher API: %v", err)
	}

	if rancherCert != nil {
		storedInRancher = true
		c.RancherCertId = rancherCert.Id
		logrus.Infof("Found existing certificate '%s' in Rancher", c.CertificateName)
	}

	if storedLocally && storedInRancher {
		if rancherCert.SerialNumber == acmeCert.SerialNumber {
			logrus.Infof("Managing renewal of certificate '%s'", c.CertificateName)
			return
		}
		logrus.Infof("Serial number mismatch between Rancher and local certificate '%s'", c.CertificateName)
		c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	if storedLocally && !storedInRancher {
		logrus.Debugf("Adding certificate '%s' to Rancher", c.CertificateName)
		c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	if c.Acme.ProviderName() == "HTTP" {
		logrus.Info("Using HTTP challenge: Sleeping for 120 seconds before requesting certificate")
		logrus.Info("Make sure that HTTP requests for '/.well-known/acme-challenge' for all certificate " +
			"domains are forwarded to port 80 of the container running this application")
		time.Sleep(120 * time.Second)
	}

	logrus.Infof("Trying to obtain SSL certificate (%s) from Let's Encrypt %s CA", strings.Join(c.Domains, ","), c.Acme.ApiVersion())

	acmeCert, failures := c.Acme.Issue(c.CertificateName, c.Domains)
	if len(failures) > 0 {
		for k, v := range failures {
			logrus.Errorf("[%s] Error obtaining certificate: %s", k, v.Error())
		}
		os.Exit(1)
	}

	logrus.Infof("Certificate obtained successfully")

	c.ExpiryDate = acmeCert.ExpiryDate

	if storedInRancher {
		logrus.Debugf("Overwriting Rancher certificate '%s'", c.CertificateName)
		c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
		return
	}

	c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}

func (c *Context) addRancherCert(privateKey, cert []byte) {
	rancherCert, err := c.Rancher.AddCertificate(c.CertificateName, CERT_DESCRIPTION, privateKey, cert)
	if err != nil {
		logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.CertificateName, err)
	}
	c.RancherCertId = rancherCert.Id
	logrus.Infof("Certificate '%s' added to Rancher", c.CertificateName)
}

func (c *Context) updateRancherCert(privateKey, cert []byte) {
	err := c.Rancher.UpdateCertificate(c.RancherCertId, CERT_DESCRIPTION, privateKey, cert)
	if err != nil {
		logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.CertificateName, err)
	}
	logrus.Infof("Updated Rancher certificate '%s'", c.CertificateName)

	err = c.Rancher.UpdateLoadBalancers(c.RancherCertId)
	if err != nil {
		logrus.Fatalf("Failed to upgrade load balancers: %v", err)
	}
}

func (c *Context) renew() {
	logrus.Infof("Trying to obtain renewed SSL certificate (%s) from Let's Encrypt %s CA", strings.Join(c.Domains, ","), c.Acme.ApiVersion())

	acmeCert, err := c.Acme.Renew(c.CertificateName)
	if err != nil {
		logrus.Fatalf("Failed to renew certificate: %v", err)
	}

	logrus.Infof("Certificate renewed successfully")

	c.ExpiryDate = acmeCert.ExpiryDate
	c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}

func (c *Context) timer() <-chan time.Time {
	now := time.Now().UTC()
	next := c.getRenewalDate()
	left := next.Sub(now)
	if left <= 0 {
		left = 10 * time.Second
	}

	logrus.Infof("Certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))

	// test mode forces renewal
	if c.TestMode {
		logrus.Debug("Test mode: Forced certificate renewal in 120 seconds")
		left = 120 * time.Second
	}

	return time.After(left)
}

func (c *Context) getRenewalDate() time.Time {
	if c.ExpiryDate.IsZero() {
		logrus.Fatalf("Could not determine expiry date for certificate: %s", c.CertificateName)
	}
	date := c.ExpiryDate.AddDate(0, 0, -c.RenewalPeriodDays)
	dYear, dMonth, dDay := date.Date()
	return time.Date(dYear, dMonth, dDay, c.RenewalDayTime, 0, 0, 0, time.UTC)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/golang/glog"
)

var (
	USER_AGENT string = "gocker/" + VERSION + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
)

type Notifier struct {
	endpoint string

	client *http.Client
}

type Payload struct {
	CallbackId      string    `json:"callback_id"`
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

type PayloadFull struct {
	CallbackId string      `json:"callback_id"`
	Containers []Container `json:"containers"`
}

type Container struct {
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

func (n *Notifier) notify(status string, process *DockerProcess) (string, error) {
	glog.V(5).Infof("Notifying server about %s", process.uid)
	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	runtimeInspect, err := process.Inspect()
	if err != nil {
		return "", err
	}

	payload := Payload{
		CallbackId:      config.CallbackId,
		ContainerId:     process.uid,
		LastObservation: process.lastObservedAt,
		Status:          status,
		Runtime:         runtimeInspect,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) notifyAll(processes []*DockerProcess) (string, error) {
	if processes == nil {
		glog.V(5).Infof("Notifying server full - no processes running")
	} else {
		glog.V(5).Infof("Notifying server full - %d processes running", len(processes))
	}

	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	containers := make([]Container, 0)
	for _, process := range processes {
		runtimeInspect, err := process.Inspect()
		if err != nil {
			glog.V(5).Infof("<<unable to get runtime information>>")
			runtimeInspect = "{\"error\":\"unable to get runtime information\"}"
		}
		container := Container{
			ContainerId:     process.uid,
			LastObservation: process.lastObservedAt,
			Status:          "new",
			Runtime:         runtimeInspect,
		}
		containers = append(containers, container)
		glog.V(5).Infof("%d containers created", len(containers))
	}

	payload := PayloadFull{
		CallbackId: config.CallbackId,
		Containers: containers,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) PerformPost(payload interface{}) (string, error) {
	var rbody io.Reader

	j, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}
	rbody = bytes.NewReader(j)

	req, err := http.NewRequest("POST", n.endpoint, rbody)
	if err != nil {
		return "", err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Request-Id", uuid.New())
	req.Header.Set("User-Agent", USER_AGENT)
	req.Header.Set("Content-Type", "application/json")

	if debugMode {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			glog.Error(err)
		} else {
			os.Stderr.Write(dump)
			os.Stderr.Write([]byte{'\n', '\n'})
		}
	}

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

	if debugMode {
		dump, err := httputil.DumpResponse(res, true)
		if err != nil {
			glog.Error(err)
		} else {
			os.Stderr.Write(dump)
			os.Stderr.Write([]byte{'\n'})
		}
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	return string(body), nil
}
<commit_msg>inspect returns array<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/golang/glog"
)

var (
	USER_AGENT string = "gocker/" + VERSION + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
)

type Notifier struct {
	endpoint string

	client *http.Client
}

type Payload struct {
	CallbackId      string    `json:"callback_id"`
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

type PayloadFull struct {
	CallbackId string      `json:"callback_id"`
	Containers []Container `json:"containers"`
}

type Container struct {
	ContainerId     string    `json:"container_id"`
	LastObservation time.Time `json:"last_observation_at"`
	Status          string    `json:"status"`
	Runtime         string    `json:"runtime"`
}

func (n *Notifier) notify(status string, process *DockerProcess) (string, error) {
	glog.V(5).Infof("Notifying server about %s", process.uid)
	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	runtimeInspect, err := process.Inspect()
	if err != nil {
		return "", err
	}

	payload := Payload{
		CallbackId:      config.CallbackId,
		ContainerId:     process.uid,
		LastObservation: process.lastObservedAt,
		Status:          status,
		Runtime:         runtimeInspect,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) notifyAll(processes []*DockerProcess) (string, error) {
	if processes == nil {
		glog.V(5).Infof("Notifying server full - no processes running")
	} else {
		glog.V(5).Infof("Notifying server full - %d processes running", len(processes))
	}

	httpClient := n.client
	if httpClient == nil {
		n.client = http.DefaultClient
	}

	containers := make([]Container, 0)
	for _, process := range processes {
		runtimeInspect, err := process.Inspect()
		if err != nil {
			glog.V(5).Infof("<<unable to get runtime information>>")
			runtimeInspect = "[{\"error\":\"unable to get runtime information\"}]"
		}
		container := Container{
			ContainerId:     process.uid,
			LastObservation: process.lastObservedAt,
			Status:          "new",
			Runtime:         runtimeInspect,
		}
		containers = append(containers, container)
		glog.V(5).Infof("%d containers created", len(containers))
	}

	payload := PayloadFull{
		CallbackId: config.CallbackId,
		Containers: containers,
	}

	return n.PerformPost(payload)
}

func (n *Notifier) PerformPost(payload interface{}) (string, error) {
	var rbody io.Reader

	j, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}
	rbody = bytes.NewReader(j)

	req, err := http.NewRequest("POST", n.endpoint, rbody)
	if err != nil {
		return "", err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Request-Id", uuid.New())
	req.Header.Set("User-Agent", USER_AGENT)
	req.Header.Set("Content-Type", "application/json")

	if debugMode {
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			glog.Error(err)
		} else {
			os.Stderr.Write(dump)
			os.Stderr.Write([]byte{'\n', '\n'})
		}
	}

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

	if debugMode {
		dump, err := httputil.DumpResponse(res, true)
		if err != nil {
			glog.Error(err)
		} else {
			os.Stderr.Write(dump)
			os.Stderr.Write([]byte{'\n'})
		}
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	return string(body), nil
}
<|endoftext|>


<commit_before><commit_msg>Add test for issue #5<commit_after><|endoftext|>


<commit_before><commit_msg>fixing spelling mistake<commit_after><|endoftext|>


<commit_before>package main

import (
	"strings"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// prometheusMetric

type prometheusMetric struct {
	NameVarnish string
	Name        string
	Value       float64
	Description string
	Group       string

	gaugeVec *prometheus.GaugeVec
	gauge    prometheus.Gauge
	labels   prometheus.Labels
}

func NewPrometheusMetric(m *varnishMetric) *prometheusMetric {
	pm := &prometheusMetric{
		NameVarnish: m.Name,
		Name:        fullPrometheusMetricName(m),
		Value:       m.Value,
		Description: m.Description,
		Group:       prometheusGroup(m),
	}
	pm.labels = prometheusLabels(pm, m)
	return pm
}

// Set value.
func (p *prometheusMetric) Set(value float64) {
	p.Value = value
	p.Gauge().Set(p.Value)
}

// Reset underlying gauge.
func (p *prometheusMetric) Reset() {
	if p.gaugeVec != nil {
		p.gaugeVec.Reset()
	} else {
		// @todo Should this be done to singleton gauges?
		// This zero value will stay here if scrape error occurs.
		// Should we emit back the last known value or zero?
		// Why do vec gauges get reseted, looks like they lose last known value there?
		// So this would be consistent with that behavior.
		p.gauge.Set(0)
	}
}

// Get underlying gauge.
func (p *prometheusMetric) Gauge() prometheus.Gauge {
	if p.gaugeVec != nil {
		return p.gaugeVec.With(p.labels)
	} else {
		return p.gauge
	}
}

// Get labels as a string.
func (p *prometheusMetric) Labels() string {
	if len(p.labels) > 0 {
		parts := []string{}
		for k, v := range p.labels {
			parts = append(parts, k+":"+v)
		}
		return strings.Join(parts, ", ")
	}
	return ""
}

// Returns label keys or nil if no labels have been attached.
// @note Due to golang map range the returned list may be in different order
// on each invocation. Don't assume order when using the list.
func (p *prometheusMetric) LabelNames() []string {
	var names []string
	for name := range p.labels {
		names = append(names, name)
	}
	if len(names) == 0 {
		return nil
	}
	return names
}

// prometheusExporter

type prometheusExporter struct {
	sync.RWMutex

	namespace string
	metrics   []*prometheusMetric

	up                          prometheus.Gauge
	totalScrapes, failedScrapes prometheus.Counter
}

func NewPrometheusExporter() *prometheusExporter {
	namespace := "varnish"
	return &prometheusExporter{
		namespace: namespace,
		metrics:   make([]*prometheusMetric, 0),
		up: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "up",
			Help:      "Was the last scrape of varnish successful.",
		}),
		totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_total_scrapes",
			Help:      "Current total varnish scrapes.",
		}),
		failedScrapes: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_total_failed_scrapes",
			Help:      "Current total of varnish scrape errors",
		}),
	}
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Describe(ch chan<- *prometheus.Desc) {
	if StartParams.Verbose {
		defer func(start time.Time) {
			logInfo("prometheus.Collector.Describe  %s", time.Now().Sub(start))
		}(time.Now())
	}

	for _, m := range pe.metrics {
		m.Gauge().Describe(ch)
	}
	ch <- pe.up.Desc()
	ch <- pe.totalScrapes.Desc()
	ch <- pe.failedScrapes.Desc()
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {
	if StartParams.Verbose {
		defer func(start time.Time) {
			logInfo("prometheus.Collector.Collect   %s", time.Now().Sub(start))
		}(time.Now())
	}

	// scrape: this is a blocking operation and is safe for concurrent use.
	err := VarnishExporter.Update()

	pe.up.Set(1)
	pe.totalScrapes.Inc()

	if err != nil {
		pe.up.Set(0)
		pe.failedScrapes.Inc()
	}

	// reset
	for _, pMetric := range pe.metrics {
		pMetric.Reset()
	}

	// lock below state for value updated, anything above is guarded.
	pe.Lock()
	defer pe.Unlock()

	// update values, if no errors on scrape
	if err == nil {
		for _, pMetric := range pe.metrics {
			if vMetric := VarnishExporter.metricsByName[pMetric.NameVarnish]; vMetric != nil {
				pMetric.Set(vMetric.Value)
			}
		}
	}
	// collect
	for _, pMetric := range pe.metrics {
		pMetric.Gauge().Collect(ch)
	}
	ch <- pe.up
	ch <- pe.totalScrapes
	ch <- pe.failedScrapes
}

func (pe *prometheusExporter) exposeMetrics(metrics []*varnishMetric) error {
	pe.Lock()
	defer pe.Unlock()

	pe.metrics = make([]*prometheusMetric, 0)

	for _, m := range metrics {
		pm := NewPrometheusMetric(m)
		opts := prometheus.GaugeOpts{
			Namespace: pe.namespace,
			Name:      pm.Name,
			Help:      m.Description,
			// @todo Put varnish version number here or should it be its own metric?
			//ConstLabels: prometheus.Labels{"type": pm.Group},
		}

		if labelNames := pm.LabelNames(); len(labelNames) > 0 {
			pm.gaugeVec = prometheus.NewGaugeVec(opts, labelNames)
		} else {
			pm.gauge = prometheus.NewGauge(opts)
		}
		pe.metrics = append(pe.metrics, pm)
	}
	return nil
}

// https://prometheus.io/docs/practices/naming/
func fullPrometheusMetricName(metric *varnishMetric) string {
	clean := strings.ToLower(metric.Name)
	// Remove unique identifiers from name to group similar metrics into a single GaugeVec
	if len(metric.Identifier) > 0 {
		clean = strings.Replace(clean, "."+strings.ToLower(metric.Identifier), "", -1)
	}
	// Make sure our group name is prefixed only once
	return prometheusGroup(metric) + "_" + strings.Replace(prometheusGroupTrim(clean), ".", "_", -1)
}

type group struct {
	name     string
	prefixes []string
}

var (
	groups = []group{
		// @note varnish 3.x does not seem to mark 'MAIN.' prefixes
		group{name: "backend", prefixes: []string{
			"VBE.",
			// varnish 4.x
			"MAIN.backend_",
			"MAIN.s_fetch",
			// varnish 3.x
			"backend_",
			"MAIN.s_fetch",
		}},
		group{name: "mempool", prefixes: []string{
			"MEMPOOL.",
		}},
		group{name: "lck", prefixes: []string{
			"LCK.",
		}},
		group{name: "sma", prefixes: []string{
			"SMA.",
		}},
		group{name: "mgt", prefixes: []string{
			"MGT.",
		}},
		// must be last so above groups have a opportunity to override
		group{name: "main", prefixes: []string{
			"MAIN.",
		}},
	}
)

func prometheusGroupTrim(name string) string {
	for _, group := range groups {
		for _, prefix := range group.prefixes {
			if startsWith(name, prefix, caseInsensitive) {
				return name[len(prefix):]
			}
		}
	}
	return name
}

// Always returns at least one main label
func prometheusGroup(metric *varnishMetric) string {
	for _, group := range groups {
		if startsWithAny(metric.Name, group.prefixes, caseInsensitive) {
			return group.name
		}
	}
	return "main"
}

// @note may modify input ptrs if finds a GaugeVec grouping pattern
func prometheusLabels(pMetric *prometheusMetric, metric *varnishMetric) prometheus.Labels {
	labels := make(prometheus.Labels)
	if len(metric.Identifier) > 0 {
		labels["ident"] = metric.Identifier
	}
	if startsWith(pMetric.Name, "main_fetch_", caseSensitive) {
		// If name is manipulated to be the same for multiple metrics
		// the description needs to match as well.
		labels["code"] = pMetric.Name[len("main_fetch_"):]
		pMetric.Name = "main_fetch"
		metric.Description = "Number of backend fetches"
	}
	if len(labels) == 0 {
		return nil
	}
	return labels
}
<commit_msg>Doc and typo fixes.<commit_after>package main

import (
	"strings"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// prometheusMetric

type prometheusMetric struct {
	NameVarnish string
	Name        string
	Value       float64
	Description string
	Group       string

	gaugeVec *prometheus.GaugeVec
	gauge    prometheus.Gauge
	labels   prometheus.Labels
}

func NewPrometheusMetric(m *varnishMetric) *prometheusMetric {
	pm := &prometheusMetric{
		NameVarnish: m.Name,
		Name:        fullPrometheusMetricName(m),
		Value:       m.Value,
		Description: m.Description,
		Group:       prometheusGroup(m),
	}
	pm.labels = prometheusLabels(pm, m)
	return pm
}

// Set value.
func (p *prometheusMetric) Set(value float64) {
	p.Value = value
	p.Gauge().Set(p.Value)
}

// Reset underlying gauge.
func (p *prometheusMetric) Reset() {
	if p.gaugeVec != nil {
		p.gaugeVec.Reset()
	} else {
		// @todo Should this be done to singleton gauges?
		// This zero value will stay here if scrape error occurs.
		// Should we emit back the last known value or zero?
		// Why do vec gauges get reseted, looks like they lose last known value there?
		// So this would be consistent with that behavior.
		p.gauge.Set(0)
	}
}

// Get underlying gauge.
func (p *prometheusMetric) Gauge() prometheus.Gauge {
	if p.gaugeVec != nil {
		return p.gaugeVec.With(p.labels)
	} else {
		return p.gauge
	}
}

// Get labels as a string.
func (p *prometheusMetric) Labels() string {
	if len(p.labels) > 0 {
		parts := []string{}
		for k, v := range p.labels {
			parts = append(parts, k+":"+v)
		}
		return strings.Join(parts, ", ")
	}
	return ""
}

// Returns label keys or nil if no labels have been attached.
// @note Due to golang map range the returned list may be in different order
// on each invocation. Don't assume order when using the list.
func (p *prometheusMetric) LabelNames() []string {
	var names []string
	for name := range p.labels {
		names = append(names, name)
	}
	if len(names) == 0 {
		return nil
	}
	return names
}

// prometheusExporter

type prometheusExporter struct {
	sync.RWMutex

	namespace string
	metrics   []*prometheusMetric

	up                          prometheus.Gauge
	totalScrapes, failedScrapes prometheus.Counter
}

func NewPrometheusExporter() *prometheusExporter {
	namespace := "varnish"
	return &prometheusExporter{
		namespace: namespace,
		metrics:   make([]*prometheusMetric, 0),
		up: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: namespace,
			Name:      "up",
			Help:      "Was the last scrape of varnish successful.",
		}),
		totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_total_scrapes",
			Help:      "Current total varnish scrapes.",
		}),
		failedScrapes: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "exporter_total_failed_scrapes",
			Help:      "Current total of varnish scrape errors",
		}),
	}
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Describe(ch chan<- *prometheus.Desc) {
	if StartParams.Verbose {
		defer func(start time.Time) {
			logInfo("prometheus.Collector.Describe  %s", time.Now().Sub(start))
		}(time.Now())
	}

	for _, m := range pe.metrics {
		m.Gauge().Describe(ch)
	}
	ch <- pe.up.Desc()
	ch <- pe.totalScrapes.Desc()
	ch <- pe.failedScrapes.Desc()
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {
	if StartParams.Verbose {
		defer func(start time.Time) {
			logInfo("prometheus.Collector.Collect   %s", time.Now().Sub(start))
		}(time.Now())
	}

	// scrape: this is a blocking operation and is safe for concurrent use (mutex inside).
	err := VarnishExporter.Update()

	pe.up.Set(1)
	pe.totalScrapes.Inc()

	if err != nil {
		pe.up.Set(0)
		pe.failedScrapes.Inc()
	}

	// reset
	for _, pMetric := range pe.metrics {
		pMetric.Reset()
	}

	// lock below state for value updates, anything above is guarded.
	pe.Lock()
	defer pe.Unlock()

	// update values, if no errors on scrape
	if err == nil {
		for _, pMetric := range pe.metrics {
			if vMetric := VarnishExporter.metricsByName[pMetric.NameVarnish]; vMetric != nil {
				pMetric.Set(vMetric.Value)
			}
		}
	}
	// collect
	for _, pMetric := range pe.metrics {
		pMetric.Gauge().Collect(ch)
	}
	ch <- pe.up
	ch <- pe.totalScrapes
	ch <- pe.failedScrapes
}

func (pe *prometheusExporter) exposeMetrics(metrics []*varnishMetric) error {
	pe.Lock()
	defer pe.Unlock()

	pe.metrics = make([]*prometheusMetric, 0)

	for _, m := range metrics {
		pm := NewPrometheusMetric(m)
		opts := prometheus.GaugeOpts{
			Namespace: pe.namespace,
			Name:      pm.Name,
			Help:      m.Description,
			// @todo Put varnish version number here or should it be its own metric?
			//ConstLabels: prometheus.Labels{"type": pm.Group},
		}

		if labelNames := pm.LabelNames(); len(labelNames) > 0 {
			pm.gaugeVec = prometheus.NewGaugeVec(opts, labelNames)
		} else {
			pm.gauge = prometheus.NewGauge(opts)
		}
		pe.metrics = append(pe.metrics, pm)
	}
	return nil
}

// https://prometheus.io/docs/practices/naming/
func fullPrometheusMetricName(metric *varnishMetric) string {
	clean := strings.ToLower(metric.Name)
	// Remove unique identifiers from name to group similar metrics into a single GaugeVec
	if len(metric.Identifier) > 0 {
		clean = strings.Replace(clean, "."+strings.ToLower(metric.Identifier), "", -1)
	}
	// Make sure our group name is prefixed only once
	return prometheusGroup(metric) + "_" + strings.Replace(prometheusGroupTrim(clean), ".", "_", -1)
}

type group struct {
	name     string
	prefixes []string
}

var (
	groups = []group{
		// @note varnish 3.x does not seem to mark 'MAIN.' prefixes
		group{name: "backend", prefixes: []string{
			"VBE.",
			// varnish 4.x
			"MAIN.backend_",
			"MAIN.s_fetch",
			// varnish 3.x
			"backend_",
			"MAIN.s_fetch",
		}},
		group{name: "mempool", prefixes: []string{
			"MEMPOOL.",
		}},
		group{name: "lck", prefixes: []string{
			"LCK.",
		}},
		group{name: "sma", prefixes: []string{
			"SMA.",
		}},
		group{name: "mgt", prefixes: []string{
			"MGT.",
		}},
		// must be last so above groups have a opportunity to override
		group{name: "main", prefixes: []string{
			"MAIN.",
		}},
	}
)

func prometheusGroupTrim(name string) string {
	for _, group := range groups {
		for _, prefix := range group.prefixes {
			if startsWith(name, prefix, caseInsensitive) {
				return name[len(prefix):]
			}
		}
	}
	return name
}

// Always returns at least one main label
func prometheusGroup(metric *varnishMetric) string {
	for _, group := range groups {
		if startsWithAny(metric.Name, group.prefixes, caseInsensitive) {
			return group.name
		}
	}
	return "main"
}

// @note may modify input ptrs if finds a GaugeVec grouping pattern
func prometheusLabels(pMetric *prometheusMetric, metric *varnishMetric) prometheus.Labels {
	labels := make(prometheus.Labels)
	if len(metric.Identifier) > 0 {
		labels["ident"] = metric.Identifier
	}
	if startsWith(pMetric.Name, "main_fetch_", caseSensitive) {
		// If name is manipulated to be the same for multiple metrics
		// the description needs to match as well.
		labels["code"] = pMetric.Name[len("main_fetch_"):]
		pMetric.Name = "main_fetch"
		metric.Description = "Number of backend fetches"
	}
	if len(labels) == 0 {
		return nil
	}
	return labels
}
<|endoftext|>


<commit_before><commit_msg>Adding logging support<commit_after><|endoftext|>


<commit_before><commit_msg>feature: Add verbosity on ergo's output<commit_after><|endoftext|>


<commit_before>/*
Package export provides an interface to instrument Go code.

This package is very similar to the "expvar" package in the vanilla Go
distribution. In fact, the variables exported with this package are also
registered with the "expvar" package, so that you can also use other existing
metric collection frameworks with it. This package differs in that it has an
explicitly cumulative type, Derive.

The intended usage pattern of this package is as follows: First, global
variables are initialized with NewDerive() and NewGauge(). The Run() function
is called as a separate goroutine as part of your program's initialization and,
last but not least, the variables are updated with their respective update
functions, Add() for Derive and Set() for Gauge.

  // Initialize global variable.
  var requestCounter = export.NewDerive(api.Identifier{
          Host:   "example.com",
          Plugin: "golang",
          Type:   "total_requests",
  })

  // Call Run() in its own goroutine.
  func main() {
          client, err := network.Dial(network.DefaultIPv6Address, network.ClientOptions{})
          if err !=  nil {
	          log.Fatal(err)
	  }
          go export.Run(client, export.Options{
                  Interval: 10 * time.Second,
	  })
          // …
  }

  // Update variable.
  func requestHandler(w http.ResponseWriter, req *http.Request) {
          defer requestCounter.Add(1)
          // …
  }
*/
package export

import (
	"expvar"
	"log"
	"math"
	"strconv"
	"sync"
	"time"

	"collectd.org/api"
)

var (
	mutex sync.RWMutex
	vars  []Var
)

// Var is an abstract type for metrics exported by this package.
type Var interface {
	ValueList() api.ValueList
}

// Publish adds v to the internal list of exported metrics.
func Publish(v Var) {
	mutex.Lock()
	defer mutex.Unlock()

	vars = append(vars, v)
}

// Options holds options for the Run() function.
type Options struct {
	Interval time.Duration
}

// Run periodically calls the ValueList function of each Var, sets the Time and
// Interval fields and passes it w.Write(). This function blocks indefinitely.
func Run(w api.Writer, opts Options) error {
	ticker := time.NewTicker(opts.Interval)

	for {
		select {
		case _ = <-ticker.C:
			mutex.RLock()
			for _, v := range vars {
				vl := v.ValueList()
				vl.Time = time.Now()
				vl.Interval = opts.Interval
				if err := w.Write(vl); err != nil {
					mutex.RUnlock()
					return err
				}
			}
			mutex.RUnlock()
		}
	}
}

// Derive represents a cumulative integer data type, for example "requests
// served since server start". It implements the Var and expvar.Var interfaces.
type Derive struct {
	mu    sync.RWMutex
	id    api.Identifier
	value api.Derive
}

// NewDerive initializes a new Derive, registers it with the "expvar" package
// and returns it. The initial value is zero.
func NewDerive(id api.Identifier) *Derive {
	d := &Derive{
		id:    id,
		value: 0,
	}

	Publish(d)
	expvar.Publish(id.String(), d)
	return d
}

// NewDeriveString parses s as an Identifier and returns a new Derive. If
// parsing s fails, it will panic. This simplifies initializing global
// variables.
func NewDeriveString(s string) *Derive {
	id, err := api.ParseIdentifier(s)
	if err != nil {
		log.Fatal(err)
	}

	return NewDerive(id)
}

// Add adds diff to d.
func (d *Derive) Add(diff int) {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.value += api.Derive(diff)
}

// String returns the string representation of d.
func (d *Derive) String() string {
	d.mu.RLock()
	defer d.mu.RUnlock()
	return strconv.FormatInt(int64(d.value), 10)
}

// ValueList returns the ValueList representation of d. Both, Time and Interval
// are set to zero.
func (d *Derive) ValueList() api.ValueList {
	d.mu.RLock()
	defer d.mu.RUnlock()
	return api.ValueList{
		Identifier: d.id,
		Values:     []api.Value{d.value},
	}
}

// Gauge represents an absolute floating point data type, for example "heap
// memory used". It implements the Var and expvar.Var interfaces.
type Gauge struct {
	mu    sync.RWMutex
	id    api.Identifier
	value api.Gauge
}

// NewGauge initializes a new Gauge, registers it with the "expvar" package and
// returns it. The initial value is NaN.
func NewGauge(id api.Identifier) *Gauge {
	g := &Gauge{
		id:    id,
		value: api.Gauge(math.NaN()),
	}

	Publish(g)
	expvar.Publish(id.String(), g)
	return g
}

// NewGaugeString parses s as an Identifier and returns a new Gauge. If parsing
// s fails, it will panic. This simplifies initializing global variables.
func NewGaugeString(s string) *Gauge {
	id, err := api.ParseIdentifier(s)
	if err != nil {
		log.Fatal(err)
	}

	return NewGauge(id)
}

// Set sets g to v.
func (g *Gauge) Set(v float64) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.value = api.Gauge(v)
}

// String returns the string representation of g.
func (g *Gauge) String() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return strconv.FormatFloat(float64(g.value), 'g', -1, 64)
}

// ValueList returns the ValueList representation of g. Both, Time and Interval
// are set to zero.
func (g *Gauge) ValueList() api.ValueList {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return api.ValueList{
		Identifier: g.id,
		Values:     []api.Value{g.value},
	}
}
<commit_msg>export: Update documentation.<commit_after>/*
Package export provides an interface to instrument Go code.

Instrumenting Go code with this package is very similar to the "expvar" package
in the vanilla Go distribution. In fact, the variables exported with this
package are also registered with the "expvar" package, so that you can also use
other existing metric collection frameworks with it. This package differs in
that it has an explicitly cumulative type, Derive.

The intended usage pattern of this package is as follows: First, global
variables are initialized with NewDerive(), NewGauge(), NewDeriveString() or
NewGaugeString(). The Run() function is called as a separate goroutine as part
of your program's initialization and, last but not least, the variables are
updated with their respective update functions, Add() for Derive and Set() for
Gauge.

  // Initialize global variable.
  var requestCounter = export.NewDeriveString("example.com/golang/total_requests")

  // Call Run() in its own goroutine.
  func main() {
          client, err := network.Dial(network.DefaultIPv6Address, network.ClientOptions{})
          if err !=  nil {
	          log.Fatal(err)
	  }
          go export.Run(client, export.Options{
                  Interval: 10 * time.Second,
	  })
          // …
  }

  // Update variable.
  func requestHandler(w http.ResponseWriter, req *http.Request) {
          defer requestCounter.Add(1)
          // …
  }
*/
package export

import (
	"expvar"
	"log"
	"math"
	"strconv"
	"sync"
	"time"

	"collectd.org/api"
)

var (
	mutex sync.RWMutex
	vars  []Var
)

// Var is an abstract type for metrics exported by this package.
type Var interface {
	ValueList() api.ValueList
}

// Publish adds v to the internal list of exported metrics.
func Publish(v Var) {
	mutex.Lock()
	defer mutex.Unlock()

	vars = append(vars, v)
}

// Options holds options for the Run() function.
type Options struct {
	Interval time.Duration
}

// Run periodically calls the ValueList function of each Var, sets the Time and
// Interval fields and passes it w.Write(). This function blocks indefinitely.
func Run(w api.Writer, opts Options) error {
	ticker := time.NewTicker(opts.Interval)

	for {
		select {
		case _ = <-ticker.C:
			mutex.RLock()
			for _, v := range vars {
				vl := v.ValueList()
				vl.Time = time.Now()
				vl.Interval = opts.Interval
				if err := w.Write(vl); err != nil {
					mutex.RUnlock()
					return err
				}
			}
			mutex.RUnlock()
		}
	}
}

// Derive represents a cumulative integer data type, for example "requests
// served since server start". It implements the Var and expvar.Var interfaces.
type Derive struct {
	mu    sync.RWMutex
	id    api.Identifier
	value api.Derive
}

// NewDerive initializes a new Derive, registers it with the "expvar" package
// and returns it. The initial value is zero.
func NewDerive(id api.Identifier) *Derive {
	d := &Derive{
		id:    id,
		value: 0,
	}

	Publish(d)
	expvar.Publish(id.String(), d)
	return d
}

// NewDeriveString parses s as an Identifier and returns a new Derive. If
// parsing s fails, it will panic. This simplifies initializing global
// variables.
func NewDeriveString(s string) *Derive {
	id, err := api.ParseIdentifier(s)
	if err != nil {
		log.Fatal(err)
	}

	return NewDerive(id)
}

// Add adds diff to d.
func (d *Derive) Add(diff int) {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.value += api.Derive(diff)
}

// String returns the string representation of d.
func (d *Derive) String() string {
	d.mu.RLock()
	defer d.mu.RUnlock()
	return strconv.FormatInt(int64(d.value), 10)
}

// ValueList returns the ValueList representation of d. Both, Time and Interval
// are set to zero.
func (d *Derive) ValueList() api.ValueList {
	d.mu.RLock()
	defer d.mu.RUnlock()
	return api.ValueList{
		Identifier: d.id,
		Values:     []api.Value{d.value},
	}
}

// Gauge represents an absolute floating point data type, for example "heap
// memory used". It implements the Var and expvar.Var interfaces.
type Gauge struct {
	mu    sync.RWMutex
	id    api.Identifier
	value api.Gauge
}

// NewGauge initializes a new Gauge, registers it with the "expvar" package and
// returns it. The initial value is NaN.
func NewGauge(id api.Identifier) *Gauge {
	g := &Gauge{
		id:    id,
		value: api.Gauge(math.NaN()),
	}

	Publish(g)
	expvar.Publish(id.String(), g)
	return g
}

// NewGaugeString parses s as an Identifier and returns a new Gauge. If parsing
// s fails, it will panic. This simplifies initializing global variables.
func NewGaugeString(s string) *Gauge {
	id, err := api.ParseIdentifier(s)
	if err != nil {
		log.Fatal(err)
	}

	return NewGauge(id)
}

// Set sets g to v.
func (g *Gauge) Set(v float64) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.value = api.Gauge(v)
}

// String returns the string representation of g.
func (g *Gauge) String() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return strconv.FormatFloat(float64(g.value), 'g', -1, 64)
}

// ValueList returns the ValueList representation of g. Both, Time and Interval
// are set to zero.
func (g *Gauge) ValueList() api.ValueList {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return api.ValueList{
		Identifier: g.id,
		Values:     []api.Value{g.value},
	}
}
<|endoftext|>


<commit_before><commit_msg>Made htpasswd optional<commit_after><|endoftext|>


<commit_before>// +build ignore

package main

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

type Event struct {
	Name string
	Link string
	Desc string
	Spec string
}

func main() {
	// nameMap translates lowercase HTML attribute names from the MDN source
	// into a proper Go style name with MixedCaps and initialisms:
	//
	//  https://github.com/golang/go/wiki/CodeReviewComments#mixed-caps
	//  https://github.com/golang/go/wiki/CodeReviewComments#initialisms
	//
	nameMap := map[string]string{
		"afterprint":               "AfterPrint",
		"animationend":             "AnimationEnd",
		"animationiteration":       "AnimationIteration",
		"animationstart":           "AnimationStart",
		"audioprocess":             "AudioProcess",
		"audioend":                 "AudioEnd",
		"audiostart":               "AudioStart",
		"beforeprint":              "BeforePrint",
		"beforeunload":             "BeforeUnload",
		"canplay":                  "CanPlay",
		"canplaythrough":           "CanPlayThrough",
		"chargingchange":           "ChargingChange",
		"chargingtimechange":       "ChargingTimeChange",
		"compassneedscalibration":  "CompassNeedsCalibration",
		"compositionend":           "CompositionEnd",
		"compositionstart":         "CompositionStart",
		"compositionupdate":        "CompositionUpdate",
		"contextmenu":              "ContextMenu",
		"dblclick":                 "DoubleClick",
		"devicelight":              "DeviceLight",
		"devicemotion":             "DeviceMotion",
		"deviceorientation":        "DeviceOrientation",
		"deviceproximity":          "DeviceProximity",
		"dischargingtimechange":    "DischargingTimeChange",
		"dragend":                  "DragEnd",
		"dragenter":                "DragEnter",
		"dragleave":                "DragLeave",
		"dragover":                 "DragOver",
		"dragstart":                "DragStart",
		"durationchange":           "DurationChange",
		"focusin":                  "FocusIn",
		"focusout":                 "FocusOut",
		"fullscreenchange":         "FullScreenChange",
		"fullscreenerror":          "FullScreenError",
		"gamepadconnected":         "GamepadConnected",
		"gamepaddisconnected":      "GamepadDisconnected",
		"gotpointercapture":        "GotPointerCapture",
		"hashchange":               "HashChange",
		"keydown":                  "KeyDown",
		"keypress":                 "KeyPress",
		"keyup":                    "KeyUp",
		"languagechange":           "LanguageChange",
		"levelchange":              "LevelChange",
		"loadeddata":               "LoadedData",
		"loadedmetadata":           "LoadedMetadata",
		"loadend":                  "LoadEnd",
		"loadstart":                "LoadStart",
		"lostpointercapture":       "LostPointerCapture",
		"mousedown":                "MouseDown",
		"mouseenter":               "MouseEnter",
		"mouseleave":               "MouseLeave",
		"mousemove":                "MouseMove",
		"mouseout":                 "MouseOut",
		"mouseover":                "MouseOver",
		"mouseup":                  "MouseUp",
		"noupdate":                 "NoUpdate",
		"nomatch":                  "NoMatch",
		"notificationclick":        "NotificationClick",
		"orientationchange":        "OrientationChange",
		"pagehide":                 "PageHide",
		"pageshow":                 "PageShow",
		"pointercancel":            "PointerCancel",
		"pointerdown":              "PointerDown",
		"pointerenter":             "PointerEnter",
		"pointerleave":             "PointerLeave",
		"pointerlockchange":        "PointerLockChange",
		"pointerlockerror":         "PointerLockError",
		"pointermove":              "PointerMove",
		"pointerout":               "PointerOut",
		"pointerover":              "PointerOver",
		"pointerup":                "PointerUp",
		"popstate":                 "PopState",
		"pushsubscriptionchange":   "PushSubscriptionChange",
		"ratechange":               "RateChange",
		"readystatechange":         "ReadyStateChange",
		"resourcetimingbufferfull": "ResourceTimingBufferFull",
		"selectstart":              "SelectStart",
		"selectionchange":          "SelectionChange",
		"soundend":                 "SoundEnd",
		"soundstart":               "SoundStart",
		"speechend":                "SpeechEnd",
		"speechstart":              "SpeechStart",
		"timeupdate":               "TimeUpdate",
		"touchcancel":              "TouchCancel",
		"touchend":                 "TouchEnd",
		"touchenter":               "TouchEnter",
		"touchleave":               "TouchLeave",
		"touchmove":                "TouchMove",
		"touchstart":               "TouchStart",
		"transitionend":            "TransitionEnd",
		"updateready":              "UpdateReady",
		"upgradeneeded":            "UpgradeNeeded",
		"userproximity":            "UserProximity",
		"versionchange":            "VersionChange",
		"visibilitychange":         "VisibilityChange",
		"voiceschanged":            "VoicesChanged",
		"volumechange":             "VolumeChange",
		"vrdisplayconnected":       "VRDisplayConnected",
		"vrdisplaydisconnected":    "VRDisplayDisconnected",
		"vrdisplaypresentchange":   "VRDisplayPresentChange",
	}

	doc, err := goquery.NewDocument("https://developer.mozilla.org/en-US/docs/Web/Events")
	if err != nil {
		panic(err)
	}

	events := make(map[string]*Event)

	doc.Find(".standard-table").Eq(0).Find("tr").Each(func(i int, s *goquery.Selection) {
		cols := s.Find("td")
		if cols.Length() == 0 || cols.Find(".icon-thumbs-down-alt").Length() != 0 {
			return
		}
		link := cols.Eq(0).Find("a").Eq(0)
		var e Event
		e.Name = link.Text()
		e.Link, _ = link.Attr("href")
		e.Desc = strings.TrimSpace(cols.Eq(3).Text())
		e.Spec = strings.TrimSpace(cols.Eq(2).Text())

		funName := nameMap[e.Name]
		if funName == "" {
			funName = capitalize(e.Name)
		}

		if e.Desc != "" {
			// Lowercase the first letter of the description.
			lowercaseDesc := strings.ToLower(string(e.Desc[0])) + e.Desc[1:]
			e.Desc = fmt.Sprintf("%s is an event fired when %s", funName, lowercaseDesc)
		} else {
			e.Desc = "(no documentation)"
		}
		events[funName] = &e
	})

	var names []string
	for name := range events {
		names = append(names, name)
	}
	sort.Strings(names)

	file, err := os.Create("event.gen.go")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fmt.Fprint(file, `//go:generate go run generate.go

// Package event defines markup to bind DOM events.
//
// Generated from "Event reference" by Mozilla Contributors,
// https://developer.mozilla.org/en-US/docs/Web/Events, licensed under
// CC-BY-SA 2.5.
package event

import "github.com/gopherjs/vecty"
`)

	for _, name := range names {
		e := events[name]
		if e.Spec == "WebVR API" {
			continue // not stabilized
		}
		fmt.Fprintf(file, `%s
//
// https://developer.mozilla.org%s
func %s(listener func(*vecty.Event)) *vecty.EventListener {
	return &vecty.EventListener{Name: "%s", Listener: listener}
}
`, descToComments(e.Desc), e.Link[6:], name, e.Name)
	}
}

func capitalize(s string) string {
	return strings.ToUpper(s[:1]) + s[1:]
}

func descToComments(desc string) string {
	c := ""
	length := 80
	for _, word := range strings.Split(desc, " ") {
		if length+len(word)+1 > 80 {
			length = 3
			c += "\n//"
		}
		c += " " + word
		length += len(word) + 1
	}
	return c
}
<commit_msg>event: use an internal 'lowercase' helper analogous to the 'capitalize' helper<commit_after>// +build ignore

package main

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

type Event struct {
	Name string
	Link string
	Desc string
	Spec string
}

func main() {
	// nameMap translates lowercase HTML attribute names from the MDN source
	// into a proper Go style name with MixedCaps and initialisms:
	//
	//  https://github.com/golang/go/wiki/CodeReviewComments#mixed-caps
	//  https://github.com/golang/go/wiki/CodeReviewComments#initialisms
	//
	nameMap := map[string]string{
		"afterprint":               "AfterPrint",
		"animationend":             "AnimationEnd",
		"animationiteration":       "AnimationIteration",
		"animationstart":           "AnimationStart",
		"audioprocess":             "AudioProcess",
		"audioend":                 "AudioEnd",
		"audiostart":               "AudioStart",
		"beforeprint":              "BeforePrint",
		"beforeunload":             "BeforeUnload",
		"canplay":                  "CanPlay",
		"canplaythrough":           "CanPlayThrough",
		"chargingchange":           "ChargingChange",
		"chargingtimechange":       "ChargingTimeChange",
		"compassneedscalibration":  "CompassNeedsCalibration",
		"compositionend":           "CompositionEnd",
		"compositionstart":         "CompositionStart",
		"compositionupdate":        "CompositionUpdate",
		"contextmenu":              "ContextMenu",
		"dblclick":                 "DoubleClick",
		"devicelight":              "DeviceLight",
		"devicemotion":             "DeviceMotion",
		"deviceorientation":        "DeviceOrientation",
		"deviceproximity":          "DeviceProximity",
		"dischargingtimechange":    "DischargingTimeChange",
		"dragend":                  "DragEnd",
		"dragenter":                "DragEnter",
		"dragleave":                "DragLeave",
		"dragover":                 "DragOver",
		"dragstart":                "DragStart",
		"durationchange":           "DurationChange",
		"focusin":                  "FocusIn",
		"focusout":                 "FocusOut",
		"fullscreenchange":         "FullScreenChange",
		"fullscreenerror":          "FullScreenError",
		"gamepadconnected":         "GamepadConnected",
		"gamepaddisconnected":      "GamepadDisconnected",
		"gotpointercapture":        "GotPointerCapture",
		"hashchange":               "HashChange",
		"keydown":                  "KeyDown",
		"keypress":                 "KeyPress",
		"keyup":                    "KeyUp",
		"languagechange":           "LanguageChange",
		"levelchange":              "LevelChange",
		"loadeddata":               "LoadedData",
		"loadedmetadata":           "LoadedMetadata",
		"loadend":                  "LoadEnd",
		"loadstart":                "LoadStart",
		"lostpointercapture":       "LostPointerCapture",
		"mousedown":                "MouseDown",
		"mouseenter":               "MouseEnter",
		"mouseleave":               "MouseLeave",
		"mousemove":                "MouseMove",
		"mouseout":                 "MouseOut",
		"mouseover":                "MouseOver",
		"mouseup":                  "MouseUp",
		"noupdate":                 "NoUpdate",
		"nomatch":                  "NoMatch",
		"notificationclick":        "NotificationClick",
		"orientationchange":        "OrientationChange",
		"pagehide":                 "PageHide",
		"pageshow":                 "PageShow",
		"pointercancel":            "PointerCancel",
		"pointerdown":              "PointerDown",
		"pointerenter":             "PointerEnter",
		"pointerleave":             "PointerLeave",
		"pointerlockchange":        "PointerLockChange",
		"pointerlockerror":         "PointerLockError",
		"pointermove":              "PointerMove",
		"pointerout":               "PointerOut",
		"pointerover":              "PointerOver",
		"pointerup":                "PointerUp",
		"popstate":                 "PopState",
		"pushsubscriptionchange":   "PushSubscriptionChange",
		"ratechange":               "RateChange",
		"readystatechange":         "ReadyStateChange",
		"resourcetimingbufferfull": "ResourceTimingBufferFull",
		"selectstart":              "SelectStart",
		"selectionchange":          "SelectionChange",
		"soundend":                 "SoundEnd",
		"soundstart":               "SoundStart",
		"speechend":                "SpeechEnd",
		"speechstart":              "SpeechStart",
		"timeupdate":               "TimeUpdate",
		"touchcancel":              "TouchCancel",
		"touchend":                 "TouchEnd",
		"touchenter":               "TouchEnter",
		"touchleave":               "TouchLeave",
		"touchmove":                "TouchMove",
		"touchstart":               "TouchStart",
		"transitionend":            "TransitionEnd",
		"updateready":              "UpdateReady",
		"upgradeneeded":            "UpgradeNeeded",
		"userproximity":            "UserProximity",
		"versionchange":            "VersionChange",
		"visibilitychange":         "VisibilityChange",
		"voiceschanged":            "VoicesChanged",
		"volumechange":             "VolumeChange",
		"vrdisplayconnected":       "VRDisplayConnected",
		"vrdisplaydisconnected":    "VRDisplayDisconnected",
		"vrdisplaypresentchange":   "VRDisplayPresentChange",
	}

	doc, err := goquery.NewDocument("https://developer.mozilla.org/en-US/docs/Web/Events")
	if err != nil {
		panic(err)
	}

	events := make(map[string]*Event)

	doc.Find(".standard-table").Eq(0).Find("tr").Each(func(i int, s *goquery.Selection) {
		cols := s.Find("td")
		if cols.Length() == 0 || cols.Find(".icon-thumbs-down-alt").Length() != 0 {
			return
		}
		link := cols.Eq(0).Find("a").Eq(0)
		var e Event
		e.Name = link.Text()
		e.Link, _ = link.Attr("href")
		e.Desc = strings.TrimSpace(cols.Eq(3).Text())
		e.Spec = strings.TrimSpace(cols.Eq(2).Text())

		funName := nameMap[e.Name]
		if funName == "" {
			funName = capitalize(e.Name)
		}

		if e.Desc != "" {
			e.Desc = fmt.Sprintf("%s is an event fired when %s", funName, lowercase(e.Desc))
		} else {
			e.Desc = "(no documentation)"
		}
		events[funName] = &e
	})

	var names []string
	for name := range events {
		names = append(names, name)
	}
	sort.Strings(names)

	file, err := os.Create("event.gen.go")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fmt.Fprint(file, `//go:generate go run generate.go

// Package event defines markup to bind DOM events.
//
// Generated from "Event reference" by Mozilla Contributors,
// https://developer.mozilla.org/en-US/docs/Web/Events, licensed under
// CC-BY-SA 2.5.
package event

import "github.com/gopherjs/vecty"
`)

	for _, name := range names {
		e := events[name]
		if e.Spec == "WebVR API" {
			continue // not stabilized
		}
		fmt.Fprintf(file, `%s
//
// https://developer.mozilla.org%s
func %s(listener func(*vecty.Event)) *vecty.EventListener {
	return &vecty.EventListener{Name: "%s", Listener: listener}
}
`, descToComments(e.Desc), e.Link[6:], name, e.Name)
	}
}

func capitalize(s string) string {
	return strings.ToUpper(s[:1]) + s[1:]
}

func lowercase(s string) string {
	return strings.ToLower(s[:1]) + s[1:]
}

func descToComments(desc string) string {
	c := ""
	length := 80
	for _, word := range strings.Split(desc, " ") {
		if length+len(word)+1 > 80 {
			length = 3
			c += "\n//"
		}
		c += " " + word
		length += len(word) + 1
	}
	return c
}
<|endoftext|>


<commit_before>package redis

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

type RedisSession struct {
	pool   *redis.Pool
	prefix string
}

func NewRedisSession(server string) (*RedisSession, error) {
	s := &RedisSession{}

	pool := &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
	s.pool = pool
	// when we use connection pooling
	// dialing and returning an error will be
	// with the request
	return s, nil
}

// SetPrefix is used to add a prefix to all keys to be used. It is useful for
// creating namespaces for each different application
func (r *RedisSession) SetPrefix(name string) {
	r.prefix = name + ":"
}

func (r *RedisSession) addPrefix(name string) string {
	return r.prefix + name
}

// Do is a wrapper around redigo's redis.Do method that executes any redis
// command. Do does not support prefix support. Example usage: redis.Do("INCR",
// "counter").
func (r *RedisSession) Do(cmd string, args ...interface{}) (interface{}, error) {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Do(cmd, args...)
}

// Send is a wrapper around redigo's redis.Send method that writes the
// command to the client's output buffer.
func (r *RedisSession) Send(cmd string, args ...interface{}) error {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Send(cmd, args...)
}

// Set is used to hold the string value. If key already holds a value, it is
// overwritten, regardless of its type. A return of nil means successfull.
// Example usage: redis.Set("arslan:name", "fatih")
func (r *RedisSession) Set(key, value string) error {
	reply, err := r.Do("SET", r.addPrefix(key), value)
	if err != nil {
		return err
	}

	if reply != "OK" {
		return fmt.Errorf("reply string is wrong!: %s", reply)

	}
	return nil
}

// Get is used to get the value of key. If the key does not exist an empty
// string is returned. Usage: redis.Get("arslan")
func (r *RedisSession) Get(key string) (string, error) {
	reply, err := redis.String(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// GetInt is used the value of key as an integer. If the key does not exist or
// the stored value is a non-integer, zero is returned. Example usage:
// redis.GetInt("counter")
func (r *RedisSession) GetInt(key string) (int, error) {
	reply, err := redis.Int(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Del is used to remove the specified keys. Key is ignored if it does not
// exist. It returns the number of keys that were removed. Example usage:
// redis.Del("counter", "arslan:name")
func (r *RedisSession) Del(args ...interface{}) (int, error) {
	prefixed := make([]interface{}, 0)
	for _, arg := range args {
		prefixed = append(prefixed, r.addPrefix(arg.(string)))
	}

	reply, err := redis.Int(r.Do("DEL", prefixed...))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// Incr increments the number stored at key by one. If the key does not exist,
// it is set to 0 before performing the operation. An error is returned if the
// key contains a value of the wrong type or contains a string that can not be
// represented as integer
func (r *RedisSession) Incr(key string) (int, error) {
	reply, err := redis.Int(r.Do("INCR", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Expire sets a timeout on a key. After the timeout has expired, the key will
// automatically be deleted. Calling Expire on a key that has already expire
// set will update the expire value.
func (r *RedisSession) Expire(key string, timeout time.Duration) error {
	seconds := strconv.Itoa(int(timeout.Seconds()))
	reply, err := redis.Int(r.Do("EXPIRE", r.addPrefix(key), seconds))
	if err != nil {
		return err
	}

	if reply != 1 {
		return errors.New("key does not exist or the timeout could not be set")
	}

	return nil
}

// Exists returns true if key exists or false if not.
func (r *RedisSession) Exists(key string) bool {
	// does not have any err message to be checked, it return either 1 or 0
	reply, _ := redis.Int(r.Do("EXISTS", r.addPrefix(key)))

	if reply == 1 {
		return true
	}

	return false // means reply is 0, key does not exist
}

// Ping pings the redis server to check if it is alive or not
// If the server is not alive will return a proper error
func (r *RedisSession) Ping() error {
	reply, err := redis.String(r.Do("PING"))
	if err != nil {
		return err
	}

	if reply != "PONG" {
		return fmt.Errorf("reply string is wrong!: %s", reply)
	}

	return nil
}

// Scard gets the member count of a Set with given key
func (r *RedisSession) Scard(key string) (int, error) {
	reply, err := redis.Int(r.Do("SCARD", key))
	if err != nil {
		return 0, err
	}

	return reply, nil
}
<commit_msg>Reids: add support for closing redis connection pool<commit_after>package redis

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

type RedisSession struct {
	pool   *redis.Pool
	prefix string
}

func NewRedisSession(server string) (*RedisSession, error) {
	s := &RedisSession{}

	pool := &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
	s.pool = pool
	// when we use connection pooling
	// dialing and returning an error will be
	// with the request
	return s, nil
}

// Close closes the connection pool for redis
func (r *RedisSession) Close() error {
	return r.pool.Close()
}

// SetPrefix is used to add a prefix to all keys to be used. It is useful for
// creating namespaces for each different application
func (r *RedisSession) SetPrefix(name string) {
	r.prefix = name + ":"
}

func (r *RedisSession) addPrefix(name string) string {
	return r.prefix + name
}

// Do is a wrapper around redigo's redis.Do method that executes any redis
// command. Do does not support prefix support. Example usage: redis.Do("INCR",
// "counter").
func (r *RedisSession) Do(cmd string, args ...interface{}) (interface{}, error) {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Do(cmd, args...)
}

// Send is a wrapper around redigo's redis.Send method that writes the
// command to the client's output buffer.
func (r *RedisSession) Send(cmd string, args ...interface{}) error {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Send(cmd, args...)
}

// Set is used to hold the string value. If key already holds a value, it is
// overwritten, regardless of its type. A return of nil means successfull.
// Example usage: redis.Set("arslan:name", "fatih")
func (r *RedisSession) Set(key, value string) error {
	reply, err := r.Do("SET", r.addPrefix(key), value)
	if err != nil {
		return err
	}

	if reply != "OK" {
		return fmt.Errorf("reply string is wrong!: %s", reply)

	}
	return nil
}

// Get is used to get the value of key. If the key does not exist an empty
// string is returned. Usage: redis.Get("arslan")
func (r *RedisSession) Get(key string) (string, error) {
	reply, err := redis.String(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// GetInt is used the value of key as an integer. If the key does not exist or
// the stored value is a non-integer, zero is returned. Example usage:
// redis.GetInt("counter")
func (r *RedisSession) GetInt(key string) (int, error) {
	reply, err := redis.Int(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Del is used to remove the specified keys. Key is ignored if it does not
// exist. It returns the number of keys that were removed. Example usage:
// redis.Del("counter", "arslan:name")
func (r *RedisSession) Del(args ...interface{}) (int, error) {
	prefixed := make([]interface{}, 0)
	for _, arg := range args {
		prefixed = append(prefixed, r.addPrefix(arg.(string)))
	}

	reply, err := redis.Int(r.Do("DEL", prefixed...))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// Incr increments the number stored at key by one. If the key does not exist,
// it is set to 0 before performing the operation. An error is returned if the
// key contains a value of the wrong type or contains a string that can not be
// represented as integer
func (r *RedisSession) Incr(key string) (int, error) {
	reply, err := redis.Int(r.Do("INCR", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Expire sets a timeout on a key. After the timeout has expired, the key will
// automatically be deleted. Calling Expire on a key that has already expire
// set will update the expire value.
func (r *RedisSession) Expire(key string, timeout time.Duration) error {
	seconds := strconv.Itoa(int(timeout.Seconds()))
	reply, err := redis.Int(r.Do("EXPIRE", r.addPrefix(key), seconds))
	if err != nil {
		return err
	}

	if reply != 1 {
		return errors.New("key does not exist or the timeout could not be set")
	}

	return nil
}

// Exists returns true if key exists or false if not.
func (r *RedisSession) Exists(key string) bool {
	// does not have any err message to be checked, it return either 1 or 0
	reply, _ := redis.Int(r.Do("EXISTS", r.addPrefix(key)))

	if reply == 1 {
		return true
	}

	return false // means reply is 0, key does not exist
}

// Ping pings the redis server to check if it is alive or not
// If the server is not alive will return a proper error
func (r *RedisSession) Ping() error {
	reply, err := redis.String(r.Do("PING"))
	if err != nil {
		return err
	}

	if reply != "PONG" {
		return fmt.Errorf("reply string is wrong!: %s", reply)
	}

	return nil
}

// Scard gets the member count of a Set with given key
func (r *RedisSession) Scard(key string) (int, error) {
	reply, err := redis.Int(r.Do("SCARD", key))
	if err != nil {
		return 0, err
	}

	return reply, nil
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"strconv"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

const (
	testConfigPath = "./test.toml"
	env            = "MehmetAli"
	hostname       = "Koding"
	port           = 5672
)

func TestConfigMustRead(t *testing.T) {
	Convey("while testing MustRead", t, func() {
		Convey("valid testConfigPath should return config", func() {
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			So(MustRead(testConfigPath), ShouldNotBeNil)
		})
		Convey("invalid testConfigPath should panic", func() {
			So(func() { MustRead("testConfigPath") }, ShouldPanic)
		})
		Convey("getting string env variable should override config", func() {
			conf := MustRead(testConfigPath)
			os.Setenv("RABBITMQ_HOST", hostname)
			getStringEnvVar(&conf.Mq.Host, "RABBITMQ_HOST")
			So(conf.Mq.Host, ShouldEqual, hostname)

		})
		Convey("getting int env variable should override config", func() {
			conf := MustRead(testConfigPath)
			os.Setenv("RABBITMQ_PORT", strconv.Itoa(port))
			getIntEnvVar(&conf.Mq.Port, "RABBITMQ_PORT")
			So(conf.Mq.Port, ShouldEqual, port)

			os.Setenv("RABBITMQ_PORT", "eighty")
			So(func() { getIntEnvVar(&conf.Mq.Port, "RABBITMQ_PORT") }, ShouldPanic)

			os.Setenv("RABBITMQ_PORT", "")
		})
		Convey("setting socialapi env should override config", func() {
			err := os.Setenv("SOCIAL_API_ENV", env)
			So(err, ShouldBeNil)
			// just to be sure about the function will not panic
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			a := MustRead(testConfigPath)
			So(a.Environment, ShouldEqual, env)
		})
		Convey("setting socialapi hostname should override config", func() {
			err := os.Setenv("SOCIAL_API_HOSTNAME", hostname)
			So(err, ShouldBeNil)
			aPath := MustRead(testConfigPath)
			So(aPath.Uri, ShouldEqual, hostname)
		})
	})
}
<commit_msg>Config: Change  obsolete Uri field to Hostname in tests<commit_after>package config

import (
	"os"
	"strconv"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

const (
	testConfigPath = "./test.toml"
	env            = "MehmetAli"
	hostname       = "Koding"
	port           = 5672
)

func TestConfigMustRead(t *testing.T) {
	Convey("while testing MustRead", t, func() {
		Convey("valid testConfigPath should return config", func() {
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			So(MustRead(testConfigPath), ShouldNotBeNil)
		})
		Convey("invalid testConfigPath should panic", func() {
			So(func() { MustRead("testConfigPath") }, ShouldPanic)
		})
		Convey("getting string env variable should override config", func() {
			conf := MustRead(testConfigPath)
			os.Setenv("RABBITMQ_HOST", hostname)
			getStringEnvVar(&conf.Mq.Host, "RABBITMQ_HOST")
			So(conf.Mq.Host, ShouldEqual, hostname)

		})
		Convey("getting int env variable should override config", func() {
			conf := MustRead(testConfigPath)
			os.Setenv("RABBITMQ_PORT", strconv.Itoa(port))
			getIntEnvVar(&conf.Mq.Port, "RABBITMQ_PORT")
			So(conf.Mq.Port, ShouldEqual, port)

			os.Setenv("RABBITMQ_PORT", "eighty")
			So(func() { getIntEnvVar(&conf.Mq.Port, "RABBITMQ_PORT") }, ShouldPanic)

			os.Setenv("RABBITMQ_PORT", "")
		})
		Convey("setting socialapi env should override config", func() {
			err := os.Setenv("SOCIAL_API_ENV", env)
			So(err, ShouldBeNil)
			// just to be sure about the function will not panic
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			a := MustRead(testConfigPath)
			So(a.Environment, ShouldEqual, env)
		})
		Convey("setting socialapi hostname should override config", func() {
			err := os.Setenv("SOCIAL_API_HOSTNAME", hostname)
			So(err, ShouldBeNil)
			aPath := MustRead(testConfigPath)
			So(aPath.Hostname, ShouldEqual, hostname)
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/rest"
	"testing"

	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelHistory(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	appConfig := config.MustRead(r.Conf.Path)
	modelhelper.Initialize(appConfig.Mongo)
	defer modelhelper.Close()

	Convey("While testing history of a channel", t, func() {
		var channel *models.Channel
		Convey("We should be able to create it(channel) first", func() {
			account, err := models.CreateAccountInBothDbsWithNick("sinan")
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel, err = rest.CreateChannel(account.Id)
			So(err, ShouldBeNil)
			So(channel, ShouldNotBeNil)

			ses, err := models.FetchOrCreateSession(account.Nick)
			So(err, ShouldBeNil)
			So(ses, ShouldNotBeNil)

			Convey("While posting a new message to it", func() {
				var channelParticipant *models.ChannelParticipant
				var err error
				Convey("We should be able to create a participant first", func() {
					channelParticipant, err = rest.CreateChannelParticipant(channel.Id, account.Id)
					So(err, ShouldBeNil)
					So(channelParticipant, ShouldNotBeNil)

					Convey("Create posts with created participant", func() {
						channel := channel
						for i := 0; i < 10; i++ {
							post, err := rest.CreatePost(channel.Id, channelParticipant.AccountId)
							So(err, ShouldBeNil)
							So(post, ShouldNotBeNil)
							So(post.Id, ShouldNotEqual, 0)
							So(post.Body, ShouldNotEqual, "")

						}
						Convey("We should be able to fetch the history", func() {
							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								ses.ClientId,
							)
							So(err, ShouldBeNil)
							So(history, ShouldNotBeNil)
							So(len(history.MessageList), ShouldEqual, 10)

							Convey("After linking to another channel", func() {
								c2, err := rest.CreateChannelByGroupNameAndType(account.Id, channel.GroupName, models.Channel_TYPE_TOPIC)
								So(err, ShouldBeNil)
								So(c2, ShouldNotBeNil)

								cl, err := rest.CreateLink(channel.Id, c2.Id, ses.ClientId)
								So(err, ShouldBeNil)
								So(cl, ShouldNotBeNil)

								_, err = rest.GetHistory(
									c2.Id,
									&request.Query{
										AccountId: account.Id,
									},
									ses.ClientId,
								)

								So(err, ShouldNotBeNil)
								So(err.Error(), ShouldContainSubstring, "not found")
							})
						})

						Convey("We should be not able to fetch the history if the clientId is not set", func() {

							ses, err := models.FetchOrCreateSession(account.Nick)
							So(err, ShouldBeNil)
							So(ses, ShouldNotBeNil)

							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								"",
							)
							So(err, ShouldNotBeNil)
							So(history, ShouldBeNil)
						})

						Convey("We should be not able to fetch the history if the clientId doesnt exist", func() {

							ses, err := models.FetchOrCreateSession(account.Nick)
							So(err, ShouldBeNil)
							So(ses, ShouldNotBeNil)

							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								"foobarzaa",
							)

							So(err, ShouldNotBeNil)
							So(history, ShouldBeNil)
						})

						Convey("We should be able to get channel message count", func() {
							count, err := rest.CountHistory(channel.Id)
							So(err, ShouldBeNil)
							So(count, ShouldNotBeNil)
							So(count.TotalCount, ShouldEqual, 10)
						})
					})
				})
			})
		})
	})
}
<commit_msg>Socialapi: do not test channel redirection<commit_after>package main

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/rest"
	"testing"

	"github.com/koding/runner"

	. "github.com/smartystreets/goconvey/convey"
)

func TestChannelHistory(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	appConfig := config.MustRead(r.Conf.Path)
	modelhelper.Initialize(appConfig.Mongo)
	defer modelhelper.Close()

	Convey("While testing history of a channel", t, func() {
		var channel *models.Channel
		Convey("We should be able to create it(channel) first", func() {
			account, err := models.CreateAccountInBothDbsWithNick("sinan")
			So(err, ShouldBeNil)
			So(account, ShouldNotBeNil)

			channel, err = rest.CreateChannel(account.Id)
			So(err, ShouldBeNil)
			So(channel, ShouldNotBeNil)

			ses, err := models.FetchOrCreateSession(account.Nick)
			So(err, ShouldBeNil)
			So(ses, ShouldNotBeNil)

			Convey("While posting a new message to it", func() {
				var channelParticipant *models.ChannelParticipant
				var err error
				Convey("We should be able to create a participant first", func() {
					channelParticipant, err = rest.CreateChannelParticipant(channel.Id, account.Id)
					So(err, ShouldBeNil)
					So(channelParticipant, ShouldNotBeNil)

					Convey("Create posts with created participant", func() {
						channel := channel
						for i := 0; i < 10; i++ {
							post, err := rest.CreatePost(channel.Id, channelParticipant.AccountId)
							So(err, ShouldBeNil)
							So(post, ShouldNotBeNil)
							So(post.Id, ShouldNotEqual, 0)
							So(post.Body, ShouldNotEqual, "")

						}
						Convey("We should be able to fetch the history", func() {
							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								ses.ClientId,
							)
							So(err, ShouldBeNil)
							So(history, ShouldNotBeNil)
							So(len(history.MessageList), ShouldEqual, 10)

							SkipConvey("After linking to another channel", func() {
								c2, err := rest.CreateChannelByGroupNameAndType(account.Id, channel.GroupName, models.Channel_TYPE_TOPIC)
								So(err, ShouldBeNil)
								So(c2, ShouldNotBeNil)

								cl, err := rest.CreateLink(channel.Id, c2.Id, ses.ClientId)
								So(err, ShouldBeNil)
								So(cl, ShouldNotBeNil)

								_, err = rest.GetHistory(
									c2.Id,
									&request.Query{
										AccountId: account.Id,
									},
									ses.ClientId,
								)

								So(err, ShouldNotBeNil)
								So(err.Error(), ShouldContainSubstring, "not found")
							})
						})

						Convey("We should be not able to fetch the history if the clientId is not set", func() {

							ses, err := models.FetchOrCreateSession(account.Nick)
							So(err, ShouldBeNil)
							So(ses, ShouldNotBeNil)

							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								"",
							)
							So(err, ShouldNotBeNil)
							So(history, ShouldBeNil)
						})

						Convey("We should be not able to fetch the history if the clientId doesnt exist", func() {

							ses, err := models.FetchOrCreateSession(account.Nick)
							So(err, ShouldBeNil)
							So(ses, ShouldNotBeNil)

							history, err := rest.GetHistory(
								channel.Id,
								&request.Query{
									AccountId: account.Id,
								},
								"foobarzaa",
							)

							So(err, ShouldNotBeNil)
							So(history, ShouldBeNil)
						})

						Convey("We should be able to get channel message count", func() {
							count, err := rest.CountHistory(channel.Id)
							So(err, ShouldBeNil)
							So(count, ShouldNotBeNil)
							So(count.TotalCount, ShouldEqual, 10)
						})
					})
				})
			})
		})
	})
}
<|endoftext|>