<commit_before>// Package directory provides the GetAnnotator function, which returns an appropriate annotator for
// requests with a particular target date.
package directory

// A directory entry points to an appropriate CompositeAnnotator.
// The composite annotators will have:
//  1. An ASN annotator
//  2. Either
//     a. A Geolite2 annotator
//     b. A legacy v4 and legacy v6 annotator
//
// Once the ASN annotators are available, we will have a different CA for every date, but
// until then, we only have a different CA for each date where a new v4 or v6, or a new GL2
// annotator is available.
//
// To construct the directory, we begin with lists of Annotator objects for each type of annotation.
// We first merge the v4 and v6 annotators into a list of CompositeAnnotators, using MergeAnnotators.
// We then append all the GeoLite2 annotators to this list.
// Then, we merge the Geo annotation list with the ASN annotator list.
// Finally, we use BuildDirectory to create a Directory based on this list.

import (
	"errors"
	"time"

	"github.com/m-lab/annotation-service/api"
)

var (
	// These errors should never happen, unless there is a bug in our implementation.

	// ErrEmptyDirectory is returned by GetAnnotator if a Directory has no entries.
	ErrEmptyDirectory = errors.New("Directory is empty")
	// ErrNilAnnotator is returned if GetAnnotator encounters a nil Directory entry.
	ErrNilAnnotator = errors.New("Annotator is nil")
)

// Directory maintains a list of Annotators, indexed by date.
type Directory struct {
	// fields are immutable after construction using BuildDirectory()
	startDate  time.Time
	annotators []api.Annotator
}

func daysSince(ref time.Time, date time.Time) int {
	i := int(date.Unix()-ref.Unix()) / (24 * 3600)
	if i < 0 {
		return 0
	}
	return i
}

// GetAnnotator returns an appropriate api.Annotator for a given date.
func (d *Directory) GetAnnotator(date time.Time) (api.Annotator, error) {
	if len(d.annotators) < 1 {
		return nil, ErrEmptyDirectory
	}

	index := daysSince(d.startDate, date)
	if index >= len(d.annotators) {
		index = len(d.annotators) - 1
	}
	if d.annotators[index] == nil {
		return nil, ErrNilAnnotator
	}
	return d.annotators[index], nil
}

func (d *Directory) replace(ann api.Annotator) {
	date := ann.AnnotatorDate()

	// Use this for any date strictly after the AnnotatorDate...
	replaceAfter := daysSince(d.startDate, date)
	for i := replaceAfter; i < len(d.annotators); i++ {
		old := d.annotators[i]
		if old == nil {
			d.annotators[i] = ann
		} else {
			oldDate := old.AnnotatorDate()
			if oldDate.Before(date) {
				d.annotators[i] = ann
			}
		}

	}
}

// BuildDirectory builds a Directory object from a list of Annotators.
// TODO - how do we handle multiple lists of Annotators that should be merged?
func BuildDirectory(all []api.Annotator) *Directory {
	start := time.Now()

	for i := range all {
		if all[i] != nil && all[i].AnnotatorDate().Before(start) {
			start = all[i].AnnotatorDate()
		}
	}

	annotators := make([]api.Annotator, daysSince(start, time.Now()))

	dir := Directory{startDate: start, annotators: annotators}
	// NOTE: this would be slightly more efficient if done in reverse order.
	for i := range all {
		if all[i] != nil {
			dir.replace(all[i])
		}
	}

	return &dir
}

// Advance advances to the next date among the list elements.
func advance(lists [][]api.Annotator) ([][]api.Annotator, bool) {
	// Start far in the future.
	date := time.Now().Add(1000000 * time.Hour)
	first := -1
	for l, list := range lists {
		if len(list) > 1 {
			d := list[1].AnnotatorDate()
			if d.Before(date) {
				first = l
				date = d
			}
		}
	}
	if first == -1 {
		return nil, false
	}

	// Now advance any list that has the same target date.
	for l, list := range lists {
		if len(list) > 1 && list[1].AnnotatorDate().Equal(date) {
			lists[l] = list[1:]
		}
	}
	return lists, true
}

// MergeAnnotators merges multiple lists of annotators, and returns a list of CompositeAnnotators, each
// containing an appropriate annotator from each list.
func MergeAnnotators(lists [][]api.Annotator) []api.Annotator {
	listCount := len(lists)
	if listCount == 0 {
		return nil
	}
	if listCount == 1 {
		return lists[0]
	}

	groups := make([][]api.Annotator, 0, 100)

	// For each step, add a group, then advance the list(s) with earliest dates at second index.
	for more := true; more; {
		// Create and add group with first annotator from each list
		group := make([]api.Annotator, len(lists))
		for l, list := range lists {
			group[l] = list[0]
		}
		groups = append(groups, group)
		// Advance the lists that have earliest next elements.
		lists, more = advance(lists)
	}

	result := make([]api.Annotator, len(groups))
	for i, group := range groups {
		result[i] = api.NewCompositeAnnotator(group)
	}
	return result
}
<commit_msg>Add example use<commit_after>// Package directory provides the GetAnnotator function, which returns an appropriate annotator for
// requests with a particular target date.
package directory

// A directory entry points to an appropriate CompositeAnnotator.
// The composite annotators will have:
//  1. An ASN annotator
//  2. Either
//     a. A Geolite2 annotator
//     b. A legacy v4 and legacy v6 annotator
//
// Once the ASN annotators are available, we will have a different CA for every date, but
// until then, we only have a different CA for each date where a new v4 or v6, or a new GL2
// annotator is available.
//
// To construct the directory, we begin with lists of Annotator objects for each type of annotation.
// We first merge the v4 and v6 annotators into a list of CompositeAnnotators, using MergeAnnotators.
// We then append all the GeoLite2 annotators to this list.
// Then, we merge the Geo annotation list with the ASN annotator list.
// Finally, we use BuildDirectory to create a Directory based on this list.

// Example use (simplified, with some functions that don't exist yet):
// v4, _ = geoloader.LoadLegacyV4(nil)
// v6, _ = geoloader.LoadLegacyV6(nil)
// legacy := directory.MergeAnnotators(v4, v6)  // Creates annotators that will handle v4 or v6
// g2, _ = geoloader.LoadGeolite2(nil)
// combo := make([]api.Annotator, len(g2)+len(legacy))
// combo = append(combo, g2...)
// combo = append(combo, legacy...)
// annotatorDirectory = directory.BuildDirectory(combo)

import (
	"errors"
	"time"

	"github.com/m-lab/annotation-service/api"
)

var (
	// These errors should never happen, unless there is a bug in our implementation.

	// ErrEmptyDirectory is returned by GetAnnotator if a Directory has no entries.
	ErrEmptyDirectory = errors.New("Directory is empty")
	// ErrNilAnnotator is returned if GetAnnotator encounters a nil Directory entry.
	ErrNilAnnotator = errors.New("Annotator is nil")
)

// Directory maintains a list of Annotators, indexed by date.
type Directory struct {
	// fields are immutable after construction using BuildDirectory()
	startDate  time.Time
	annotators []api.Annotator
}

func daysSince(ref time.Time, date time.Time) int {
	i := int(date.Unix()-ref.Unix()) / (24 * 3600)
	if i < 0 {
		return 0
	}
	return i
}

// GetAnnotator returns an appropriate api.Annotator for a given date.
func (d *Directory) GetAnnotator(date time.Time) (api.Annotator, error) {
	if len(d.annotators) < 1 {
		return nil, ErrEmptyDirectory
	}

	index := daysSince(d.startDate, date)
	if index >= len(d.annotators) {
		index = len(d.annotators) - 1
	}
	if d.annotators[index] == nil {
		return nil, ErrNilAnnotator
	}
	return d.annotators[index], nil
}

func (d *Directory) replace(ann api.Annotator) {
	date := ann.AnnotatorDate()

	// Use this for any date strictly after the AnnotatorDate...
	replaceAfter := daysSince(d.startDate, date)
	for i := replaceAfter; i < len(d.annotators); i++ {
		old := d.annotators[i]
		if old == nil {
			d.annotators[i] = ann
		} else {
			oldDate := old.AnnotatorDate()
			if oldDate.Before(date) {
				d.annotators[i] = ann
			}
		}

	}
}

// BuildDirectory builds a Directory object from a list of Annotators.
// TODO - how do we handle multiple lists of Annotators that should be merged?
func BuildDirectory(all []api.Annotator) *Directory {
	start := time.Now()

	for i := range all {
		if all[i] != nil && all[i].AnnotatorDate().Before(start) {
			start = all[i].AnnotatorDate()
		}
	}

	annotators := make([]api.Annotator, daysSince(start, time.Now()))

	dir := Directory{startDate: start, annotators: annotators}
	// NOTE: this would be slightly more efficient if done in reverse order.
	for i := range all {
		if all[i] != nil {
			dir.replace(all[i])
		}
	}

	return &dir
}

// Advance advances to the next date among the list elements.
func advance(lists [][]api.Annotator) ([][]api.Annotator, bool) {
	// Start far in the future.
	date := time.Now().Add(1000000 * time.Hour)
	first := -1
	for l, list := range lists {
		if len(list) > 1 {
			d := list[1].AnnotatorDate()
			if d.Before(date) {
				first = l
				date = d
			}
		}
	}
	if first == -1 {
		return nil, false
	}

	// Now advance any list that has the same target date.
	for l, list := range lists {
		if len(list) > 1 && list[1].AnnotatorDate().Equal(date) {
			lists[l] = list[1:]
		}
	}
	return lists, true
}

// MergeAnnotators merges multiple lists of annotators, and returns a list of CompositeAnnotators, each
// containing an appropriate annotator from each list.
func MergeAnnotators(lists [][]api.Annotator) []api.Annotator {
	listCount := len(lists)
	if listCount == 0 {
		return nil
	}
	if listCount == 1 {
		return lists[0]
	}

	groups := make([][]api.Annotator, 0, 100)

	// For each step, add a group, then advance the list(s) with earliest dates at second index.
	for more := true; more; {
		// Create and add group with first annotator from each list
		group := make([]api.Annotator, len(lists))
		for l, list := range lists {
			group[l] = list[0]
		}
		groups = append(groups, group)
		// Advance the lists that have earliest next elements.
		lists, more = advance(lists)
	}

	result := make([]api.Annotator, len(groups))
	for i, group := range groups {
		result[i] = api.NewCompositeAnnotator(group)
	}
	return result
}
<|endoftext|>


<commit_before>package dashboard

// This file handles the front page.

import (
	"bytes"
	"html/template"
	"io"
	"net/http"
	"sync"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/user"
)

func init() {
	http.HandleFunc("/", handleFront)
	http.HandleFunc("/favicon.ico", http.NotFound)
}

func handleFront(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	data := &frontPageData{
		Reviewers: personList,
	}
	var currentPerson string
	currentPerson, data.UserIsReviewer = emailToPerson[user.Current(c).Email]

	var wg sync.WaitGroup
	errc := make(chan error, 10)
	activeCLs := datastore.NewQuery("CL").
		Filter("Closed =", false).
		Order("-Modified")

	tableFetch := func(index int, f func(tbl *clTable) error) {
		wg.Add(1)
		go func() {
			defer wg.Done()
			start := time.Now()
			if err := f(&data.Tables[index]); err != nil {
				errc <- err
			}
			data.Timing[index] = time.Now().Sub(start)
		}()
	}

	if data.UserIsReviewer {
		tableFetch(0, func(tbl *clTable) error {
			q := activeCLs.Filter("Reviewer =", currentPerson).Limit(10)
			tbl.Title = "CLs assigned to you for review"
			tbl.Assignable = true
			_, err := q.GetAll(c, &tbl.CLs)
			return err
		})
	}

	tableFetch(1, func(tbl *clTable) error {
		q := activeCLs.Filter("Author =", currentPerson).Limit(10)
		tbl.Title = "CLs sent by you"
		tbl.Assignable = true
		_, err := q.GetAll(c, &tbl.CLs)
		return err
	})

	tableFetch(2, func(tbl *clTable) error {
		q := activeCLs.Limit(50)
		tbl.Title = "Other active CLs"
		tbl.Assignable = true
		if _, err := q.GetAll(c, &tbl.CLs); err != nil {
			return err
		}
		// filter
		if data.UserIsReviewer {
			for i := len(tbl.CLs) - 1; i >= 0; i-- {
				cl := tbl.CLs[i]
				if cl.Author == currentPerson || cl.Reviewer == currentPerson {
					tbl.CLs[i] = tbl.CLs[len(tbl.CLs)-1]
					tbl.CLs = tbl.CLs[:len(tbl.CLs)-1]
				}
			}
		}
		return nil
	})

	tableFetch(3, func(tbl *clTable) error {
		q := datastore.NewQuery("CL").
			Filter("Closed =", true).
			Order("-Modified").
			Limit(10)
		tbl.Title = "Recently closed CLs"
		tbl.Assignable = false
		_, err := q.GetAll(c, &tbl.CLs)
		return err
	})

	wg.Wait()

	select {
	case err := <-errc:
		c.Errorf("%v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
	}

	var b bytes.Buffer
	if err := frontPage.ExecuteTemplate(&b, "front", data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	io.Copy(w, &b)
}

type frontPageData struct {
	Tables [4]clTable
	Timing [4]time.Duration

	Reviewers      []string
	UserIsReviewer bool
}

type clTable struct {
	Title      string
	Assignable bool
	CLs        []*CL
}

var frontPage = template.Must(template.New("front").Funcs(template.FuncMap{
	"selected": func(a, b string) string {
		if a == b {
			return "selected"
		}
		return ""
	},
}).Parse(`
<!doctype html>

    <title>Go code reviews</title>
    <link rel="icon" type="image/png" href="/static/icon.png" />

Go code reviews

{{range $tbl := .Tables}}

{{$tbl.Title}}

{{if .CLs}}

{{range $cl := .CLs}}

    {{$cl.DisplayOwner}}
    {{if $tbl.Assignable}}

    <select id="cl-rev-{{$cl.Number}}" {{if not $.UserIsReviewer}}disabled{{end}}>
      <option></option>
      {{range $.Reviewers}}
      <option {{selected . $cl.Reviewer}}>{{.}}</option>
      {{end}}
    </select>

    {{end}}

      {{.Number}}: {{.FirstLineHTML}}
      {{if and .LGTMs $tbl.Assignable}}
LGTMs: {{.LGTMHTML}}{{end}}

    {{.ModifiedAgo}}

{{end}}

{{else}}
none
{{end}}
{{end}}

<address>
datastore timing: {{range .Timing}} {{.}}{{end}}
</address>

`))
<commit_msg>misc/dashboard/codereview: add handy logout URL.<commit_after>package dashboard

// This file handles the front page.

import (
	"bytes"
	"html/template"
	"io"
	"net/http"
	"sync"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/user"
)

func init() {
	http.HandleFunc("/", handleFront)
	http.HandleFunc("/favicon.ico", http.NotFound)
}

func handleFront(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	data := &frontPageData{
		Reviewers: personList,
		User:      user.Current(c).Email,
	}
	var currentPerson string
	currentPerson, data.UserIsReviewer = emailToPerson[data.User]

	var wg sync.WaitGroup
	errc := make(chan error, 10)
	activeCLs := datastore.NewQuery("CL").
		Filter("Closed =", false).
		Order("-Modified")

	tableFetch := func(index int, f func(tbl *clTable) error) {
		wg.Add(1)
		go func() {
			defer wg.Done()
			start := time.Now()
			if err := f(&data.Tables[index]); err != nil {
				errc <- err
			}
			data.Timing[index] = time.Now().Sub(start)
		}()
	}

	if data.UserIsReviewer {
		tableFetch(0, func(tbl *clTable) error {
			q := activeCLs.Filter("Reviewer =", currentPerson).Limit(10)
			tbl.Title = "CLs assigned to you for review"
			tbl.Assignable = true
			_, err := q.GetAll(c, &tbl.CLs)
			return err
		})
	}

	tableFetch(1, func(tbl *clTable) error {
		q := activeCLs.Filter("Author =", currentPerson).Limit(10)
		tbl.Title = "CLs sent by you"
		tbl.Assignable = true
		_, err := q.GetAll(c, &tbl.CLs)
		return err
	})

	tableFetch(2, func(tbl *clTable) error {
		q := activeCLs.Limit(50)
		tbl.Title = "Other active CLs"
		tbl.Assignable = true
		if _, err := q.GetAll(c, &tbl.CLs); err != nil {
			return err
		}
		// filter
		if data.UserIsReviewer {
			for i := len(tbl.CLs) - 1; i >= 0; i-- {
				cl := tbl.CLs[i]
				if cl.Author == currentPerson || cl.Reviewer == currentPerson {
					tbl.CLs[i] = tbl.CLs[len(tbl.CLs)-1]
					tbl.CLs = tbl.CLs[:len(tbl.CLs)-1]
				}
			}
		}
		return nil
	})

	tableFetch(3, func(tbl *clTable) error {
		q := datastore.NewQuery("CL").
			Filter("Closed =", true).
			Order("-Modified").
			Limit(10)
		tbl.Title = "Recently closed CLs"
		tbl.Assignable = false
		_, err := q.GetAll(c, &tbl.CLs)
		return err
	})

	// Not really a table fetch.
	tableFetch(0, func(_ *clTable) error {
		var err error
		data.LogoutURL, err = user.LogoutURL(c, "/")
		return err
	})

	wg.Wait()

	select {
	case err := <-errc:
		c.Errorf("%v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	default:
	}

	var b bytes.Buffer
	if err := frontPage.ExecuteTemplate(&b, "front", &data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	io.Copy(w, &b)
}

type frontPageData struct {
	Tables [4]clTable
	Timing [4]time.Duration

	Reviewers      []string
	UserIsReviewer bool

	User, LogoutURL string
}

type clTable struct {
	Title      string
	Assignable bool
	CLs        []*CL
}

var frontPage = template.Must(template.New("front").Funcs(template.FuncMap{
	"selected": func(a, b string) string {
		if a == b {
			return "selected"
		}
		return ""
	},
}).Parse(`
<!doctype html>

    <title>Go code reviews</title>
    <link rel="icon" type="image/png" href="/static/icon.png" />

Go code reviews

{{range $tbl := .Tables}}

{{$tbl.Title}}

{{if .CLs}}

{{range $cl := .CLs}}

    {{$cl.DisplayOwner}}
    {{if $tbl.Assignable}}

    <select id="cl-rev-{{$cl.Number}}" {{if not $.UserIsReviewer}}disabled{{end}}>
      <option></option>
      {{range $.Reviewers}}
      <option {{selected . $cl.Reviewer}}>{{.}}</option>
      {{end}}
    </select>

    {{end}}

      {{.Number}}: {{.FirstLineHTML}}
      {{if and .LGTMs $tbl.Assignable}}
LGTMs: {{.LGTMHTML}}{{end}}

    {{.ModifiedAgo}}

{{end}}

{{else}}
none
{{end}}
{{end}}

<address>
You are {{.User}} · logout

datastore timing: {{range .Timing}} {{.}}{{end}}
</address>

`))
<|endoftext|>


<commit_before>package validator

import (
	"testing"

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

func TestObjectIDValidator(t *testing.T) {

	Convey("When creating a ObjectID validator", t, func() {
		v := ObjectID{}
		Convey("give a ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ea"), ShouldBeTrue)
		})
		Convey("give a NOT ObjectID paramater", func() {
			So(v.Validate("507f191e810c1972"), ShouldBeFalse)
		})
		Convey("give a NOT ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ev"), ShouldBeFalse)
		})
		Convey("give a 'empty' paramater", func() {
			So(v.Validate(""), ShouldBeFalse)
		})
	})

	Convey("When creating a ObjectID not if empty validator", t, func() {
		v := ObjectIDIfNotEmpty{}
		Convey("give a ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ea"), ShouldBeTrue)
		})
		Convey("give a NOT ObjectID paramater", func() {
			So(v.Validate("507f191e810c1972"), ShouldBeFalse)
		})
		Convey("give a NOT ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ev"), ShouldBeFalse)
		})
		Convey("give a 'empty' paramater", func() {
			So(v.Validate(""), ShouldBeTrue)
		})
	})
}
<commit_msg>Fix test case<commit_after>package validator

import (
	"testing"

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

func TestObjectIDValidator(t *testing.T) {

	Convey("When creating a ObjectID validator", t, func() {
		v := ObjectID{}
		Convey("give a ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ea"), ShouldBeTrue)
		})
		Convey("give a NOT ObjectID paramater (short)", func() {
			So(v.Validate("507f191e810c1972"), ShouldBeFalse)
		})
		Convey("give a NOT ObjectID paramater (long)", func() {
			So(v.Validate("507f191e810c19729de860ev"), ShouldBeFalse)
		})
		Convey("give a 'empty' paramater", func() {
			So(v.Validate(""), ShouldBeFalse)
		})
	})

	Convey("When creating a ObjectID not if empty validator", t, func() {
		v := ObjectIDIfNotEmpty{}
		Convey("give a ObjectID paramater", func() {
			So(v.Validate("507f191e810c19729de860ea"), ShouldBeTrue)
		})
		Convey("give a NOT ObjectID paramater (short)", func() {
			So(v.Validate("507f191e810c1972"), ShouldBeFalse)
		})
		Convey("give a NOT ObjectID paramater (long)", func() {
			So(v.Validate("507f191e810c19729de860ev"), ShouldBeFalse)
		})
		Convey("give a 'empty' paramater", func() {
			So(v.Validate(""), ShouldBeTrue)
		})
	})
}
<|endoftext|>


<commit_before>package websocket

import (
	"encoding/json"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
)

type Message struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"`
	To   string          `json:"to"`
}
type Clients struct {
	sync.Mutex
	clients []*Client
	Router  *Router `inject:""`
}
type Client struct {
	Name       string
	in         <-chan *Message
	out        chan<- *Message
	done       <-chan bool
	err        <-chan error
	disconnect chan<- int
}

// Add a client to a room
func (r *Clients) appendClient(client *Client) {
	r.Lock()
	r.clients = append(r.clients, client)
	r.Unlock()

	msgs := r.Router.RunOnClientConnectHandlers()
	for _, msg := range msgs {
		client.out <- msg
	}
}

// Message all the other clients
func (r *Clients) SendToAll(t string, data interface{}) {

	out, err := json.Marshal(data)
	if err != nil {
		log.Error(err)
		return
	}
	msg := &Message{Type: t, Data: out}

	r.Lock()
	defer r.Unlock()
	clientsToRemove := make([]*Client, 0)

	for _, c := range r.clients {
		select {
		case c.out <- msg:
			// Everything went well :)
		case <-time.After(time.Second):
			log.Warn("Failed writing to websocket: timeout (", c.Name, ")")
			clientsToRemove = append(clientsToRemove, c)
		}
	}

	go func() {
		for _, c := range clientsToRemove {
			r.removeClient(c)
		}
	}()
}

// Remove a client
func (r *Clients) removeClient(client *Client) {
	r.Lock()
	defer r.Unlock()

	for index, c := range r.clients {
		if c == client {
			c.disconnect <- websocket.CloseInternalServerErr
			r.clients = append(r.clients[:index], r.clients[(index+1):]...)
		}
	}
}

// Disconnect all clients
func (r *Clients) disconnectAll() {
	r.Lock()
	defer r.Unlock()

	for _, c := range r.clients {
		c.disconnect <- websocket.CloseGoingAway
	}
}

func newClients() *Clients {
	return &Clients{sync.Mutex{}, make([]*Client, 0), nil}
}
func (clients *Clients) WebsocketRoute(params martini.Params, receiver <-chan *Message, sender chan<- *Message, done <-chan bool, disconnect chan<- int, err <-chan error) (int, string) {
	client := &Client{params["clientname"], receiver, sender, done, err, disconnect}
	clients.appendClient(client)

	// A single select can be used to do all the messaging
	for {
		select {
		case <-client.err:
			// Don't try to do this:
			// client.out <- &Message{"system", "system", "There has been an error with your connection"}
			// The socket connection is already long gone.
			// Use the error for statistics etc
		case msg := <-client.in:
			//TODO implement command from websocket here. using same process as WebHandlerCommandToNode

			log.Info("incoming message from webui on websocket", string(msg.Data))
			clients.Router.Run(msg)
		case <-client.done:
			clients.removeClient(client)
			return 200, "OK"
		}
	}
}
<commit_msg>Revert "Possible fix for bug #17"<commit_after>package websocket

import (
	"encoding/json"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
)

type Message struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"`
	To   string          `json:"to"`
}
type Clients struct {
	sync.Mutex
	clients []*Client
	Router  *Router `inject:""`
}
type Client struct {
	Name       string
	in         <-chan *Message
	out        chan<- *Message
	done       <-chan bool
	err        <-chan error
	disconnect chan<- int
}

// Add a client to a room
func (r *Clients) appendClient(client *Client) {
	r.Lock()
	r.clients = append(r.clients, client)
	r.Unlock()

	msgs := r.Router.RunOnClientConnectHandlers()
	for _, msg := range msgs {
		client.out <- msg
	}
}

// Message all the other clients
func (r *Clients) SendToAll(t string, data interface{}) {

	out, err := json.Marshal(data)
	if err != nil {
		log.Error(err)
		return
	}
	msg := &Message{Type: t, Data: out}

	r.Lock()
	defer r.Unlock()
	for _, c := range r.clients {
		select {
		case c.out <- msg:
			// Everything went well :)
		case <-time.After(time.Second):
			log.Warn("Failed writing to websocket: timeout (", c.Name, ")")
		}
	}
}

// Remove a client
func (r *Clients) removeClient(client *Client) {
	r.Lock()
	defer r.Unlock()

	for index, c := range r.clients {
		if c == client {
			r.clients = append(r.clients[:index], r.clients[(index+1):]...)
		}
	}
}

// Disconnect all clients
func (r *Clients) disconnectAll() {
	r.Lock()
	defer r.Unlock()

	for _, c := range r.clients {
		c.disconnect <- websocket.CloseGoingAway
	}
}

func newClients() *Clients {
	return &Clients{sync.Mutex{}, make([]*Client, 0), nil}
}
func (clients *Clients) WebsocketRoute(params martini.Params, receiver <-chan *Message, sender chan<- *Message, done <-chan bool, disconnect chan<- int, err <-chan error) (int, string) {
	client := &Client{params["clientname"], receiver, sender, done, err, disconnect}
	clients.appendClient(client)

	// A single select can be used to do all the messaging
	for {
		select {
		case <-client.err:
			// Don't try to do this:
			// client.out <- &Message{"system", "system", "There has been an error with your connection"}
			// The socket connection is already long gone.
			// Use the error for statistics etc
		case msg := <-client.in:
			//TODO implement command from websocket here. using same process as WebHandlerCommandToNode

			log.Info("incoming message from webui on websocket", string(msg.Data))
			clients.Router.Run(msg)
		case <-client.done:
			clients.removeClient(client)
			return 200, "OK"
		}
	}
}
<|endoftext|>


<commit_before>package log_buffer

import "time"

type MemBuffer struct {
	buf       []byte
	size      int
	startTime time.Time
	stopTime  time.Time
}

type SealedBuffers struct {
	buffers []*MemBuffer
}

func newSealedBuffers(size int) *SealedBuffers {
	sbs := &SealedBuffers{}

	sbs.buffers = make([]*MemBuffer, size)
	for i := 0; i < size; i++ {
		sbs.buffers[i] = &MemBuffer{
			buf: make([]byte, BufferSize),
		}
	}

	return sbs
}

func (sbs *SealedBuffers) SealBuffer(startTime, stopTime time.Time, buf []byte, pos int) (newBuf []byte) {
	oldMemBuffer := sbs.buffers[0]
	size := len(sbs.buffers)
	for i := 0; i < size-1; i++ {
		sbs.buffers[i].buf = sbs.buffers[i+1].buf
		sbs.buffers[i].size = sbs.buffers[i+1].size
		sbs.buffers[i].startTime = sbs.buffers[i+1].startTime
		sbs.buffers[i].stopTime = sbs.buffers[i+1].stopTime
	}
	sbs.buffers[size-1].buf = buf
	sbs.buffers[size-1].size = pos
	sbs.buffers[size-1].startTime = startTime
	sbs.buffers[size-1].stopTime = stopTime
	return oldMemBuffer.buf
}

func (mb *MemBuffer) locateByTs(lastReadTime time.Time) (pos int) {
	lastReadTs := lastReadTime.UnixNano()
	for pos < len(mb.buf) {
		size, t := readTs(mb.buf, pos)
		if t > lastReadTs {
			return
		}
		pos += size + 4
	}
	return len(mb.buf)
}
<commit_msg>for debugging<commit_after>package log_buffer

import (
	"fmt"
	"time"
)

type MemBuffer struct {
	buf       []byte
	size      int
	startTime time.Time
	stopTime  time.Time
}

type SealedBuffers struct {
	buffers []*MemBuffer
}

func newSealedBuffers(size int) *SealedBuffers {
	sbs := &SealedBuffers{}

	sbs.buffers = make([]*MemBuffer, size)
	for i := 0; i < size; i++ {
		sbs.buffers[i] = &MemBuffer{
			buf: make([]byte, BufferSize),
		}
	}

	return sbs
}

func (sbs *SealedBuffers) SealBuffer(startTime, stopTime time.Time, buf []byte, pos int) (newBuf []byte) {
	oldMemBuffer := sbs.buffers[0]
	size := len(sbs.buffers)
	for i := 0; i < size-1; i++ {
		sbs.buffers[i].buf = sbs.buffers[i+1].buf
		sbs.buffers[i].size = sbs.buffers[i+1].size
		sbs.buffers[i].startTime = sbs.buffers[i+1].startTime
		sbs.buffers[i].stopTime = sbs.buffers[i+1].stopTime
	}
	sbs.buffers[size-1].buf = buf
	sbs.buffers[size-1].size = pos
	sbs.buffers[size-1].startTime = startTime
	sbs.buffers[size-1].stopTime = stopTime
	return oldMemBuffer.buf
}

func (mb *MemBuffer) locateByTs(lastReadTime time.Time) (pos int) {
	lastReadTs := lastReadTime.UnixNano()
	for pos < len(mb.buf) {
		size, t := readTs(mb.buf, pos)
		if t > lastReadTs {
			return
		}
		pos += size + 4
	}
	return len(mb.buf)
}

func (mb *MemBuffer) String() string {
	return fmt.Sprintf("[%v,%v] bytes:%d", mb.startTime, mb.stopTime, mb.size)
}<|endoftext|>


<commit_before>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/instancewriter"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators)
	load() error
	isRemote() bool
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(vol Volume) bool

	// Export struct details.
	Name() string
	Config() map[string]string
	Logger() logger.Logger

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	// Mount mounts a storage pool if needed, returns true if we caused a new mount, false if already mounted.
	Mount() (bool, error)

	// Unmount unmounts a storage pool if needed, returns true if unmounted, false if was not mounted.
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)
	Validate(config map[string]string) error
	Update(changedConfig map[string]string) error
	ApplyPatch(name string) error

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error
	DeleteVolume(vol Volume, op *operations.Operation) error
	RenameVolume(vol Volume, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error
	GetVolumeUsage(vol Volume) (int64, error)
	SetVolumeQuota(vol Volume, size string, op *operations.Operation) error
	GetVolumeDiskPath(vol Volume) (string, error)

	// MountVolume mounts a storage volume (if not mounted) and increments reference counter.
	MountVolume(vol Volume, op *operations.Operation) error

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType, refresh bool) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error

	// Backup.
	BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error
	CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error)
}
<commit_msg>lxd/storage/drivers/interface: Adds FillVolumeConfig<commit_after>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/instancewriter"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators)
	load() error
	isRemote() bool
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(vol Volume) bool

	// Export struct details.
	Name() string
	Config() map[string]string
	Logger() logger.Logger

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	// Mount mounts a storage pool if needed, returns true if we caused a new mount, false if already mounted.
	Mount() (bool, error)

	// Unmount unmounts a storage pool if needed, returns true if unmounted, false if was not mounted.
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)
	Validate(config map[string]string) error
	Update(changedConfig map[string]string) error
	ApplyPatch(name string) error

	// Volumes.
	FillVolumeConfig(vol Volume) error
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error
	DeleteVolume(vol Volume, op *operations.Operation) error
	RenameVolume(vol Volume, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error
	GetVolumeUsage(vol Volume) (int64, error)
	SetVolumeQuota(vol Volume, size string, op *operations.Operation) error
	GetVolumeDiskPath(vol Volume) (string, error)

	// MountVolume mounts a storage volume (if not mounted) and increments reference counter.
	MountVolume(vol Volume, op *operations.Operation) error

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(vol Volume, keepBlockDev bool, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType, refresh bool) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error

	// Backup.
	BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error
	CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error)
}
<|endoftext|>


<commit_before>package sharings

import (
	"fmt"
	"os"
	"testing"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/stretchr/testify/assert"
)

var testDocType = "io.cozy.tests"
var testDocID = "aydiayda"

var in *instance.Instance
var domainSharer = "domain.sharer"

func createInstance(domain, publicName string) (*instance.Instance, error) {
	var settings couchdb.JSONDoc
	settings.M = make(map[string]interface{})
	settings.M["public_name"] = publicName
	opts := &instance.Options{
		Domain:   domain,
		Settings: settings,
	}
	return instance.Create(opts)
}

func TestSendDataMissingDocType(t *testing.T) {
	docType := "fakedoctype"
	err := couchdb.ResetDB(in, docType)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      "fakeid",
		DocType:    docType,
		Recipients: []*RecipientInfo{},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.Error(t, err)
	assert.Equal(t, "CouchDB(not_found): missing", err.Error())
}

func TestSendDataBadID(t *testing.T) {

	doc := &couchdb.JSONDoc{
		Type: testDocType,
		M:    map[string]interface{}{"test": "tests"},
	}
	doc.SetID(testDocID)
	err := couchdb.CreateNamedDocWithDB(in, doc)
	assert.NoError(t, err)
	defer func() {
		couchdb.DeleteDoc(in, doc)
	}()

	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      "fakeid",
		DocType:    testDocType,
		Recipients: []*RecipientInfo{},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.Error(t, err)
	assert.Equal(t, "CouchDB(not_found): missing", err.Error())
}

func TestSendDataBadRecipient(t *testing.T) {

	doc := &couchdb.JSONDoc{
		Type: testDocType,
		M:    map[string]interface{}{"test": "tests"},
	}
	doc.SetID(testDocID)
	err := couchdb.CreateNamedDocWithDB(in, doc)
	assert.NoError(t, err)
	defer func() {
		couchdb.DeleteDoc(in, doc)
	}()

	rec := &RecipientInfo{
		URL:   "nowhere",
		Token: "inthesky",
	}

	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      testDocID,
		DocType:    testDocType,
		Recipients: []*RecipientInfo{rec},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.NoError(t, err)
}

func TestMain(m *testing.M) {
	config.UseTestFile()

	var err error
	err = jobs.StartSystem()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	_, _ = instance.Destroy(domainSharer)
	in, err = createInstance(domainSharer, "Alice")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.ResetDB(in, testDocType)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.ResetDB(in, consts.Sharings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.DefineIndex(in, mango.IndexOnFields(consts.Sharings, "by-sharing-id", []string{"sharing_id"}))
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	os.Exit(m.Run())
}
<commit_msg>Gometalinter again<commit_after>package sharings

import (
	"fmt"
	"os"
	"testing"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/stretchr/testify/assert"
)

var testDocType = "io.cozy.tests"
var testDocID = "aydiayda"

var in *instance.Instance
var domainSharer = "domain.sharer"

func createInstance(domain, publicName string) (*instance.Instance, error) {
	var settings couchdb.JSONDoc
	settings.M = make(map[string]interface{})
	settings.M["public_name"] = publicName
	opts := &instance.Options{
		Domain:   domain,
		Settings: settings,
	}
	return instance.Create(opts)
}

func TestSendDataMissingDocType(t *testing.T) {
	docType := "fakedoctype"
	err := couchdb.ResetDB(in, docType)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      "fakeid",
		DocType:    docType,
		Recipients: []*RecipientInfo{},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.Error(t, err)
	assert.Equal(t, "CouchDB(not_found): missing", err.Error())
}

func TestSendDataBadID(t *testing.T) {

	doc := &couchdb.JSONDoc{
		Type: testDocType,
		M:    map[string]interface{}{"test": "tests"},
	}
	doc.SetID(testDocID)
	err := couchdb.CreateNamedDocWithDB(in, doc)
	assert.NoError(t, err)
	defer func() {
		couchdb.DeleteDoc(in, doc)
	}()

	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      "fakeid",
		DocType:    testDocType,
		Recipients: []*RecipientInfo{},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.Error(t, err)
	assert.Equal(t, "CouchDB(not_found): missing", err.Error())
}

func TestSendDataBadRecipient(t *testing.T) {

	doc := &couchdb.JSONDoc{
		Type: testDocType,
		M:    map[string]interface{}{"test": "tests"},
	}
	doc.SetID(testDocID)
	err := couchdb.CreateNamedDocWithDB(in, doc)
	assert.NoError(t, err)
	defer func() {
		couchdb.DeleteDoc(in, doc)
	}()

	rec := &RecipientInfo{
		URL:   "nowhere",
		Token: "inthesky",
	}

	msg, err := jobs.NewMessage(jobs.JSONEncoding, SendOptions{
		DocID:      testDocID,
		DocType:    testDocType,
		Recipients: []*RecipientInfo{rec},
	})
	assert.NoError(t, err)

	err = SendData(jobs.NewWorkerContext(domainSharer), msg)
	assert.NoError(t, err)
}

func TestMain(m *testing.M) {
	config.UseTestFile()

	err := jobs.StartSystem()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	_, _ = instance.Destroy(domainSharer)
	in, err = createInstance(domainSharer, "Alice")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.ResetDB(in, testDocType)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.ResetDB(in, consts.Sharings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	err = couchdb.DefineIndex(in, mango.IndexOnFields(consts.Sharings, "by-sharing-id", []string{"sharing_id"}))
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	os.Exit(m.Run())
}
<|endoftext|>


<commit_before>package notifiers

import (
	"strconv"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pagerduty",
		Name:        "PagerDuty",
		Description: "Sends notifications to PagerDuty",
		Factory:     NewPagerdutyNotifier,
		OptionsTemplate: `

PagerDuty settings

        Integration Key
        <input type="text" required class="gf-form-input max-width-22" ng-model="ctrl.model.settings.integrationKey" placeholder="Pagerduty integeration Key"></input>

        <gf-form-switch
           class="gf-form"
           label="Auto resolve incidents"
           label-class="width-14"
           checked="ctrl.model.settings.autoResolve"
           tooltip="Resolve incidents in pagerduty once the alert goes back to ok.">
        </gf-form-switch>

    `,
	})
}

var (
	pagerdutyEventApiUrl string = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
)

func NewPagerdutyNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	autoResolve := model.Settings.Get("autoResolve").MustBool(true)
	key := model.Settings.Get("integrationKey").MustString()
	if key == "" {
		return nil, alerting.ValidationError{Reason: "Could not find integration key property in settings"}
	}

	return &PagerdutyNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Key:          key,
		AutoResolve:  autoResolve,
		log:          log.New("alerting.notifier.pagerduty"),
	}, nil
}

type PagerdutyNotifier struct {
	NotifierBase
	Key         string
	AutoResolve bool
	log         log.Logger
}

func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
	metrics.M_Alerting_Notification_Sent_PagerDuty.Inc(1)

	if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
		this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve)
		return nil
	}

	eventType := "trigger"
	if evalContext.Rule.State == m.AlertStateOK {
		eventType = "resolve"
	}
	customData := make([]map[string]interface{}, 0)
	fieldLimitCount := 4
	for index, evt := range evalContext.EvalMatches {
		customData = append(customData, map[string]interface{}{
			evt.Metric: evt.Value,
		})
		if index > fieldLimitCount {
			break
		}
	}

	this.log.Info("Notifying Pagerduty", "event_type", eventType)

	bodyJSON := simplejson.New()
	bodyJSON.Set("service_key", this.Key)
	bodyJSON.Set("description", evalContext.Rule.Name+" - "+evalContext.Rule.Message)
	bodyJSON.Set("client", "Grafana")
	bodyJSON.Set("details", customData)
	bodyJSON.Set("event_type", eventType)
	bodyJSON.Set("incident_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10))

	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}
	bodyJSON.Set("client_url", ruleUrl)

	if evalContext.ImagePublicUrl != "" {
		contexts := make([]interface{}, 1)
		imageJSON := simplejson.New()
		imageJSON.Set("type", "image")
		imageJSON.Set("src", evalContext.ImagePublicUrl)
		contexts[0] = imageJSON
		bodyJSON.Set("contexts", contexts)
	}

	body, _ := bodyJSON.MarshalJSON()

	cmd := &m.SendWebhookSync{
		Url:        pagerdutyEventApiUrl,
		Body:       string(body),
		HttpMethod: "POST",
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body))
		return err
	}

	return nil
}
<commit_msg>Make details more clean in PD description<commit_after>package notifiers

import (
	"strconv"

	"fmt"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pagerduty",
		Name:        "PagerDuty",
		Description: "Sends notifications to PagerDuty",
		Factory:     NewPagerdutyNotifier,
		OptionsTemplate: `

PagerDuty settings

        Integration Key
        <input type="text" required class="gf-form-input max-width-22" ng-model="ctrl.model.settings.integrationKey" placeholder="Pagerduty integeration Key"></input>

        <gf-form-switch
           class="gf-form"
           label="Auto resolve incidents"
           label-class="width-14"
           checked="ctrl.model.settings.autoResolve"
           tooltip="Resolve incidents in pagerduty once the alert goes back to ok.">
        </gf-form-switch>

    `,
	})
}

var (
	pagerdutyEventApiUrl string = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
)

func NewPagerdutyNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	autoResolve := model.Settings.Get("autoResolve").MustBool(true)
	key := model.Settings.Get("integrationKey").MustString()
	if key == "" {
		return nil, alerting.ValidationError{Reason: "Could not find integration key property in settings"}
	}

	return &PagerdutyNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Key:          key,
		AutoResolve:  autoResolve,
		log:          log.New("alerting.notifier.pagerduty"),
	}, nil
}

type PagerdutyNotifier struct {
	NotifierBase
	Key         string
	AutoResolve bool
	log         log.Logger
}

func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
	metrics.M_Alerting_Notification_Sent_PagerDuty.Inc(1)

	if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
		this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve)
		return nil
	}

	eventType := "trigger"
	if evalContext.Rule.State == m.AlertStateOK {
		eventType = "resolve"
	}
	customData := "Triggered metrics:\n\n"
	for _, evt := range evalContext.EvalMatches {
		customData = customData + fmt.Sprintf("%s: %v\n", evt.Metric, evt.Value)
	}

	this.log.Info("Notifying Pagerduty", "event_type", eventType)

	bodyJSON := simplejson.New()
	bodyJSON.Set("service_key", this.Key)
	bodyJSON.Set("description", evalContext.Rule.Name+" - "+evalContext.Rule.Message)
	bodyJSON.Set("client", "Grafana")
	bodyJSON.Set("details", customData)
	bodyJSON.Set("event_type", eventType)
	bodyJSON.Set("incident_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10))

	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}
	bodyJSON.Set("client_url", ruleUrl)

	if evalContext.ImagePublicUrl != "" {
		contexts := make([]interface{}, 1)
		imageJSON := simplejson.New()
		imageJSON.Set("type", "image")
		imageJSON.Set("src", evalContext.ImagePublicUrl)
		contexts[0] = imageJSON
		bodyJSON.Set("contexts", contexts)
	}

	body, _ := bodyJSON.MarshalJSON()

	cmd := &m.SendWebhookSync{
		Url:        pagerdutyEventApiUrl,
		Body:       string(body),
		HttpMethod: "POST",
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body))
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package dashboards

import (
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/guardian"
	"github.com/grafana/grafana/pkg/util"
)

// DashboardService service for operating on dashboards
type DashboardService interface {
	SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
	ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
}

// DashboardProvisioningService service for operating on provisioned dashboards
type DashboardProvisioningService interface {
	SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
	SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
	GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
}

// NewService factory for creating a new dashboard service
var NewService = func() DashboardService {
	return &dashboardServiceImpl{
		log: log.New("dashboard-service"),
	}
}

// NewProvisioningService factory for creating a new dashboard provisioning service
var NewProvisioningService = func() DashboardProvisioningService {
	return &dashboardServiceImpl{}
}

type SaveDashboardDTO struct {
	OrgId     int64
	UpdatedAt time.Time
	User      *models.SignedInUser
	Message   string
	Overwrite bool
	Dashboard *models.Dashboard
}

type dashboardServiceImpl struct {
	orgId int64
	user  *models.SignedInUser
	log   log.Logger
}

func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
	cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
	err := bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
	dash := dto.Dashboard

	dash.Title = strings.TrimSpace(dash.Title)
	dash.Data.Set("title", dash.Title)
	dash.SetUid(strings.TrimSpace(dash.Uid))

	if dash.Title == "" {
		return nil, models.ErrDashboardTitleEmpty
	}

	if dash.IsFolder && dash.FolderId > 0 {
		return nil, models.ErrDashboardFolderCannotHaveParent
	}

	if dash.IsFolder && strings.EqualFold(dash.Title, models.RootFolderName) {
		return nil, models.ErrDashboardFolderNameExists
	}

	if !util.IsValidShortUid(dash.Uid) {
		return nil, models.ErrDashboardInvalidUid
	} else if len(dash.Uid) > 40 {
		return nil, models.ErrDashboardUidToLong
	}

	if validateAlerts {
		validateAlertsCmd := models.ValidateDashboardAlertsCommand{
			OrgId:     dto.OrgId,
			Dashboard: dash,
			User:      dto.User,
		}

		if err := bus.Dispatch(&validateAlertsCmd); err != nil {
			return nil, err
		}
	}

	validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
		OrgId:     dto.OrgId,
		Dashboard: dash,
		Overwrite: dto.Overwrite,
	}

	if err := bus.Dispatch(&validateBeforeSaveCmd); err != nil {
		return nil, err
	}

	if validateBeforeSaveCmd.Result.IsParentFolderChanged {
		folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
		if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
			if err != nil {
				return nil, err
			}
			return nil, models.ErrDashboardUpdateAccessDenied
		}
	}

	if validateProvisionedDashboard {
		isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
		err := bus.Dispatch(isDashboardProvisioned)

		if err != nil {
			return nil, err
		}

		if isDashboardProvisioned.Result {
			return nil, models.ErrDashboardCannotSaveProvisionedDashboard
		}
	}

	guard := guardian.New(dash.GetDashboardIdForSavePermissionCheck(), dto.OrgId, dto.User)
	if canSave, err := guard.CanSave(); err != nil || !canSave {
		if err != nil {
			return nil, err
		}
		return nil, models.ErrDashboardUpdateAccessDenied
	}

	cmd := &models.SaveDashboardCommand{
		Dashboard: dash.Data,
		Message:   dto.Message,
		OrgId:     dto.OrgId,
		Overwrite: dto.Overwrite,
		UserId:    dto.User.UserId,
		FolderId:  dash.FolderId,
		IsFolder:  dash.IsFolder,
		PluginId:  dash.PluginId,
	}

	if !dto.UpdatedAt.IsZero() {
		cmd.UpdatedAt = dto.UpdatedAt
	}

	return cmd, nil
}

func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
	alertCmd := models.UpdateDashboardAlertsCommand{
		OrgId:     dto.OrgId,
		Dashboard: cmd.Result,
		User:      dto.User,
	}

	if err := bus.Dispatch(&alertCmd); err != nil {
		return err
	}

	return nil
}

func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
	dto.User = &models.SignedInUser{
		UserId:  0,
		OrgRole: models.ROLE_ADMIN,
		OrgId:   dto.OrgId,
	}

	cmd, err := dr.buildSaveDashboardCommand(dto, true, false)
	if err != nil {
		return nil, err
	}

	saveCmd := &models.SaveProvisionedDashboardCommand{
		DashboardCmd:          cmd,
		DashboardProvisioning: provisioning,
	}

	// dashboard
	err = bus.Dispatch(saveCmd)
	if err != nil {
		return nil, err
	}

	//alerts
	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	dto.User = &models.SignedInUser{
		UserId:  0,
		OrgRole: models.ROLE_ADMIN,
	}
	cmd, err := dr.buildSaveDashboardCommand(dto, false, false)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	cmd, err := dr.buildSaveDashboardCommand(dto, true, true)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	cmd, err := dr.buildSaveDashboardCommand(dto, false, true)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

type FakeDashboardService struct {
	SaveDashboardResult *models.Dashboard
	SaveDashboardError  error
	SavedDashboards     []*SaveDashboardDTO
}

func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	s.SavedDashboards = append(s.SavedDashboards, dto)

	if s.SaveDashboardResult == nil && s.SaveDashboardError == nil {
		s.SaveDashboardResult = dto.Dashboard
	}

	return s.SaveDashboardResult, s.SaveDashboardError
}

func (s *FakeDashboardService) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	return s.SaveDashboard(dto)
}

func MockDashboardService(mock *FakeDashboardService) {
	NewService = func() DashboardService {
		return mock
	}
}
<commit_msg>pkg/services/dashboards/dashboard_service.go: simplify return<commit_after>package dashboards

import (
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/guardian"
	"github.com/grafana/grafana/pkg/util"
)

// DashboardService service for operating on dashboards
type DashboardService interface {
	SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
	ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
}

// DashboardProvisioningService service for operating on provisioned dashboards
type DashboardProvisioningService interface {
	SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
	SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
	GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
}

// NewService factory for creating a new dashboard service
var NewService = func() DashboardService {
	return &dashboardServiceImpl{
		log: log.New("dashboard-service"),
	}
}

// NewProvisioningService factory for creating a new dashboard provisioning service
var NewProvisioningService = func() DashboardProvisioningService {
	return &dashboardServiceImpl{}
}

type SaveDashboardDTO struct {
	OrgId     int64
	UpdatedAt time.Time
	User      *models.SignedInUser
	Message   string
	Overwrite bool
	Dashboard *models.Dashboard
}

type dashboardServiceImpl struct {
	orgId int64
	user  *models.SignedInUser
	log   log.Logger
}

func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
	cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
	err := bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
	dash := dto.Dashboard

	dash.Title = strings.TrimSpace(dash.Title)
	dash.Data.Set("title", dash.Title)
	dash.SetUid(strings.TrimSpace(dash.Uid))

	if dash.Title == "" {
		return nil, models.ErrDashboardTitleEmpty
	}

	if dash.IsFolder && dash.FolderId > 0 {
		return nil, models.ErrDashboardFolderCannotHaveParent
	}

	if dash.IsFolder && strings.EqualFold(dash.Title, models.RootFolderName) {
		return nil, models.ErrDashboardFolderNameExists
	}

	if !util.IsValidShortUid(dash.Uid) {
		return nil, models.ErrDashboardInvalidUid
	} else if len(dash.Uid) > 40 {
		return nil, models.ErrDashboardUidToLong
	}

	if validateAlerts {
		validateAlertsCmd := models.ValidateDashboardAlertsCommand{
			OrgId:     dto.OrgId,
			Dashboard: dash,
			User:      dto.User,
		}

		if err := bus.Dispatch(&validateAlertsCmd); err != nil {
			return nil, err
		}
	}

	validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
		OrgId:     dto.OrgId,
		Dashboard: dash,
		Overwrite: dto.Overwrite,
	}

	if err := bus.Dispatch(&validateBeforeSaveCmd); err != nil {
		return nil, err
	}

	if validateBeforeSaveCmd.Result.IsParentFolderChanged {
		folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
		if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
			if err != nil {
				return nil, err
			}
			return nil, models.ErrDashboardUpdateAccessDenied
		}
	}

	if validateProvisionedDashboard {
		isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
		err := bus.Dispatch(isDashboardProvisioned)

		if err != nil {
			return nil, err
		}

		if isDashboardProvisioned.Result {
			return nil, models.ErrDashboardCannotSaveProvisionedDashboard
		}
	}

	guard := guardian.New(dash.GetDashboardIdForSavePermissionCheck(), dto.OrgId, dto.User)
	if canSave, err := guard.CanSave(); err != nil || !canSave {
		if err != nil {
			return nil, err
		}
		return nil, models.ErrDashboardUpdateAccessDenied
	}

	cmd := &models.SaveDashboardCommand{
		Dashboard: dash.Data,
		Message:   dto.Message,
		OrgId:     dto.OrgId,
		Overwrite: dto.Overwrite,
		UserId:    dto.User.UserId,
		FolderId:  dash.FolderId,
		IsFolder:  dash.IsFolder,
		PluginId:  dash.PluginId,
	}

	if !dto.UpdatedAt.IsZero() {
		cmd.UpdatedAt = dto.UpdatedAt
	}

	return cmd, nil
}

func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
	alertCmd := models.UpdateDashboardAlertsCommand{
		OrgId:     dto.OrgId,
		Dashboard: cmd.Result,
		User:      dto.User,
	}

	return bus.Dispatch(&alertCmd)
}

func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
	dto.User = &models.SignedInUser{
		UserId:  0,
		OrgRole: models.ROLE_ADMIN,
		OrgId:   dto.OrgId,
	}

	cmd, err := dr.buildSaveDashboardCommand(dto, true, false)
	if err != nil {
		return nil, err
	}

	saveCmd := &models.SaveProvisionedDashboardCommand{
		DashboardCmd:          cmd,
		DashboardProvisioning: provisioning,
	}

	// dashboard
	err = bus.Dispatch(saveCmd)
	if err != nil {
		return nil, err
	}

	//alerts
	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	dto.User = &models.SignedInUser{
		UserId:  0,
		OrgRole: models.ROLE_ADMIN,
	}
	cmd, err := dr.buildSaveDashboardCommand(dto, false, false)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	cmd, err := dr.buildSaveDashboardCommand(dto, true, true)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	err = dr.updateAlerting(cmd, dto)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	cmd, err := dr.buildSaveDashboardCommand(dto, false, true)
	if err != nil {
		return nil, err
	}

	err = bus.Dispatch(cmd)
	if err != nil {
		return nil, err
	}

	return cmd.Result, nil
}

type FakeDashboardService struct {
	SaveDashboardResult *models.Dashboard
	SaveDashboardError  error
	SavedDashboards     []*SaveDashboardDTO
}

func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	s.SavedDashboards = append(s.SavedDashboards, dto)

	if s.SaveDashboardResult == nil && s.SaveDashboardError == nil {
		s.SaveDashboardResult = dto.Dashboard
	}

	return s.SaveDashboardResult, s.SaveDashboardError
}

func (s *FakeDashboardService) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
	return s.SaveDashboard(dto)
}

func MockDashboardService(mock *FakeDashboardService) {
	NewService = func() DashboardService {
		return mock
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package flowcontrol

import (
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apiserver/pkg/server/mux"
)

const (
	queryIncludeRequestDetails = "includeRequestDetails"
)

func (cfgCtlr *configController) Install(c *mux.PathRecorderMux) {
	// TODO(yue9944882): handle "Accept" header properly
	// debugging dumps a CSV content for three levels of granularity
	// 1. row per priority-level
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_priority_levels", cfgCtlr.dumpPriorityLevels)
	// 2. row per queue
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_queues", cfgCtlr.dumpQueues)
	// 3. row per request
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_requests", cfgCtlr.dumpRequests)
}

func (cfgCtlr *configController) dumpPriorityLevels(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()
	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	columnHeaders := []string{
		"PriorityLevelName", // 1
		"ActiveQueues",      // 2
		"IsIdle",            // 3
		"IsQuiescing",       // 4
		"WaitingRequests",   // 5
		"ExecutingRequests", // 6
	}
	tabPrint(tabWriter, rowForHeaders(columnHeaders))
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			tabPrint(tabWriter, row(
				plState.pl.Name, // 1
				"<none>",        // 2
				"<none>",        // 3
				"<none>",        // 4
				"<none>",        // 5
				"<none>",        // 6
			))
			endLine(tabWriter)
			continue
		}
		queueSetDigest := plState.queues.Dump(false)
		activeQueueNum := 0
		for _, q := range queueSetDigest.Queues {
			if len(q.Requests) > 0 {
				activeQueueNum++
			}
		}

		tabPrint(tabWriter, rowForPriorityLevel(
			plState.pl.Name,          // 1
			activeQueueNum,           // 2
			plState.queues.IsIdle(),  // 3
			plState.quiescing,        // 4
			queueSetDigest.Waiting,   // 5
			queueSetDigest.Executing, // 6
		))
		endLine(tabWriter)
	}
	runtime.HandleError(tabWriter.Flush())
}

func (cfgCtlr *configController) dumpQueues(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()
	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	columnHeaders := []string{
		"PriorityLevelName", // 1
		"Index",             // 2
		"PendingRequests",   // 3
		"ExecutingRequests", // 4
		"VirtualStart",      // 5
	}
	tabPrint(tabWriter, rowForHeaders(columnHeaders))
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			tabPrint(tabWriter, row(
				plState.pl.Name, // 1
				"<none>",        // 2
				"<none>",        // 3
				"<none>",        // 4
				"<none>",        // 5
			))
			endLine(tabWriter)
			continue
		}
		queueSetDigest := plState.queues.Dump(false)
		for i, q := range queueSetDigest.Queues {
			tabPrint(tabWriter, rowForQueue(
				plState.pl.Name,     // 1
				i,                   // 2
				len(q.Requests),     // 3
				q.ExecutingRequests, // 4
				q.VirtualStart,      // 5
			))
			endLine(tabWriter)
		}
	}
	runtime.HandleError(tabWriter.Flush())
}

func (cfgCtlr *configController) dumpRequests(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()

	includeRequestDetails := len(r.URL.Query().Get(queryIncludeRequestDetails)) > 0

	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	tabPrint(tabWriter, rowForHeaders([]string{
		"PriorityLevelName",   // 1
		"FlowSchemaName",      // 2
		"QueueIndex",          // 3
		"RequestIndexInQueue", // 4
		"FlowDistingsher",     // 5
		"ArriveTime",          // 6
	}))
	if includeRequestDetails {
		continueLine(tabWriter)
		tabPrint(tabWriter, rowForHeaders([]string{
			"UserName",    // 7
			"Verb",        // 8
			"APIPath",     // 9
			"Namespace",   // 10
			"Name",        // 11
			"APIVersion",  // 12
			"Resource",    // 13
			"SubResource", // 14
		}))
	}
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			tabPrint(tabWriter, row(
				plState.pl.Name, // 1
				"<none>",        // 2
				"<none>",        // 3
				"<none>",        // 4
				"<none>",        // 5
				"<none>",        // 6
			))
			if includeRequestDetails {
				continueLine(tabWriter)
				tabPrint(tabWriter, row(
					"<none>", // 7
					"<none>", // 8
					"<none>", // 9
					"<none>", // 10
					"<none>", // 11
					"<none>", // 12
					"<none>", // 13
					"<none>", // 14
				))
			}
			endLine(tabWriter)
			continue
		}
		queueSetDigest := plState.queues.Dump(includeRequestDetails)
		for iq, q := range queueSetDigest.Queues {
			for ir, r := range q.Requests {
				tabPrint(tabWriter, rowForRequest(
					plState.pl.Name,     // 1
					r.MatchedFlowSchema, // 2
					iq,                  // 3
					ir,                  // 4
					r.FlowDistinguisher, // 5
					r.ArriveTime,        // 6
				))
				if includeRequestDetails {
					continueLine(tabWriter)
					tabPrint(tabWriter, rowForRequestDetails(
						r.UserName,              // 7
						r.RequestInfo.Verb,      // 8
						r.RequestInfo.Path,      // 9
						r.RequestInfo.Namespace, // 10
						r.RequestInfo.Name,      // 11
						schema.GroupVersion{
							Group:   r.RequestInfo.APIGroup,
							Version: r.RequestInfo.APIVersion,
						}.String(), // 12
						r.RequestInfo.Resource,    // 13
						r.RequestInfo.Subresource, // 14
					))
				}
				endLine(tabWriter)
			}
		}
	}
	runtime.HandleError(tabWriter.Flush())
}

func tabPrint(w io.Writer, row string) {
	_, err := fmt.Fprint(w, row)
	runtime.HandleError(err)
}

func continueLine(w io.Writer) {
	_, err := fmt.Fprint(w, ",\t")
	runtime.HandleError(err)
}
func endLine(w io.Writer) {
	_, err := fmt.Fprint(w, "\n")
	runtime.HandleError(err)
}

func rowForHeaders(headers []string) string {
	return row(headers...)
}

func rowForPriorityLevel(plName string, activeQueues int, isIdle, isQuiescing bool, waitingRequests, executingRequests int) string {
	return row(
		plName,
		strconv.Itoa(activeQueues),
		strconv.FormatBool(isIdle),
		strconv.FormatBool(isQuiescing),
		strconv.Itoa(waitingRequests),
		strconv.Itoa(executingRequests),
	)
}

func rowForQueue(plName string, index, waitingRequests, executingRequests int, virtualStart float64) string {
	return row(
		plName,
		strconv.Itoa(index),
		strconv.Itoa(waitingRequests),
		strconv.Itoa(executingRequests),
		fmt.Sprintf("%.4f", virtualStart),
	)
}

func rowForRequest(plName, fsName string, queueIndex, requestIndex int, flowDistinguisher string, arriveTime time.Time) string {
	return row(
		plName,
		fsName,
		strconv.Itoa(queueIndex),
		strconv.Itoa(requestIndex),
		flowDistinguisher,
		arriveTime.UTC().Format(time.RFC3339Nano),
	)
}

func rowForRequestDetails(username, verb, path, namespace, name, apiVersion, resource, subResource string) string {
	return row(
		username,
		verb,
		path,
		namespace,
		name,
		apiVersion,
		resource,
		subResource,
	)
}

func row(columns ...string) string {
	return strings.Join(columns, ",\t")
}
<commit_msg>Remove phantoms from dump_requests output<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package flowcontrol

import (
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apiserver/pkg/server/mux"
)

const (
	queryIncludeRequestDetails = "includeRequestDetails"
)

func (cfgCtlr *configController) Install(c *mux.PathRecorderMux) {
	// TODO(yue9944882): handle "Accept" header properly
	// debugging dumps a CSV content for three levels of granularity
	// 1. row per priority-level
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_priority_levels", cfgCtlr.dumpPriorityLevels)
	// 2. row per queue
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_queues", cfgCtlr.dumpQueues)
	// 3. row per request
	c.UnlistedHandleFunc("/debug/api_priority_and_fairness/dump_requests", cfgCtlr.dumpRequests)
}

func (cfgCtlr *configController) dumpPriorityLevels(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()
	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	columnHeaders := []string{
		"PriorityLevelName", // 1
		"ActiveQueues",      // 2
		"IsIdle",            // 3
		"IsQuiescing",       // 4
		"WaitingRequests",   // 5
		"ExecutingRequests", // 6
	}
	tabPrint(tabWriter, rowForHeaders(columnHeaders))
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			tabPrint(tabWriter, row(
				plState.pl.Name, // 1
				"<none>",        // 2
				"<none>",        // 3
				"<none>",        // 4
				"<none>",        // 5
				"<none>",        // 6
			))
			endLine(tabWriter)
			continue
		}
		queueSetDigest := plState.queues.Dump(false)
		activeQueueNum := 0
		for _, q := range queueSetDigest.Queues {
			if len(q.Requests) > 0 {
				activeQueueNum++
			}
		}

		tabPrint(tabWriter, rowForPriorityLevel(
			plState.pl.Name,          // 1
			activeQueueNum,           // 2
			plState.queues.IsIdle(),  // 3
			plState.quiescing,        // 4
			queueSetDigest.Waiting,   // 5
			queueSetDigest.Executing, // 6
		))
		endLine(tabWriter)
	}
	runtime.HandleError(tabWriter.Flush())
}

func (cfgCtlr *configController) dumpQueues(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()
	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	columnHeaders := []string{
		"PriorityLevelName", // 1
		"Index",             // 2
		"PendingRequests",   // 3
		"ExecutingRequests", // 4
		"VirtualStart",      // 5
	}
	tabPrint(tabWriter, rowForHeaders(columnHeaders))
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			tabPrint(tabWriter, row(
				plState.pl.Name, // 1
				"<none>",        // 2
				"<none>",        // 3
				"<none>",        // 4
				"<none>",        // 5
			))
			endLine(tabWriter)
			continue
		}
		queueSetDigest := plState.queues.Dump(false)
		for i, q := range queueSetDigest.Queues {
			tabPrint(tabWriter, rowForQueue(
				plState.pl.Name,     // 1
				i,                   // 2
				len(q.Requests),     // 3
				q.ExecutingRequests, // 4
				q.VirtualStart,      // 5
			))
			endLine(tabWriter)
		}
	}
	runtime.HandleError(tabWriter.Flush())
}

func (cfgCtlr *configController) dumpRequests(w http.ResponseWriter, r *http.Request) {
	cfgCtlr.lock.Lock()
	defer cfgCtlr.lock.Unlock()

	includeRequestDetails := len(r.URL.Query().Get(queryIncludeRequestDetails)) > 0

	tabWriter := tabwriter.NewWriter(w, 8, 0, 1, ' ', 0)
	tabPrint(tabWriter, rowForHeaders([]string{
		"PriorityLevelName",   // 1
		"FlowSchemaName",      // 2
		"QueueIndex",          // 3
		"RequestIndexInQueue", // 4
		"FlowDistingsher",     // 5
		"ArriveTime",          // 6
	}))
	if includeRequestDetails {
		continueLine(tabWriter)
		tabPrint(tabWriter, rowForHeaders([]string{
			"UserName",    // 7
			"Verb",        // 8
			"APIPath",     // 9
			"Namespace",   // 10
			"Name",        // 11
			"APIVersion",  // 12
			"Resource",    // 13
			"SubResource", // 14
		}))
	}
	endLine(tabWriter)
	for _, plState := range cfgCtlr.priorityLevelStates {
		if plState.queues == nil {
			continue
		}
		queueSetDigest := plState.queues.Dump(includeRequestDetails)
		for iq, q := range queueSetDigest.Queues {
			for ir, r := range q.Requests {
				tabPrint(tabWriter, rowForRequest(
					plState.pl.Name,     // 1
					r.MatchedFlowSchema, // 2
					iq,                  // 3
					ir,                  // 4
					r.FlowDistinguisher, // 5
					r.ArriveTime,        // 6
				))
				if includeRequestDetails {
					continueLine(tabWriter)
					tabPrint(tabWriter, rowForRequestDetails(
						r.UserName,              // 7
						r.RequestInfo.Verb,      // 8
						r.RequestInfo.Path,      // 9
						r.RequestInfo.Namespace, // 10
						r.RequestInfo.Name,      // 11
						schema.GroupVersion{
							Group:   r.RequestInfo.APIGroup,
							Version: r.RequestInfo.APIVersion,
						}.String(), // 12
						r.RequestInfo.Resource,    // 13
						r.RequestInfo.Subresource, // 14
					))
				}
				endLine(tabWriter)
			}
		}
	}
	runtime.HandleError(tabWriter.Flush())
}

func tabPrint(w io.Writer, row string) {
	_, err := fmt.Fprint(w, row)
	runtime.HandleError(err)
}

func continueLine(w io.Writer) {
	_, err := fmt.Fprint(w, ",\t")
	runtime.HandleError(err)
}
func endLine(w io.Writer) {
	_, err := fmt.Fprint(w, "\n")
	runtime.HandleError(err)
}

func rowForHeaders(headers []string) string {
	return row(headers...)
}

func rowForPriorityLevel(plName string, activeQueues int, isIdle, isQuiescing bool, waitingRequests, executingRequests int) string {
	return row(
		plName,
		strconv.Itoa(activeQueues),
		strconv.FormatBool(isIdle),
		strconv.FormatBool(isQuiescing),
		strconv.Itoa(waitingRequests),
		strconv.Itoa(executingRequests),
	)
}

func rowForQueue(plName string, index, waitingRequests, executingRequests int, virtualStart float64) string {
	return row(
		plName,
		strconv.Itoa(index),
		strconv.Itoa(waitingRequests),
		strconv.Itoa(executingRequests),
		fmt.Sprintf("%.4f", virtualStart),
	)
}

func rowForRequest(plName, fsName string, queueIndex, requestIndex int, flowDistinguisher string, arriveTime time.Time) string {
	return row(
		plName,
		fsName,
		strconv.Itoa(queueIndex),
		strconv.Itoa(requestIndex),
		flowDistinguisher,
		arriveTime.UTC().Format(time.RFC3339Nano),
	)
}

func rowForRequestDetails(username, verb, path, namespace, name, apiVersion, resource, subResource string) string {
	return row(
		username,
		verb,
		path,
		namespace,
		name,
		apiVersion,
		resource,
		subResource,
	)
}

func row(columns ...string) string {
	return strings.Join(columns, ",\t")
}
<|endoftext|>


<commit_before>package hostdb

// hdbPersist defines what HostDB data persists across sessions.
type hdbPersist struct {
	// TODO: save hosts
}

// save saves the hostdb persistence data to disk.
func (hdb *HostDB) save() error {
	return nil
}

// load loads the hostdb persistence data from disk.
func (hdb *HostDB) load() error {
	return nil
}
<commit_msg>real hostdb persistence<commit_after>package hostdb

// hdbPersist defines what HostDB data persists across sessions.
type hdbPersist struct {
	AllHosts    []hostEntry
	ActiveHosts []hostEntry
}

// save saves the hostdb persistence data to disk.
func (hdb *HostDB) save() error {
	var data hdbPersist
	for _, entry := range hdb.allHosts {
		data.AllHosts = append(data.AllHosts, *entry)
	}
	for _, node := range hdb.activeHosts {
		data.ActiveHosts = append(data.ActiveHosts, *node.hostEntry)
	}
	return hdb.persist.save(data)
}

// load loads the hostdb persistence data from disk.
func (hdb *HostDB) load() error {
	var data hdbPersist
	err := hdb.persist.load(&data)
	if err != nil {
		return err
	}
	for _, entry := range data.AllHosts {
		hdb.allHosts[entry.NetAddress] = &entry
	}
	for _, entry := range data.ActiveHosts {
		hdb.insertNode(&entry)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

// beehive's SpaceAPI module.
package twitterbee

import (
	"github.com/muesli/beehive/modules"
	"github.com/ChimeraCoder/anaconda"
)

type TwitterBee struct {
	modules.Module

	consumer_key string
	consumer_secret string
	access_token string
	access_token_secret string

	twitter_api *anaconda.TwitterApi

	evchan chan modules.Event
}

func (mod *TwitterBee) Action(action modules.Action) []modules.Placeholder {
	outs := []modules.Placeholder{}
	switch action.Name {
	case "tweet":

		ev := modules.Event{
			Bee:  mod.Name(),
			Name: "call_finished",
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name:  "success",
					Type:  "bool",
					Value: true,
				},
			},
		}
		mod.evchan <- ev

	default:
		panic("Unknown action triggered in " +mod.Name()+": "+action.Name)
	}

	return outs
}

func (mod *TwitterBee) Run(eventChan chan modules.Event) {
	mod.evchan = eventChan

	anaconda.SetConsumerKey(mod.consumer_key)
	anaconda.SetConsumerSecret(mod.consumer_secret)
	mod.twitter_api = anaconda.NewTwitterApi(mod.access_token, mod.access_token_secret)
}
<commit_msg>Check if Twitter Credentials are working and populate Mentions<commit_after>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

// beehive's SpaceAPI module.
package twitterbee

import (
	"github.com/muesli/beehive/modules"
	"github.com/ChimeraCoder/anaconda"
	"net/url"
)

type TwitterBee struct {
	modules.Module

	consumer_key string
	consumer_secret string
	access_token string
	access_token_secret string

	twitter_api *anaconda.TwitterApi
	twitter_mentions []anaconda.Tweet

	evchan chan modules.Event
}

func (mod *TwitterBee) Action(action modules.Action) []modules.Placeholder {
	outs := []modules.Placeholder{}
	switch action.Name {
	case "tweet":

		ev := modules.Event{
			Bee:  mod.Name(),
			Name: "call_finished",
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name:  "success",
					Type:  "bool",
					Value: true,
				},
			},
		}
		mod.evchan <- ev

	default:
		panic("Unknown action triggered in " +mod.Name()+": "+action.Name)
	}

	return outs
}

func (mod *TwitterBee) Run(eventChan chan modules.Event) {
	mod.evchan = eventChan

	anaconda.SetConsumerKey(mod.consumer_key)
	anaconda.SetConsumerSecret(mod.consumer_secret)
	mod.twitter_api = anaconda.NewTwitterApi(mod.access_token, mod.access_token_secret)

	// Test the credentials on startup
	_, err := mod.twitter_api.VerifyCredentials()
	if err != nil {
		panic("The credentials you provided in your conf are invalid. Failing :(")
	}

	// populate mentions initially
	v := url.Values{}
	v.Set("count", "30")
	mentions, err := mod.twitter_api.GetMentionsTimeline(v)
	if err != nil {
		panic("Could not populate Twitter mentions")
	}
	mod.twitter_mentions = mentions
}
<|endoftext|>


<commit_before>package postgresql

import (
	"fmt"
	"testing"

	"github.com/docker/go-connections/nat"
	"github.com/stretchr/testify/require"
	"github.com/testcontainers/testcontainers-go/wait"

	"github.com/influxdata/telegraf/testutil"
)

const servicePort = "5432"

func launchTestContainer(t *testing.T) *testutil.Container {
	container := testutil.Container{
		Image:        "postgres:alpine",
		ExposedPorts: []string{servicePort},
		Env: map[string]string{
			"POSTGRES_HOST_AUTH_METHOD": "trust",
		},
		WaitingFor: wait.ForAll(
			wait.ForLog("database system is ready to accept connections"),
			wait.ForListeningPort(nat.Port(servicePort)),
		),
	}

	err := container.Start()
	require.NoError(t, err, "failed to start container")

	return &container
}

func TestPostgresqlGeneratesMetricsIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
			IsPgBouncer: false,
		},
		Databases: []string{"postgres"},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	intMetrics := []string{
		"xact_commit",
		"xact_rollback",
		"blks_read",
		"blks_hit",
		"tup_returned",
		"tup_fetched",
		"tup_inserted",
		"tup_updated",
		"tup_deleted",
		"conflicts",
		"temp_files",
		"temp_bytes",
		"deadlocks",
		"buffers_alloc",
		"buffers_backend",
		"buffers_backend_fsync",
		"buffers_checkpoint",
		"buffers_clean",
		"checkpoints_req",
		"checkpoints_timed",
		"maxwritten_clean",
		"datid",
		"numbackends",
	}

	int32Metrics := []string{}

	floatMetrics := []string{
		"blk_read_time",
		"blk_write_time",
		"checkpoint_write_time",
		"checkpoint_sync_time",
	}

	stringMetrics := []string{
		"datname",
	}

	metricsCounted := 0

	for _, metric := range intMetrics {
		require.True(t, acc.HasInt64Field("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range int32Metrics {
		require.True(t, acc.HasInt32Field("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range floatMetrics {
		require.True(t, acc.HasFloatField("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range stringMetrics {
		require.True(t, acc.HasStringField("postgresql", metric))
		metricsCounted++
	}

	require.True(t, metricsCounted > 0)
	require.Equal(t, len(floatMetrics)+len(intMetrics)+len(int32Metrics)+len(stringMetrics), metricsCounted)
}

func TestPostgresqlTagsMetricsWithDatabaseNameIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		Databases: []string{"postgres"},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	point, ok := acc.Get("postgresql")
	require.True(t, ok)

	require.Equal(t, "postgres", point.Tags["db"])
}

func TestPostgresqlDefaultsToAllDatabasesIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var found bool

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "postgres" {
				found = true
				break
			}
		}
	}

	require.True(t, found)
}

func TestPostgresqlIgnoresUnwantedColumnsIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	for col := range p.IgnoredColumns() {
		require.False(t, acc.HasMeasurement(col))
	}
}

func TestPostgresqlDatabaseWhitelistTestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		Databases: []string{"template0"},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var foundTemplate0 = false
	var foundTemplate1 = false

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template0" {
				foundTemplate0 = true
			}
		}
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template1" {
				foundTemplate1 = true
			}
		}
	}

	require.True(t, foundTemplate0)
	require.False(t, foundTemplate1)
}

func TestPostgresqlDatabaseBlacklistTestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		IgnoredDatabases: []string{"template0"},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var foundTemplate0 = false
	var foundTemplate1 = false

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template0" {
				foundTemplate0 = true
			}
		}
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template1" {
				foundTemplate1 = true
			}
		}
	}

	require.False(t, foundTemplate0)
	require.True(t, foundTemplate1)
}
<commit_msg>test: update wait for statement for postgres (#11309)<commit_after>package postgresql

import (
	"fmt"
	"testing"

	"github.com/docker/go-connections/nat"
	"github.com/stretchr/testify/require"
	"github.com/testcontainers/testcontainers-go/wait"

	"github.com/influxdata/telegraf/testutil"
)

const servicePort = "5432"

func launchTestContainer(t *testing.T) *testutil.Container {
	container := testutil.Container{
		Image:        "postgres:alpine",
		ExposedPorts: []string{servicePort},
		Env: map[string]string{
			"POSTGRES_HOST_AUTH_METHOD": "trust",
		},
		WaitingFor: wait.ForAll(
			// the database comes up twice, once right away, then again a second
			// time after the docker entrypoint starts configuraiton
			wait.ForLog("database system is ready to accept connections").WithOccurrence(2),
			wait.ForListeningPort(nat.Port(servicePort)),
		),
	}

	err := container.Start()
	require.NoError(t, err, "failed to start container")

	return &container
}

func TestPostgresqlGeneratesMetricsIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
			IsPgBouncer: false,
		},
		Databases: []string{"postgres"},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	intMetrics := []string{
		"xact_commit",
		"xact_rollback",
		"blks_read",
		"blks_hit",
		"tup_returned",
		"tup_fetched",
		"tup_inserted",
		"tup_updated",
		"tup_deleted",
		"conflicts",
		"temp_files",
		"temp_bytes",
		"deadlocks",
		"buffers_alloc",
		"buffers_backend",
		"buffers_backend_fsync",
		"buffers_checkpoint",
		"buffers_clean",
		"checkpoints_req",
		"checkpoints_timed",
		"maxwritten_clean",
		"datid",
		"numbackends",
	}

	int32Metrics := []string{}

	floatMetrics := []string{
		"blk_read_time",
		"blk_write_time",
		"checkpoint_write_time",
		"checkpoint_sync_time",
	}

	stringMetrics := []string{
		"datname",
	}

	metricsCounted := 0

	for _, metric := range intMetrics {
		require.True(t, acc.HasInt64Field("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range int32Metrics {
		require.True(t, acc.HasInt32Field("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range floatMetrics {
		require.True(t, acc.HasFloatField("postgresql", metric))
		metricsCounted++
	}

	for _, metric := range stringMetrics {
		require.True(t, acc.HasStringField("postgresql", metric))
		metricsCounted++
	}

	require.True(t, metricsCounted > 0)
	require.Equal(t, len(floatMetrics)+len(intMetrics)+len(int32Metrics)+len(stringMetrics), metricsCounted)
}

func TestPostgresqlTagsMetricsWithDatabaseNameIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		Databases: []string{"postgres"},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	point, ok := acc.Get("postgresql")
	require.True(t, ok)

	require.Equal(t, "postgres", point.Tags["db"])
}

func TestPostgresqlDefaultsToAllDatabasesIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var found bool

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "postgres" {
				found = true
				break
			}
		}
	}

	require.True(t, found)
}

func TestPostgresqlIgnoresUnwantedColumnsIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	for col := range p.IgnoredColumns() {
		require.False(t, acc.HasMeasurement(col))
	}
}

func TestPostgresqlDatabaseWhitelistTestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		Databases: []string{"template0"},
	}

	var acc testutil.Accumulator

	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var foundTemplate0 = false
	var foundTemplate1 = false

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template0" {
				foundTemplate0 = true
			}
		}
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template1" {
				foundTemplate1 = true
			}
		}
	}

	require.True(t, foundTemplate0)
	require.False(t, foundTemplate1)
}

func TestPostgresqlDatabaseBlacklistTestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	container := launchTestContainer(t)
	defer func() {
		require.NoError(t, container.Terminate(), "terminating container failed")
	}()

	p := &Postgresql{
		Service: Service{
			Address: fmt.Sprintf(
				"host=%s port=%s user=postgres sslmode=disable",
				container.Address,
				container.Ports[servicePort],
			),
		},
		IgnoredDatabases: []string{"template0"},
	}

	var acc testutil.Accumulator
	require.NoError(t, p.Start(&acc))
	require.NoError(t, p.Gather(&acc))

	var foundTemplate0 = false
	var foundTemplate1 = false

	for _, pnt := range acc.Metrics {
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template0" {
				foundTemplate0 = true
			}
		}
		if pnt.Measurement == "postgresql" {
			if pnt.Tags["db"] == "template1" {
				foundTemplate1 = true
			}
		}
	}

	require.False(t, foundTemplate0)
	require.True(t, foundTemplate1)
}
<|endoftext|>