<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 main

import (
	"fmt"
	"k8s.io/test-infra/testgrid/config"
	"path"
	"strings"

	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	prowConfig "k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
	prowGCS "k8s.io/test-infra/prow/pod-utils/gcs"
)

const testgridCreateTestGroupAnnotation = "testgrid-create-test-group"
const testgridDashboardsAnnotation = "testgrid-dashboards"
const testgridTabNameAnnotation = "testgrid-tab-name"
const descriptionAnnotation = "description"

// Talk to @michelle192837 if you're thinking about adding more of these!

func applySingleProwjobAnnotations(c *Config, pc *prowConfig.Config, j prowConfig.JobBase, jobType prowapi.ProwJobType) error {
	tabName := j.Name
	testGroupName := j.Name
	description := j.Name

	mustMakeGroup := j.Annotations[testgridCreateTestGroupAnnotation] == "true"
	mustNotMakeGroup := j.Annotations[testgridCreateTestGroupAnnotation] == "false"
	dashboards, addToDashboards := j.Annotations[testgridDashboardsAnnotation]
	mightMakeGroup := (mustMakeGroup || addToDashboards || jobType != prowapi.PresubmitJob) && !mustNotMakeGroup

	if mightMakeGroup {
		if c.config.FindTestGroup(testGroupName) != nil {
			if mustMakeGroup {
				return fmt.Errorf("test group %q already exists", testGroupName)
			}
		} else {
			var prefix string
			if j.DecorationConfig != nil && j.DecorationConfig.GCSConfiguration != nil {
				prefix = path.Join(j.DecorationConfig.GCSConfiguration.Bucket, j.DecorationConfig.GCSConfiguration.PathPrefix)
			} else if pc.Plank.DefaultDecorationConfig != nil && pc.Plank.DefaultDecorationConfig.GCSConfiguration != nil {
				prefix = path.Join(pc.Plank.DefaultDecorationConfig.GCSConfiguration.Bucket, pc.Plank.DefaultDecorationConfig.GCSConfiguration.PathPrefix)
			} else {
				return fmt.Errorf("job %s: couldn't figure out a default decoration config", j.Name)
			}

			g := &config.TestGroup{
				Name:      testGroupName,
				GcsPrefix: path.Join(prefix, prowGCS.RootForSpec(&downwardapi.JobSpec{Job: j.Name, Type: jobType})),
			}
			ReconcileTestGroup(g, c.defaultConfig.DefaultTestGroup)
			c.config.TestGroups = append(c.config.TestGroups, g)
		}
	}

	if tn, ok := j.Annotations[testgridTabNameAnnotation]; ok {
		tabName = tn
	}
	if d := j.Annotations[descriptionAnnotation]; d != "" {
		description = d
	}

	if addToDashboards {
		for _, dashboardName := range strings.Split(dashboards, ",") {
			dashboardName = strings.TrimSpace(dashboardName)
			d := c.config.FindDashboard(dashboardName)
			if d == nil {
				return fmt.Errorf("couldn't find dashboard %q for job %q", dashboardName, j.Name)
			}
			dt := &config.DashboardTab{
				Name:          tabName,
				TestGroupName: testGroupName,
				Description:   description,
			}
			ReconcileDashboardTab(dt, c.defaultConfig.DefaultDashboardTab)
			d.DashboardTab = append(d.DashboardTab, dt)
		}
	}

	return nil
}

func applyProwjobAnnotations(c *Config, prowConfigAgent *prowConfig.Agent) error {
	pc := prowConfigAgent.Config()
	if pc == nil {
		return nil
	}
	jobs := prowConfigAgent.Config().JobConfig
	for _, j := range jobs.AllPeriodics() {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PeriodicJob); err != nil {
			return err
		}
	}
	for _, j := range jobs.AllPostsubmits(nil) {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PostsubmitJob); err != nil {
			return err
		}
	}
	for _, j := range jobs.AllPresubmits(nil) {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PresubmitJob); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Add support for testgrid-alert-email annotation.<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 main

import (
	"fmt"
	"k8s.io/test-infra/testgrid/config"
	"path"
	"strings"

	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	prowConfig "k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
	prowGCS "k8s.io/test-infra/prow/pod-utils/gcs"
)

const testgridCreateTestGroupAnnotation = "testgrid-create-test-group"
const testgridDashboardsAnnotation = "testgrid-dashboards"
const testgridTabNameAnnotation = "testgrid-tab-name"
const testgridEmailAnnotation = "testgrid-alert-email"
const descriptionAnnotation = "description"

// Talk to @michelle192837 if you're thinking about adding more of these!

func applySingleProwjobAnnotations(c *Config, pc *prowConfig.Config, j prowConfig.JobBase, jobType prowapi.ProwJobType) error {
	tabName := j.Name
	testGroupName := j.Name
	description := j.Name

	mustMakeGroup := j.Annotations[testgridCreateTestGroupAnnotation] == "true"
	mustNotMakeGroup := j.Annotations[testgridCreateTestGroupAnnotation] == "false"
	dashboards, addToDashboards := j.Annotations[testgridDashboardsAnnotation]
	mightMakeGroup := (mustMakeGroup || addToDashboards || jobType != prowapi.PresubmitJob) && !mustNotMakeGroup

	if mightMakeGroup {
		if c.config.FindTestGroup(testGroupName) != nil {
			if mustMakeGroup {
				return fmt.Errorf("test group %q already exists", testGroupName)
			}
		} else {
			var prefix string
			if j.DecorationConfig != nil && j.DecorationConfig.GCSConfiguration != nil {
				prefix = path.Join(j.DecorationConfig.GCSConfiguration.Bucket, j.DecorationConfig.GCSConfiguration.PathPrefix)
			} else if pc.Plank.DefaultDecorationConfig != nil && pc.Plank.DefaultDecorationConfig.GCSConfiguration != nil {
				prefix = path.Join(pc.Plank.DefaultDecorationConfig.GCSConfiguration.Bucket, pc.Plank.DefaultDecorationConfig.GCSConfiguration.PathPrefix)
			} else {
				return fmt.Errorf("job %s: couldn't figure out a default decoration config", j.Name)
			}

			g := &config.TestGroup{
				Name:      testGroupName,
				GcsPrefix: path.Join(prefix, prowGCS.RootForSpec(&downwardapi.JobSpec{Job: j.Name, Type: jobType})),
			}
			ReconcileTestGroup(g, c.defaultConfig.DefaultTestGroup)
			c.config.TestGroups = append(c.config.TestGroups, g)
		}
	}

	if tn, ok := j.Annotations[testgridTabNameAnnotation]; ok {
		tabName = tn
	}
	if d := j.Annotations[descriptionAnnotation]; d != "" {
		description = d
	}

	if addToDashboards {
		firstDashboard := true
		for _, dashboardName := range strings.Split(dashboards, ",") {
			dashboardName = strings.TrimSpace(dashboardName)
			d := c.config.FindDashboard(dashboardName)
			if d == nil {
				return fmt.Errorf("couldn't find dashboard %q for job %q", dashboardName, j.Name)
			}
			dt := &config.DashboardTab{
				Name:          tabName,
				TestGroupName: testGroupName,
				Description:   description,
			}
			if firstDashboard {
				firstDashboard = false
				if emails, ok := j.Annotations[testgridEmailAnnotation]; ok {
					dt.AlertOptions = &config.DashboardTabAlertOptions{AlertMailToAddresses: emails}
				}
			}
			ReconcileDashboardTab(dt, c.defaultConfig.DefaultDashboardTab)
			d.DashboardTab = append(d.DashboardTab, dt)
		}
	}

	return nil
}

func applyProwjobAnnotations(c *Config, prowConfigAgent *prowConfig.Agent) error {
	pc := prowConfigAgent.Config()
	if pc == nil {
		return nil
	}
	jobs := prowConfigAgent.Config().JobConfig
	for _, j := range jobs.AllPeriodics() {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PeriodicJob); err != nil {
			return err
		}
	}
	for _, j := range jobs.AllPostsubmits(nil) {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PostsubmitJob); err != nil {
			return err
		}
	}
	for _, j := range jobs.AllPresubmits(nil) {
		if err := applySingleProwjobAnnotations(c, pc, j.JobBase, prowapi.PresubmitJob); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/modules"
	"os"

	"flag"
	"github.com/eaciit/dbox"
	"github.com/eaciit/toolkit"
	"strings"
	// "sync"
	"time"
)

var conn dbox.IConnection
var count int

var (
	t0         time.Time
	fiscalyear int
	data       map[string]float64
	tablename  = "salespls-1"
	alldata    = toolkit.M{}
)

func setinitialconnection() {
	var err error
	conn, err = modules.GetDboxIConnection("db_godrej")

	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}

	err = gdrj.SetDb(conn)
	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}
}

func main() {
	t0 = time.Now()
	data = make(map[string]float64)
	flag.IntVar(&fiscalyear, "year", 2015, "YYYY representation of godrej fiscal year. Default is 2015")
	flag.Parse()

	setinitialconnection()
	defer gdrj.CloseDb()

	toolkit.Println("Preparing data query...")

	eperiode := time.Date(fiscalyear, 4, 1, 0, 0, 0, 0, time.UTC)
	speriode := eperiode.AddDate(-1, 0, 0)

	seeds := make([]time.Time, 0, 0)
	seeds = append(seeds, speriode)
	for {
		speriode = speriode.AddDate(0, 0, 1)
		if !speriode.Before(eperiode) {
			break
		}
		seeds = append(seeds, speriode)
	}

	toolkit.Println("Starting worker query...")
	result := make(chan toolkit.M, len(seeds))
	for i, v := range seeds {
		filter := dbox.Eq("date.date", v)
		go workerproc(i, filter, result)
	}

	step := len(seeds) / 100
	if step == 0 {
		step = 1
	}
	// "date.fiscal","date.quartertxt","date.month", "customer.branchname", "customer.keyaccount",
	// "customer.channelid", "customer.channelname", "customer.reportchannel", "customer.reportsubchannel",
	// "customer.zone", "customer.region", "customer.areaname","product.brand"
	toolkit.Println("Waiting result query...")
	for i := 1; i <= len(seeds); i++ {
		a := <-result
		for k, v := range a {
			lasttkm := toolkit.M{}
			newtkm, _ := toolkit.ToM(v)
			if alldata.Has(k) {
				lasttkm = alldata[k].(toolkit.M)
			}

			for tk, tv := range newtkm {
				df := toolkit.ToFloat64(tv, 6, toolkit.RoundingAuto) + lasttkm.GetFloat64(tk)
				lasttkm.Set(tk, df)
			}
			alldata.Set(k, lasttkm)
		}

		if i%step == 0 {
			toolkit.Printfn("Worker %d of %d (%d), Done in %s",
				i, len(seeds), (i / step), time.Since(t0).String())
		}
	}

	listdimension := []string{"date.fiscal,customer.channelid,customer.channelname",
		"date.fiscal,customer.channelid,customer.channelname,customer.reportsubchannel",
		"date.fiscal,customer.channelid,customer.channelname,customer.zone",
		"date.fiscal,customer.channelid,customer.channelname,customer.areaname",
		"date.fiscal,customer.channelid,customer.channelname,customer.region"}

	resdimension := make(chan int, len(listdimension))
	dimension := make(chan string, len(listdimension))
	detaildata := make(chan toolkit.M)
	ressavedata := make(chan int)

	for i := 0; i < 3; i++ {
		go workerbuilddimension(i, dimension, detaildata, resdimension)
	}

	for i := 0; i < 10; i++ {
		go workersavedata(i, detaildata, ressavedata)
	}

	toolkit.Printfn("Prepare saving collection, Create dimension")
	for _, str := range listdimension {
		dimension <- str
	}
	// close(dimension)

	alldatarows := 0
	toolkit.Printfn("Waiting dimension result")
	for i := 0; i < len(listdimension); i++ {
		alldatarows += <-resdimension
	}
	close(detaildata)

	step = alldatarows / 100
	if step == 0 {
		step = 1
	}
	toolkit.Printfn("Saving dimension result")
	for i := 0; i < alldatarows; i++ {
		<-ressavedata
		if i%step == 0 {
			toolkit.Printfn("Data saved %d of %d (%d), Done in %s",
				i, alldatarows, (i / step), time.Since(t0).String())
		}
	}

	toolkit.Printfn("Processing done in %s",
		time.Since(t0).String())
}

func workerproc(wi int, filter *dbox.Filter, result chan<- toolkit.M) {
	workerconn, _ := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()
	tkm := toolkit.M{}
	// "date.fiscal","date.quartertxt","date.month", "customer.branchname", "customer.keyaccount",
	// "customer.channelid", "customer.channelname", "customer.reportchannel", "customer.reportsubchannel",
	// "customer.zone", "customer.region", "customer.areaname","product.brand"
	csr, _ := workerconn.NewQuery().Select("date", "customer", "product.brand", "pldatas").
		From(tablename).
		Where(filter).
		Cursor(nil)

	defer csr.Close()

	scount := csr.Count()
	iscount := 0
	step := scount / 100

	if step == 0 {
		step = 1
	}

	for {
		iscount++
		spl := new(gdrj.SalesPL)
		e := csr.Fetch(spl, 1, false)
		if e != nil {
			break
		}

		key := toolkit.Sprintf("%s|%s|%d|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s", spl.Date.Fiscal, spl.Date.QuarterTxt, spl.Date.Month,
			spl.Customer.BranchName, spl.Customer.KeyAccount, spl.Customer.ChannelID, spl.Customer.ChannelName,
			spl.Customer.ReportChannel, spl.Customer.ReportSubChannel, spl.Customer.Zone, spl.Customer.Region,
			spl.Customer.AreaName, spl.Product.Brand)

		dtkm := toolkit.M{}
		if tkm.Has(key) {
			dtkm = tkm[key].(toolkit.M)
		}

		tv := spl.GrossAmount + dtkm.GetFloat64("grossamount")
		dtkm.Set("grossamount", tv)

		tv = spl.NetAmount + dtkm.GetFloat64("netamount")
		dtkm.Set("netamount", tv)

		tv = spl.DiscountAmount + dtkm.GetFloat64("discountamount")
		dtkm.Set("discountamount", tv)

		tv = spl.SalesQty + dtkm.GetFloat64("salesqty")
		dtkm.Set("salesqty", tv)

		for k, v := range spl.PLDatas {
			tv := v.Amount + tkm.GetFloat64(k)
			dtkm.Set(k, tv)
		}

		tkm.Set(key, dtkm)

		if iscount == 3 {
			break
		}

	}

	result <- tkm

	toolkit.Printfn("Go %d. Processing done in %s",
		wi,
		time.Since(t0).String())
}

func workerbuilddimension(wi int, dimension <-chan string, detaildata chan<- toolkit.M, resdimension chan<- int) {
	//"date.fiscal", "customer.channelid", "customer.channelname"
	sortkeys := []string{"date.fiscal", "date.quartertxt", "date.month",
		"customer.branchname", "customer.keyaccount", "customer.channelid", "customer.channelname", "customer.reportchannel",
		"customer.reportsubchannel", "customer.zone", "customer.region", "customer.areaname", "product.brand"}

	str := ""
	for str = range dimension {
		toolkit.Println(str)
		payload := new(gdrj.PLFinderParam)
		payload.Breakdowns = strings.Split(str, ",")
		tablename := toolkit.Sprintf("1-%v", payload.GetTableName())

		tkm := toolkit.M{}
		for key, val := range alldata {
			// toolkit.Printfn("%s", key)
			arrkey := strings.Split(key, "|")
			// toolkit.Printfn("%d - %d", len(sortkeys), len(arrkey))
			dkey := ""
			for i, v := range sortkeys {
				for _, vx := range payload.Breakdowns {
					if vx == v {
						if len(dkey) > 0 {
							dkey = toolkit.Sprintf("%s|%s", dkey, arrkey[i])
						} else {
							dkey = arrkey[i]
						}
					}
				}
			}

			stkm, _ := toolkit.ToM(val)
			dtkm := toolkit.M{}
			if tkm.Has(dkey) {
				dtkm = tkm[dkey].(toolkit.M)
			}

			for k, v := range stkm {
				fv := toolkit.ToFloat64(v, 6, toolkit.RoundingAuto) + dtkm.GetFloat64(k)
				dtkm.Set(k, fv)
			}

			tkm.Set(dkey, dtkm)
		}
		toolkit.Println("SUM Data : ", len(tkm))
		for k, v := range tkm {
			a, _ := toolkit.ToM(v)
			id := toolkit.M{}
			arrk := strings.Split(k, "|")
			for i, sv := range payload.Breakdowns {
				tsv := strings.Replace(sv, ".", "_", -1)
				id.Set(tsv, arrk[i])
			}
			a.Set("_id", id)

			detaildata <- toolkit.M{}.Set(tablename, a)
		}

		resdimension <- len(tkm)
	}
}

func workersavedata(wi int, detaildata <-chan toolkit.M, ressavedata chan<- int) {
	workerconn, _ := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()

	tkm := toolkit.M{}
	for tkm = range detaildata {
		for tbl, dt := range tkm {
			workerconn.NewQuery().From(tbl).
				Save().
				Exec(toolkit.M{}.
				Set("data", dt))
			ressavedata <- 1
		}
	}
}
<commit_msg>debug<commit_after>package main

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/modules"
	"os"

	"flag"
	"github.com/eaciit/dbox"
	"github.com/eaciit/toolkit"
	"strings"
	// "sync"
	"time"
)

var conn dbox.IConnection
var count int

var (
	t0         time.Time
	fiscalyear int
	data       map[string]float64
	tablename  = "salespls-1"
	alldata    = toolkit.M{}
)

func setinitialconnection() {
	var err error
	conn, err = modules.GetDboxIConnection("db_godrej")

	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}

	err = gdrj.SetDb(conn)
	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}
}

func main() {
	t0 = time.Now()
	data = make(map[string]float64)
	flag.IntVar(&fiscalyear, "year", 2015, "YYYY representation of godrej fiscal year. Default is 2015")
	flag.Parse()

	setinitialconnection()
	defer gdrj.CloseDb()

	toolkit.Println("Preparing data query...")

	eperiode := time.Date(fiscalyear, 4, 1, 0, 0, 0, 0, time.UTC)
	speriode := eperiode.AddDate(-1, 0, 0)

	seeds := make([]time.Time, 0, 0)
	seeds = append(seeds, speriode)
	for {
		speriode = speriode.AddDate(0, 0, 1)
		if !speriode.Before(eperiode) {
			break
		}
		seeds = append(seeds, speriode)
	}

	toolkit.Println("Starting worker query...")
	result := make(chan toolkit.M, len(seeds))
	for i, v := range seeds {
		filter := dbox.Eq("date.date", v)
		go workerproc(i, filter, result)
	}

	step := len(seeds) / 100
	if step == 0 {
		step = 1
	}
	// "date.fiscal","date.quartertxt","date.month", "customer.branchname", "customer.keyaccount",
	// "customer.channelid", "customer.channelname", "customer.reportchannel", "customer.reportsubchannel",
	// "customer.zone", "customer.region", "customer.areaname","product.brand"
	toolkit.Println("Waiting result query...")
	for i := 1; i <= len(seeds); i++ {
		a := <-result
		for k, v := range a {
			lasttkm := toolkit.M{}
			newtkm, _ := toolkit.ToM(v)
			if alldata.Has(k) {
				lasttkm = alldata[k].(toolkit.M)
			}

			for tk, tv := range newtkm {
				df := toolkit.ToFloat64(tv, 6, toolkit.RoundingAuto) + lasttkm.GetFloat64(tk)
				lasttkm.Set(tk, df)
			}
			alldata.Set(k, lasttkm)
		}

		if i%step == 0 {
			toolkit.Printfn("Worker %d of %d (%d), Done in %s",
				i, len(seeds), (i / step), time.Since(t0).String())
		}
	}

	listdimension := []string{"date.fiscal,customer.channelid,customer.channelname",
		"date.fiscal,customer.channelid,customer.channelname,customer.reportsubchannel",
		"date.fiscal,customer.channelid,customer.channelname,customer.zone",
		"date.fiscal,customer.channelid,customer.channelname,customer.areaname",
		"date.fiscal,customer.channelid,customer.channelname,customer.region"}

	resdimension := make(chan int, len(listdimension))
	dimension := make(chan string, len(listdimension))
	detaildata := make(chan toolkit.M)
	ressavedata := make(chan int)

	for i := 0; i < 3; i++ {
		go workerbuilddimension(i, dimension, detaildata, resdimension)
	}

	for i := 0; i < 10; i++ {
		go workersavedata(i, detaildata, ressavedata)
	}

	toolkit.Printfn("Prepare saving collection, Create dimension")
	for _, str := range listdimension {
		dimension <- str
	}
	// close(dimension)

	alldatarows := 0
	toolkit.Printfn("Waiting dimension result")
	for i := 0; i < len(listdimension); i++ {
		alldatarows += <-resdimension
	}
	close(detaildata)

	step = alldatarows / 100
	if step == 0 {
		step = 1
	}
	toolkit.Printfn("Saving dimension result")
	for i := 0; i < alldatarows; i++ {
		<-ressavedata
		if i%step == 0 {
			toolkit.Printfn("Data saved %d of %d (%d), Done in %s",
				i, alldatarows, (i / step), time.Since(t0).String())
		}
	}

	toolkit.Printfn("Processing done in %s",
		time.Since(t0).String())
}

func workerproc(wi int, filter *dbox.Filter, result chan<- toolkit.M) {
	workerconn, _ := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()
	tkm := toolkit.M{}
	// "date.fiscal","date.quartertxt","date.month", "customer.branchname", "customer.keyaccount",
	// "customer.channelid", "customer.channelname", "customer.reportchannel", "customer.reportsubchannel",
	// "customer.zone", "customer.region", "customer.areaname","product.brand"
	csr, _ := workerconn.NewQuery().Select("date", "customer", "product.brand", "pldatas").
		From(tablename).
		Where(filter).
		Cursor(nil)

	defer csr.Close()

	scount := csr.Count()
	iscount := 0
	step := scount / 100

	if step == 0 {
		step = 1
	}

	for {
		iscount++
		spl := new(gdrj.SalesPL)
		e := csr.Fetch(spl, 1, false)
		if e != nil {
			break
		}

		key := toolkit.Sprintf("%s|%s|%d|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s", spl.Date.Fiscal, spl.Date.QuarterTxt, spl.Date.Month,
			spl.Customer.BranchName, spl.Customer.KeyAccount, spl.Customer.ChannelID, spl.Customer.ChannelName,
			spl.Customer.ReportChannel, spl.Customer.ReportSubChannel, spl.Customer.Zone, spl.Customer.Region,
			spl.Customer.AreaName, spl.Product.Brand)

		dtkm := toolkit.M{}
		if tkm.Has(key) {
			dtkm = tkm[key].(toolkit.M)
		}

		tv := spl.GrossAmount + dtkm.GetFloat64("grossamount")
		dtkm.Set("grossamount", tv)

		tv = spl.NetAmount + dtkm.GetFloat64("netamount")
		dtkm.Set("netamount", tv)

		tv = spl.DiscountAmount + dtkm.GetFloat64("discountamount")
		dtkm.Set("discountamount", tv)

		tv = spl.SalesQty + dtkm.GetFloat64("salesqty")
		dtkm.Set("salesqty", tv)

		for k, v := range spl.PLDatas {
			tv := v.Amount + tkm.GetFloat64(k)
			dtkm.Set(k, tv)
		}

		tkm.Set(key, dtkm)

		if iscount == 3 {
			break
		}

	}

	result <- tkm

	toolkit.Printfn("Go %d. Processing done in %s",
		wi,
		time.Since(t0).String())
}

func workerbuilddimension(wi int, dimension <-chan string, detaildata chan<- toolkit.M, resdimension chan<- int) {
	//"date.fiscal", "customer.channelid", "customer.channelname"
	sortkeys := []string{"date.fiscal", "date.quartertxt", "date.month",
		"customer.branchname", "customer.keyaccount", "customer.channelid", "customer.channelname", "customer.reportchannel",
		"customer.reportsubchannel", "customer.zone", "customer.region", "customer.areaname", "product.brand"}

	str := ""
	for str = range dimension {
		toolkit.Println(str)
		payload := new(gdrj.PLFinderParam)
		payload.Breakdowns = strings.Split(str, ",")
		tablename := toolkit.Sprintf("1-%v", payload.GetTableName())

		tkm := toolkit.M{}
		for key, val := range alldata {
			toolkit.Printfn("%s", key)
			arrkey := strings.Split(key, "|")
			// toolkit.Printfn("%d - %d", len(sortkeys), len(arrkey))
			dkey := ""
			for i, v := range sortkeys {
				for _, vx := range payload.Breakdowns {
					if vx == v {
						if len(dkey) > 0 {
							dkey = toolkit.Sprintf("%s|%s", dkey, arrkey[i])
						} else {
							dkey = arrkey[i]
						}
					}
				}
			}

			stkm, _ := toolkit.ToM(val)
			dtkm := toolkit.M{}
			if tkm.Has(dkey) {
				dtkm = tkm[dkey].(toolkit.M)
			}

			for k, v := range stkm {
				fv := toolkit.ToFloat64(v, 6, toolkit.RoundingAuto) + dtkm.GetFloat64(k)
				dtkm.Set(k, fv)
			}

			tkm.Set(dkey, dtkm)
		}
		toolkit.Println("SUM Data : ", len(tkm))
		for k, v := range tkm {
			a, _ := toolkit.ToM(v)
			id := toolkit.M{}
			arrk := strings.Split(k, "|")
			for i, sv := range payload.Breakdowns {
				tsv := strings.Replace(sv, ".", "_", -1)
				id.Set(tsv, arrk[i])
			}
			a.Set("_id", id)

			detaildata <- toolkit.M{}.Set(tablename, a)
		}

		resdimension <- len(tkm)
	}
}

func workersavedata(wi int, detaildata <-chan toolkit.M, ressavedata chan<- int) {
	workerconn, _ := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()

	tkm := toolkit.M{}
	for tkm = range detaildata {
		for tbl, dt := range tkm {
			workerconn.NewQuery().From(tbl).
				Save().
				Exec(toolkit.M{}.
				Set("data", dt))
			ressavedata <- 1
		}
	}
}
<|endoftext|>


<commit_before>package plugins

import (
    "github.com/bwmarrin/discordgo"
    "strings"
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/Seklfreak/Robyul2/logger"
    "fmt"
    "strconv"
    "github.com/getsentry/raven-go"
    "time"
    rethink "github.com/gorethink/gorethink"
)

type Troublemaker struct{}

func (t *Troublemaker) Commands() []string {
    return []string{
        "troublemaker",
        "troublemakers",
        "tm",
    }
}

type DB_Troublemaker_Entry struct {
    ID                string         `gorethink:"id,omitempty"`
    UserID            string         `gorethink:"userid"`
    Reason            string         `gorethink:"reason"`
    CreatedAt         time.Time      `gorethink:"createdat"`
    ReportedByGuildID string         `gorethink:"reportedby_guildid"`
    ReportedByUserID  string         `gorethink:"reportedby_userid"`
}

func (t *Troublemaker) Init(session *discordgo.Session) {

}

func (t *Troublemaker) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    args := strings.Fields(content)
    if len(args) >= 1 {
        switch args[0] {
        case "participate":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                channel, err := helpers.GetChannel(msg.ChannelID)
                helpers.Relax(err)

                settings := helpers.GuildSettingsGetCached(channel.GuildID)

                if len(args) >= 2 {
                    // Set new log channel
                    targetChannel, err := helpers.GetChannelFromMention(msg, args[1])
                    if err != nil || targetChannel.ID == "" {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                        return
                    }

                    settings.TroublemakerIsParticipating = true
                    settings.TroublemakerLogChannel = targetChannel.ID
                    err = helpers.GuildSettingsSet(channel.GuildID, settings)
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.troublemaker.participation-enabled"))
                    helpers.Relax(err)

                    return
                } else {
                    // Disable logging
                    settings.TroublemakerIsParticipating = false
                    err = helpers.GuildSettingsSet(channel.GuildID, settings)
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.troublemaker.participation-disabled"))
                    helpers.Relax(err)

                    return
                }
                return
            })
            break
        case "list":
            helpers.RequireMod(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                if len(args) < 2 {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                targetUser, err := helpers.GetUserFromMention(args[1])
                if err != nil || targetUser.ID == "" {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                troublemakerReports := t.getTroublemakerReports(targetUser)

                if len(troublemakerReports) <= 0 {
                    _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.troublemaker.list-no-reports", targetUser.Username))
                    helpers.Relax(err)
                    return
                } else {
                    troublemakerText := fmt.Sprintf("I found %d report(s) for `%s#%s` `#%s` <@%s>:\n",
                        len(troublemakerReports), targetUser.Username, targetUser.Discriminator, targetUser.ID, targetUser.ID,
                    )

                    for _, troublemakerReport := range troublemakerReports {
                        reportedByUser, err := helpers.GetUser(troublemakerReport.ReportedByUserID)
                        if err != nil {
                            reportedByUser = new(discordgo.User)
                            reportedByUser.ID = troublemakerReport.ReportedByUserID
                            reportedByUser.Username = "N/A"
                        }
                        reportedByGuild, err := helpers.GetGuild(troublemakerReport.ReportedByGuildID)
                        if err != nil {
                            reportedByGuild = new(discordgo.Guild)
                            reportedByGuild.ID = troublemakerReport.ReportedByGuildID
                            reportedByGuild.Name = "N/A"
                        }
                        troublemakerText += fmt.Sprintf("At: `%s`, Reason: `%s`, Reported By: `%s` (`#%s`) On: `%s` (`#%s`)\n",
                            troublemakerReport.CreatedAt.Format(time.ANSIC), troublemakerReport.Reason,
                            reportedByUser.Username, reportedByUser.ID, reportedByGuild.Name, reportedByGuild.ID,
                        )
                    }

                    for _, page := range helpers.Pagify(troublemakerText, "\n") {
                        _, err := session.ChannelMessageSend(msg.ChannelID, page)
                        helpers.Relax(err)
                    }
                }
            })
        default:
            helpers.RequireMod(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                channel, err := helpers.GetChannel(msg.ChannelID)
                helpers.Relax(err)
                guild, err := helpers.GetGuild(channel.GuildID)
                helpers.Relax(err)

                if len(args) < 2 {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                targetUser, err := helpers.GetUserFromMention(args[0])
                if err != nil || targetUser.ID == "" {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                reasonText := strings.TrimSpace(strings.Replace(content, strings.Join(args[:1], " "), "", 1))

                if helpers.ConfirmEmbed(msg.ChannelID, msg.Author, helpers.GetTextF("plugins.troublemaker.report-confirm",
                    targetUser.Username, targetUser.Discriminator, targetUser.ID, targetUser.ID, reasonText,
                ), "✅", "🚫") == true {
                    // Save to log DB
                    troublemakerLogEntry := t.getEntryByOrCreateEmpty("id", "")
                    troublemakerLogEntry.UserID = targetUser.ID
                    troublemakerLogEntry.Reason = reasonText
                    troublemakerLogEntry.CreatedAt = time.Now()
                    troublemakerLogEntry.ReportedByGuildID = guild.ID
                    troublemakerLogEntry.ReportedByUserID = msg.Author.ID
                    t.setEntry(troublemakerLogEntry)

                    logger.INFO.L("troublemaker", fmt.Sprintf("will notify about troublemaker %s (#%s) by %s (#%s) on %s (#%s) reason %s",
                        targetUser.Username, targetUser.ID,
                        msg.Author.Username, msg.Author.ID,
                        guild.Name, guild.ID,
                        reasonText,
                    ))

                    guildsToNotify := make([]*discordgo.Guild, 0)

                    for _, guildToNotify := range session.State.Guilds {
                        if guildToNotify.ID != guild.ID {
                            guildToNotifySettings := helpers.GuildSettingsGetCached(guildToNotify.ID)
                            if guildToNotifySettings.TroublemakerIsParticipating == true && guildToNotifySettings.TroublemakerLogChannel != "" {
                                guildsToNotify = append(guildsToNotify, guildToNotify)
                            }
                        }
                    }

                    successMessage, _ := session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.troublemaker.report-successful", len(guildsToNotify)))

                    // Send notifications out
                    go func() {

                        for _, guildToNotify := range guildsToNotify {
                            guildToNotifySettings := helpers.GuildSettingsGetCached(guildToNotify.ID)
                            if guildToNotifySettings.TroublemakerIsParticipating == true && guildToNotifySettings.TroublemakerLogChannel != "" {
                                targetUserIsOnServer := false
                                _, err := helpers.GetGuildMember(guildToNotify.ID, targetUser.ID)
                                if err == nil {
                                    targetUserIsOnServer = true
                                }

                                reportEmbed := &discordgo.MessageEmbed{
                                    Title: helpers.GetTextF("plugins.troublemaker.report-embed-title", targetUser.Username, targetUser.Discriminator),
                                    Description: helpers.GetTextF("plugins.troublemaker.report-embed-description", targetUser.ID, targetUser.ID),
                                    URL:       helpers.GetAvatarUrl(targetUser),
                                    Thumbnail: &discordgo.MessageEmbedThumbnail{URL: helpers.GetAvatarUrl(targetUser)},
                                    Footer:    &discordgo.MessageEmbedFooter{Text: helpers.GetTextF("plugins.troublemaker.report-embed-footer", len(guildsToNotify))},
                                    Color:     0x0FADED,
                                    Fields: []*discordgo.MessageEmbedField{
                                        {Name: "Reason stated", Value: reasonText, Inline: false},
                                    },
                                }

                                if targetUserIsOnServer == true {
                                    reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                        Name: "Member status", Value: "⚠ User is on this server", Inline: false,
                                    })
                                } else {
                                    reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                        Name: "Member status", Value: "✅ User is not on this server", Inline: false,
                                    })
                                }

                                reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                    Name: "Reported by", Value: fmt.Sprintf("**%s** (#%s) <@%s>\non **%s** (#%s)",
                                    msg.Author.Username, msg.Author.ID, msg.Author.ID, guild.Name, guild.ID,
                                ), Inline: false})

                                _, err = session.ChannelMessageSendEmbed(guildToNotifySettings.TroublemakerLogChannel, reportEmbed)
                                if err != nil {
                                    raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{
                                        "ChannelID":       msg.ChannelID,
                                        "Content":         msg.Content,
                                        "Timestamp":       string(msg.Timestamp),
                                        "TTS":             strconv.FormatBool(msg.Tts),
                                        "MentionEveryone": strconv.FormatBool(msg.MentionEveryone),
                                        "IsBot":           strconv.FormatBool(msg.Author.Bot),
                                    })
                                }
                            }
                        }

                        if successMessage.ID != "" {
                            session.MessageReactionAdd(msg.ChannelID, successMessage.ID, "👌")
                        }
                        return
                    }()
                    return
                }
                return
            })
            break
        }
    }
}

func (t *Troublemaker) getTroublemakerReports(user *discordgo.User) []DB_Troublemaker_Entry {
    var entryBucket []DB_Troublemaker_Entry
    listCursor, err := rethink.Table("troublemakerlog").Filter(
        rethink.Row.Field("userid").Eq(user.ID),
    ).Run(helpers.GetDB())
    helpers.Relax(err)
    defer listCursor.Close()
    listCursor.All(&entryBucket)

    return entryBucket
}

func (t *Troublemaker) getEntryByOrCreateEmpty(key string, id string) DB_Troublemaker_Entry {
    var entryBucket DB_Troublemaker_Entry
    listCursor, err := rethink.Table("troublemakerlog").Filter(
        rethink.Row.Field(key).Eq(id),
    ).Run(helpers.GetDB())
    defer listCursor.Close()
    err = listCursor.One(&entryBucket)

    // If user has no DB entries create an empty document
    if err == rethink.ErrEmptyResult {
        insert := rethink.Table("troublemakerlog").Insert(DB_Troublemaker_Entry{})
        res, e := insert.RunWrite(helpers.GetDB())
        // If the creation was successful read the document
        if e != nil {
            panic(e)
        } else {
            return t.getEntryByOrCreateEmpty("id", res.GeneratedKeys[0])
        }
    } else if err != nil {
        panic(err)
    }

    return entryBucket
}

func (t *Troublemaker) setEntry(entry DB_Troublemaker_Entry) {
    _, err := rethink.Table("troublemakerlog").Update(entry).Run(helpers.GetDB())
    helpers.Relax(err)
}
<commit_msg>fixes ROBYUL2-3V<commit_after>package plugins

import (
    "github.com/bwmarrin/discordgo"
    "strings"
    "github.com/Seklfreak/Robyul2/helpers"
    "github.com/Seklfreak/Robyul2/logger"
    "fmt"
    "time"
    rethink "github.com/gorethink/gorethink"
)

type Troublemaker struct{}

func (t *Troublemaker) Commands() []string {
    return []string{
        "troublemaker",
        "troublemakers",
        "tm",
    }
}

type DB_Troublemaker_Entry struct {
    ID                string         `gorethink:"id,omitempty"`
    UserID            string         `gorethink:"userid"`
    Reason            string         `gorethink:"reason"`
    CreatedAt         time.Time      `gorethink:"createdat"`
    ReportedByGuildID string         `gorethink:"reportedby_guildid"`
    ReportedByUserID  string         `gorethink:"reportedby_userid"`
}

func (t *Troublemaker) Init(session *discordgo.Session) {

}

func (t *Troublemaker) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
    args := strings.Fields(content)
    if len(args) >= 1 {
        switch args[0] {
        case "participate":
            helpers.RequireAdmin(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                channel, err := helpers.GetChannel(msg.ChannelID)
                helpers.Relax(err)

                settings := helpers.GuildSettingsGetCached(channel.GuildID)

                if len(args) >= 2 {
                    // Set new log channel
                    targetChannel, err := helpers.GetChannelFromMention(msg, args[1])
                    if err != nil || targetChannel.ID == "" {
                        session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                        return
                    }

                    settings.TroublemakerIsParticipating = true
                    settings.TroublemakerLogChannel = targetChannel.ID
                    err = helpers.GuildSettingsSet(channel.GuildID, settings)
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.troublemaker.participation-enabled"))
                    helpers.Relax(err)

                    return
                } else {
                    // Disable logging
                    settings.TroublemakerIsParticipating = false
                    err = helpers.GuildSettingsSet(channel.GuildID, settings)
                    helpers.Relax(err)

                    _, err = session.ChannelMessageSend(msg.ChannelID, helpers.GetText("plugins.troublemaker.participation-disabled"))
                    helpers.Relax(err)

                    return
                }
                return
            })
            break
        case "list":
            helpers.RequireMod(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                if len(args) < 2 {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                targetUser, err := helpers.GetUserFromMention(args[1])
                if err != nil || targetUser.ID == "" {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                troublemakerReports := t.getTroublemakerReports(targetUser)

                if len(troublemakerReports) <= 0 {
                    _, err := session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.troublemaker.list-no-reports", targetUser.Username))
                    helpers.Relax(err)
                    return
                } else {
                    troublemakerText := fmt.Sprintf("I found %d report(s) for `%s#%s` `#%s` <@%s>:\n",
                        len(troublemakerReports), targetUser.Username, targetUser.Discriminator, targetUser.ID, targetUser.ID,
                    )

                    for _, troublemakerReport := range troublemakerReports {
                        reportedByUser, err := helpers.GetUser(troublemakerReport.ReportedByUserID)
                        if err != nil {
                            reportedByUser = new(discordgo.User)
                            reportedByUser.ID = troublemakerReport.ReportedByUserID
                            reportedByUser.Username = "N/A"
                        }
                        reportedByGuild, err := helpers.GetGuild(troublemakerReport.ReportedByGuildID)
                        if err != nil {
                            reportedByGuild = new(discordgo.Guild)
                            reportedByGuild.ID = troublemakerReport.ReportedByGuildID
                            reportedByGuild.Name = "N/A"
                        }
                        troublemakerText += fmt.Sprintf("At: `%s`, Reason: `%s`, Reported By: `%s` (`#%s`) On: `%s` (`#%s`)\n",
                            troublemakerReport.CreatedAt.Format(time.ANSIC), troublemakerReport.Reason,
                            reportedByUser.Username, reportedByUser.ID, reportedByGuild.Name, reportedByGuild.ID,
                        )
                    }

                    for _, page := range helpers.Pagify(troublemakerText, "\n") {
                        _, err := session.ChannelMessageSend(msg.ChannelID, page)
                        helpers.Relax(err)
                    }
                }
            })
        default:
            helpers.RequireMod(msg, func() {
                session.ChannelTyping(msg.ChannelID)

                channel, err := helpers.GetChannel(msg.ChannelID)
                helpers.Relax(err)
                guild, err := helpers.GetGuild(channel.GuildID)
                helpers.Relax(err)

                if len(args) < 2 {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                targetUser, err := helpers.GetUserFromMention(args[0])
                if err != nil || targetUser.ID == "" {
                    session.ChannelMessageSend(msg.ChannelID, helpers.GetText("bot.arguments.invalid"))
                    return
                }

                reasonText := strings.TrimSpace(strings.Replace(content, strings.Join(args[:1], " "), "", 1))

                if helpers.ConfirmEmbed(msg.ChannelID, msg.Author, helpers.GetTextF("plugins.troublemaker.report-confirm",
                    targetUser.Username, targetUser.Discriminator, targetUser.ID, targetUser.ID, reasonText,
                ), "✅", "🚫") == true {
                    // Save to log DB
                    troublemakerLogEntry := t.getEntryByOrCreateEmpty("id", "")
                    troublemakerLogEntry.UserID = targetUser.ID
                    troublemakerLogEntry.Reason = reasonText
                    troublemakerLogEntry.CreatedAt = time.Now()
                    troublemakerLogEntry.ReportedByGuildID = guild.ID
                    troublemakerLogEntry.ReportedByUserID = msg.Author.ID
                    t.setEntry(troublemakerLogEntry)

                    logger.INFO.L("troublemaker", fmt.Sprintf("will notify about troublemaker %s (#%s) by %s (#%s) on %s (#%s) reason %s",
                        targetUser.Username, targetUser.ID,
                        msg.Author.Username, msg.Author.ID,
                        guild.Name, guild.ID,
                        reasonText,
                    ))

                    guildsToNotify := make([]*discordgo.Guild, 0)

                    for _, guildToNotify := range session.State.Guilds {
                        if guildToNotify.ID != guild.ID {
                            guildToNotifySettings := helpers.GuildSettingsGetCached(guildToNotify.ID)
                            if guildToNotifySettings.TroublemakerIsParticipating == true && guildToNotifySettings.TroublemakerLogChannel != "" {
                                guildsToNotify = append(guildsToNotify, guildToNotify)
                            }
                        }
                    }

                    successMessage, _ := session.ChannelMessageSend(msg.ChannelID, helpers.GetTextF("plugins.troublemaker.report-successful", len(guildsToNotify)))

                    // Send notifications out
                    go func() {

                        for _, guildToNotify := range guildsToNotify {
                            guildToNotifySettings := helpers.GuildSettingsGetCached(guildToNotify.ID)
                            if guildToNotifySettings.TroublemakerIsParticipating == true && guildToNotifySettings.TroublemakerLogChannel != "" {
                                targetUserIsOnServer := false
                                _, err := helpers.GetGuildMember(guildToNotify.ID, targetUser.ID)
                                if err == nil {
                                    targetUserIsOnServer = true
                                }

                                reportEmbed := &discordgo.MessageEmbed{
                                    Title: helpers.GetTextF("plugins.troublemaker.report-embed-title", targetUser.Username, targetUser.Discriminator),
                                    Description: helpers.GetTextF("plugins.troublemaker.report-embed-description", targetUser.ID, targetUser.ID),
                                    URL:       helpers.GetAvatarUrl(targetUser),
                                    Thumbnail: &discordgo.MessageEmbedThumbnail{URL: helpers.GetAvatarUrl(targetUser)},
                                    Footer:    &discordgo.MessageEmbedFooter{Text: helpers.GetTextF("plugins.troublemaker.report-embed-footer", len(guildsToNotify))},
                                    Color:     0x0FADED,
                                    Fields: []*discordgo.MessageEmbedField{
                                        {Name: "Reason stated", Value: reasonText, Inline: false},
                                    },
                                }

                                if targetUserIsOnServer == true {
                                    reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                        Name: "Member status", Value: "⚠ User is on this server", Inline: false,
                                    })
                                } else {
                                    reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                        Name: "Member status", Value: "✅ User is not on this server", Inline: false,
                                    })
                                }

                                reportEmbed.Fields = append(reportEmbed.Fields, &discordgo.MessageEmbedField{
                                    Name: "Reported by", Value: fmt.Sprintf("**%s** (#%s) <@%s>\non **%s** (#%s)",
                                    msg.Author.Username, msg.Author.ID, msg.Author.ID, guild.Name, guild.ID,
                                ), Inline: false})

                                _, err = session.ChannelMessageSendEmbed(guildToNotifySettings.TroublemakerLogChannel, reportEmbed)
                                if err != nil {
                                    logger.ERROR.L("troublemaker", fmt.Sprintf("Failed to send troublemaker report to channel #%s on guild #%s: %s" +
                                        guildToNotifySettings.TroublemakerLogChannel, guildToNotifySettings.Guild, err.Error()))
                                }
                            }
                        }

                        if successMessage.ID != "" {
                            session.MessageReactionAdd(msg.ChannelID, successMessage.ID, "👌")
                        }
                        return
                    }()
                    return
                }
                return
            })
            break
        }
    }
}

func (t *Troublemaker) getTroublemakerReports(user *discordgo.User) []DB_Troublemaker_Entry {
    var entryBucket []DB_Troublemaker_Entry
    listCursor, err := rethink.Table("troublemakerlog").Filter(
        rethink.Row.Field("userid").Eq(user.ID),
    ).Run(helpers.GetDB())
    helpers.Relax(err)
    defer listCursor.Close()
    listCursor.All(&entryBucket)

    return entryBucket
}

func (t *Troublemaker) getEntryByOrCreateEmpty(key string, id string) DB_Troublemaker_Entry {
    var entryBucket DB_Troublemaker_Entry
    listCursor, err := rethink.Table("troublemakerlog").Filter(
        rethink.Row.Field(key).Eq(id),
    ).Run(helpers.GetDB())
    defer listCursor.Close()
    err = listCursor.One(&entryBucket)

    // If user has no DB entries create an empty document
    if err == rethink.ErrEmptyResult {
        insert := rethink.Table("troublemakerlog").Insert(DB_Troublemaker_Entry{})
        res, e := insert.RunWrite(helpers.GetDB())
        // If the creation was successful read the document
        if e != nil {
            panic(e)
        } else {
            return t.getEntryByOrCreateEmpty("id", res.GeneratedKeys[0])
        }
    } else if err != nil {
        panic(err)
    }

    return entryBucket
}

func (t *Troublemaker) setEntry(entry DB_Troublemaker_Entry) {
    _, err := rethink.Table("troublemakerlog").Update(entry).Run(helpers.GetDB())
    helpers.Relax(err)
}
<|endoftext|>


<commit_before>package plugins

import (
	"strings"

	"net/url"

	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/go-wolfram"
	"github.com/bwmarrin/discordgo"
)

type WolframAlpha struct{}

func (m *WolframAlpha) Commands() []string {
	return []string{
		"wolfram",
		"w",
		"ask",
	}
}

func (m *WolframAlpha) Init(session *discordgo.Session) {

}

func (m *WolframAlpha) TypingLoop(channelID string, quitChannel chan int) {
	for {
		select {
		case <-quitChannel:
			return
		default:
			cache.GetSession().ChannelTyping(channelID)
			time.Sleep(5 * time.Second)
		}
	}
}

func (m *WolframAlpha) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	quitChannel := make(chan int)
	defer func() { quitChannel <- 0 }()

	go m.TypingLoop(msg.ChannelID, quitChannel)

	wolframClient := &wolfram.Client{AppID: helpers.GetConfig().Path("wolframalpha.appid").Data().(string)}

	var res string
	var imageSearch bool
	if strings.HasPrefix(content, "image ") || strings.HasPrefix(content, "img ") {
		content = strings.TrimLeft(content, "image ")
		content = strings.TrimLeft(content, "img ")
		content = strings.TrimSpace(content)

		imageSearch = true
	}

	res, err := wolframClient.GetShortAnswerQuery(content, wolfram.Metric, 10)
	helpers.Relax(err)

	if res == "No short answer available" {
		imageSearch = true
	}

	if imageSearch {
		urlValues := url.Values{}
		urlValues.Add("foreground", "white")
		urlValues.Add("background", "35393E")
		urlValues.Add("layout", "labelbar")
		urlValues.Add("timeout", "30")

		image, _, err := wolframClient.GetSimpleQuery(content, urlValues)
		helpers.Relax(err)

		_, err = helpers.SendComplex(
			msg.ChannelID, &discordgo.MessageSend{
				Files: []*discordgo.File{
					{
						Name:   "wolframalpha-robyul.png",
						Reader: image,
					},
				},
			})
		helpers.RelaxEmbed(err, msg.ChannelID, msg.ID)
		return
	}

	if res == "" || res == "Wolfram|Alpha did not understand your input" {
		helpers.SendMessage(msg.ChannelID, helpers.GetText("plugins.wolframalpha.error"))
		return
	}

	_, err = helpers.SendMessage(msg.ChannelID, res)
	helpers.RelaxMessage(err, msg.ChannelID, msg.ID)
}
<commit_msg>[wolframalpha] switches to white background, cleanup<commit_after>package plugins

import (
	"strings"

	"net/url"

	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/metrics"
	"github.com/Seklfreak/go-wolfram"
	"github.com/bwmarrin/discordgo"
)

type WolframAlpha struct{}

func (m *WolframAlpha) Commands() []string {
	return []string{
		"wolfram",
		"w",
		"ask",
	}
}

func (m *WolframAlpha) Init(session *discordgo.Session) {

}

func (m *WolframAlpha) TypingLoop(channelID string, quitChannel chan int) {
	for {
		select {
		case <-quitChannel:
			return
		default:
			cache.GetSession().ChannelTyping(channelID)
			time.Sleep(5 * time.Second)
		}
	}
}

func (m *WolframAlpha) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	quitChannel := make(chan int)
	defer func() { quitChannel <- 0 }()

	go m.TypingLoop(msg.ChannelID, quitChannel)

	wolframClient := &wolfram.Client{AppID: helpers.GetConfig().Path("wolframalpha.appid").Data().(string)}

	var err error
	var res string
	var imageSearch bool
	if strings.HasPrefix(content, "image ") || strings.HasPrefix(content, "img ") {
		content = strings.TrimLeft(content, "image ")
		content = strings.TrimLeft(content, "img ")
		content = strings.TrimSpace(content)

		imageSearch = true
	}

	if !imageSearch {
		metrics.WolframAlphaRequests.Add(1)
		res, err = wolframClient.GetShortAnswerQuery(content, wolfram.Metric, 10)
		helpers.Relax(err)

		if res == "No short answer available" {
			imageSearch = true
		}
	}

	if imageSearch {
		urlValues := url.Values{}
		//urlValues.Add("foreground", "white")
		//urlValues.Add("background", "35393E")
		urlValues.Add("layout", "labelbar")
		urlValues.Add("timeout", "30")

		metrics.WolframAlphaRequests.Add(1)
		image, _, err := wolframClient.GetSimpleQuery(content, urlValues)
		helpers.Relax(err)

		_, err = helpers.SendComplex(
			msg.ChannelID, &discordgo.MessageSend{
				Files: []*discordgo.File{
					{
						Name:   "wolframalpha-robyul.png",
						Reader: image,
					},
				},
			})
		helpers.RelaxEmbed(err, msg.ChannelID, msg.ID)
		return
	}

	if res == "" || res == "Wolfram|Alpha did not understand your input" {
		helpers.SendMessage(msg.ChannelID, helpers.GetText("plugins.wolframalpha.error"))
		return
	}

	_, err = helpers.SendMessage(msg.ChannelID, res)
	helpers.RelaxMessage(err, msg.ChannelID, msg.ID)
}
<|endoftext|>


<commit_before>// Package hostdb provides a HostDB object that implements the renter.hostDB
// interface. The blockchain is scanned for host announcements and hosts that
// are found get added to the host database. The database continually scans the
// set of hosts it has found and updates who is online.
package hostdb

// TODO: There should be some mechanism that detects if the number of active
// hosts is low. Then either the user can be informed, or the hostdb can start
// scanning hosts that have been offline for a while and are no longer
// prioritized by the scan loop.

// TODO: There should be some mechanism for detecting if the hostdb cannot
// connect to the internet. If it cannot, hosts should not be penalized for
// appearing to be offline, because they may not actually be offline and it'll
// unfairly over-penalize the hosts with the highest uptime.

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/renter/hostdb/hosttree"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// scanPoolSize sets the buffer size of the channel that holds hosts which
	// need to be scanned. A thread pool pulls from the scan pool to query
	// hosts that are due for an update.
	scanPoolSize = 1000
)

var (
	errNilCS = errors.New("cannot create hostdb with nil consensus set")
)

// The HostDB is a database of potential hosts. It assigns a weight to each
// host based on their hosting parameters, and then can select hosts at random
// for uploading files.
type HostDB struct {
	// dependencies
	dialer  dialer
	log     *persist.Logger
	mu      sync.RWMutex
	persist persister
	sleeper sleeper
	tg      siasync.ThreadGroup

	// The hostTree is the root node of the tree that organizes hosts by
	// weight. The tree is necessary for selecting weighted hosts at
	// random. 'activeHosts' provides a lookup from hostname to the the
	// corresponding node, as the hostTree is unsorted. A host is active if
	// it is currently responding to queries about price and other
	// settings.
	hostTree    *hosttree.HostTree
	activeHosts map[modules.NetAddress]*hostEntry

	// allHosts is a simple list of all known hosts by their network address,
	// including hosts that are currently offline.
	allHosts map[modules.NetAddress]*hostEntry

	// the scanPool is a set of hosts that need to be scanned. There are a
	// handful of goroutines constantly waiting on the channel for hosts to
	// scan.
	scanList []*hostEntry
	scanPool chan *hostEntry
	scanWait bool

	blockHeight types.BlockHeight
	lastChange  modules.ConsensusChangeID
}

// New returns a new HostDB.
func New(cs consensusSet, persistDir string) (*HostDB, error) {
	// Check for nil inputs.
	if cs == nil {
		return nil, errNilCS
	}

	// Create the persist directory if it does not yet exist.
	err := os.MkdirAll(persistDir, 0700)
	if err != nil {
		return nil, err
	}
	// Create the logger.
	logger, err := persist.NewFileLogger(filepath.Join(persistDir, "hostdb.log"))
	if err != nil {
		return nil, err
	}

	// Create HostDB using production dependencies.
	return newHostDB(cs, stdDialer{}, stdSleeper{}, newPersist(persistDir), logger)
}

// newHostDB creates a HostDB using the provided dependencies. It loads the old
// persistence data, spawns the HostDB's scanning threads, and subscribes it to
// the consensusSet.
func newHostDB(cs consensusSet, d dialer, s sleeper, p persister, l *persist.Logger) (*HostDB, error) {
	// Create the HostDB object.
	hdb := &HostDB{
		dialer:  d,
		sleeper: s,
		persist: p,
		log:     l,

		// TODO: should index by pubkey, not ip
		activeHosts: make(map[modules.NetAddress]*hostEntry),
		allHosts:    make(map[modules.NetAddress]*hostEntry),
		scanPool:    make(chan *hostEntry, scanPoolSize),
	}

	hdb.hostTree = hosttree.New(hdb.calculateHostWeight())

	// Load the prior persistence structures.
	err := hdb.load()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	err = cs.ConsensusSetSubscribe(hdb, hdb.lastChange)
	if err == modules.ErrInvalidConsensusChangeID {
		hdb.lastChange = modules.ConsensusChangeBeginning
		// clear the host sets
		hdb.activeHosts = make(map[modules.NetAddress]*hostEntry)
		hdb.allHosts = make(map[modules.NetAddress]*hostEntry)
		// subscribe again using the new ID
		err = cs.ConsensusSetSubscribe(hdb, hdb.lastChange)
	}
	if err != nil {
		return nil, errors.New("hostdb subscription failed: " + err.Error())
	}

	// Spin up the host scanning processes.
	for i := 0; i < scanningThreads; i++ {
		go hdb.threadedProbeHosts()
	}
	go hdb.threadedScan()

	return hdb, nil
}

// Close closes the hostdb, terminating its scanning threads
func (hdb *HostDB) Close() error {
	return hdb.tg.Stop()
}

func (hdb *HostDB) RandomHosts(n int, exclude []modules.NetAddress) []modules.HostDBEntry {
	// Convert exclusion netaddresses to public keys
	var excludeKeys []types.SiaPublicKey
	for _, addr := range exclude {
		entry, exists := hdb.activeHosts[addr]
		if exists {
			excludeKeys = append(excludeKeys, entry.HostDBEntry.PublicKey)
		}
	}

	hosts, err := hdb.hostTree.SelectRandom(n, excludeKeys)
	if err != nil {
		// TODO: handle this err
	}
	return hosts
}
<commit_msg>add RandomHosts docstrings, debug logging<commit_after>// Package hostdb provides a HostDB object that implements the renter.hostDB
// interface. The blockchain is scanned for host announcements and hosts that
// are found get added to the host database. The database continually scans the
// set of hosts it has found and updates who is online.
package hostdb

// TODO: There should be some mechanism that detects if the number of active
// hosts is low. Then either the user can be informed, or the hostdb can start
// scanning hosts that have been offline for a while and are no longer
// prioritized by the scan loop.

// TODO: There should be some mechanism for detecting if the hostdb cannot
// connect to the internet. If it cannot, hosts should not be penalized for
// appearing to be offline, because they may not actually be offline and it'll
// unfairly over-penalize the hosts with the highest uptime.

import (
	"errors"
	"os"
	"path/filepath"
	"sync"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/renter/hostdb/hosttree"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// scanPoolSize sets the buffer size of the channel that holds hosts which
	// need to be scanned. A thread pool pulls from the scan pool to query
	// hosts that are due for an update.
	scanPoolSize = 1000
)

var (
	errNilCS = errors.New("cannot create hostdb with nil consensus set")
)

// The HostDB is a database of potential hosts. It assigns a weight to each
// host based on their hosting parameters, and then can select hosts at random
// for uploading files.
type HostDB struct {
	// dependencies
	dialer  dialer
	log     *persist.Logger
	mu      sync.RWMutex
	persist persister
	sleeper sleeper
	tg      siasync.ThreadGroup

	// The hostTree is the root node of the tree that organizes hosts by
	// weight. The tree is necessary for selecting weighted hosts at
	// random. 'activeHosts' provides a lookup from hostname to the the
	// corresponding node, as the hostTree is unsorted. A host is active if
	// it is currently responding to queries about price and other
	// settings.
	hostTree    *hosttree.HostTree
	activeHosts map[modules.NetAddress]*hostEntry

	// allHosts is a simple list of all known hosts by their network address,
	// including hosts that are currently offline.
	allHosts map[modules.NetAddress]*hostEntry

	// the scanPool is a set of hosts that need to be scanned. There are a
	// handful of goroutines constantly waiting on the channel for hosts to
	// scan.
	scanList []*hostEntry
	scanPool chan *hostEntry
	scanWait bool

	blockHeight types.BlockHeight
	lastChange  modules.ConsensusChangeID
}

// New returns a new HostDB.
func New(cs consensusSet, persistDir string) (*HostDB, error) {
	// Check for nil inputs.
	if cs == nil {
		return nil, errNilCS
	}

	// Create the persist directory if it does not yet exist.
	err := os.MkdirAll(persistDir, 0700)
	if err != nil {
		return nil, err
	}
	// Create the logger.
	logger, err := persist.NewFileLogger(filepath.Join(persistDir, "hostdb.log"))
	if err != nil {
		return nil, err
	}

	// Create HostDB using production dependencies.
	return newHostDB(cs, stdDialer{}, stdSleeper{}, newPersist(persistDir), logger)
}

// newHostDB creates a HostDB using the provided dependencies. It loads the old
// persistence data, spawns the HostDB's scanning threads, and subscribes it to
// the consensusSet.
func newHostDB(cs consensusSet, d dialer, s sleeper, p persister, l *persist.Logger) (*HostDB, error) {
	// Create the HostDB object.
	hdb := &HostDB{
		dialer:  d,
		sleeper: s,
		persist: p,
		log:     l,

		// TODO: should index by pubkey, not ip
		activeHosts: make(map[modules.NetAddress]*hostEntry),
		allHosts:    make(map[modules.NetAddress]*hostEntry),
		scanPool:    make(chan *hostEntry, scanPoolSize),
	}

	hdb.hostTree = hosttree.New(hdb.calculateHostWeight())

	// Load the prior persistence structures.
	err := hdb.load()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	err = cs.ConsensusSetSubscribe(hdb, hdb.lastChange)
	if err == modules.ErrInvalidConsensusChangeID {
		hdb.lastChange = modules.ConsensusChangeBeginning
		// clear the host sets
		hdb.activeHosts = make(map[modules.NetAddress]*hostEntry)
		hdb.allHosts = make(map[modules.NetAddress]*hostEntry)
		// subscribe again using the new ID
		err = cs.ConsensusSetSubscribe(hdb, hdb.lastChange)
	}
	if err != nil {
		return nil, errors.New("hostdb subscription failed: " + err.Error())
	}

	// Spin up the host scanning processes.
	for i := 0; i < scanningThreads; i++ {
		go hdb.threadedProbeHosts()
	}
	go hdb.threadedScan()

	return hdb, nil
}

// Close closes the hostdb, terminating its scanning threads
func (hdb *HostDB) Close() error {
	return hdb.tg.Stop()
}

// RandomHosts implements the HostDB interface's RandomHosts() method. It takes
// a number of hosts to return, and a slice of netaddresses to ignore, and
// returns a slice of entries.
func (hdb *HostDB) RandomHosts(n int, exclude []modules.NetAddress) []modules.HostDBEntry {
	// The HostTree uses public keys: we have to convert the exclusion netaddress
	// to keys using activeHosts first. This will go away once the HostDB is
	// transitioned to using public keys.
	var excludeKeys []types.SiaPublicKey
	for _, addr := range exclude {
		entry, exists := hdb.activeHosts[addr]
		if exists {
			excludeKeys = append(excludeKeys, entry.HostDBEntry.PublicKey)
		}
	}

	hosts, err := hdb.hostTree.SelectRandom(n, excludeKeys)
	if err != nil {
		hdb.log.Debugln("error selecting random hosts from the tree: ", err)
	}
	return hosts
}
<|endoftext|>


<commit_before>/* Copyright 2018 The Bazel Authors. All rights reserved.

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

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

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

package proto

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/bazelbuild/bazel-gazelle/config"
	"github.com/bazelbuild/bazel-gazelle/language"
	"github.com/bazelbuild/bazel-gazelle/rule"
)

func (_ *protoLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
	c := args.Config
	pc := GetProtoConfig(c)
	if !pc.Mode.ShouldGenerateRules() {
		// Don't create or delete proto rules in this mode. Any existing rules
		// are likely hand-written.
		return language.GenerateResult{}
	}

	var regularProtoFiles []string
	for _, name := range args.RegularFiles {
		if strings.HasSuffix(name, ".proto") {
			regularProtoFiles = append(regularProtoFiles, name)
		}
	}

	// Some of the generated files may have been consumed by other rules
	consumedFileSet := make(map[string]bool)
	for _, r := range args.OtherGen {
		if r.Kind() != "proto_library" {
			continue
		}
		for _, f := range r.AttrStrings("srcs") {
			consumedFileSet[f] = true
		}
	}

	// genProtoFilesNotConsumed represents only not consumed generetad files.
	// genProtoFiles represents all generated files.
	// This is required for not generating empty rules for consumed generated
	// files.
	var genProtoFiles, genProtoFilesNotConsumed []string
	for _, name := range args.GenFiles {
		if strings.HasSuffix(name, ".proto") {
			genProtoFiles = append(genProtoFiles, name)
			if !consumedFileSet[name] {
				genProtoFilesNotConsumed = append(genProtoFilesNotConsumed, name)
			}
		}
	}
	pkgs := buildPackages(pc, args.Dir, args.Rel, regularProtoFiles, genProtoFilesNotConsumed)
	shouldSetVisibility := args.File == nil || !args.File.HasDefaultVisibility()
	var res language.GenerateResult
	for _, pkg := range pkgs {
		r := generateProto(pc, args.Rel, pkg, shouldSetVisibility)
		if r.IsEmpty(protoKinds[r.Kind()]) {
			res.Empty = append(res.Empty, r)
		} else {
			res.Gen = append(res.Gen, r)
		}
	}
	sort.SliceStable(res.Gen, func(i, j int) bool {
		return res.Gen[i].Name() < res.Gen[j].Name()
	})
	res.Imports = make([]interface{}, len(res.Gen))
	for i, r := range res.Gen {
		res.Imports[i] = r.PrivateAttr(config.GazelleImportsKey)
	}
	res.Empty = append(res.Empty, generateEmpty(args.File, regularProtoFiles, genProtoFiles)...)
	return res
}

// RuleName returns a name for a proto_library derived from the given strings.
// For each string, RuleName will look for a non-empty suffix of identifier
// characters and then append "_proto" to that.
func RuleName(names ...string) string {
	base := "root"
	for _, name := range names {
		notIdent := func(c rune) bool {
			return !('A' <= c && c <= 'Z' ||
				'a' <= c && c <= 'z' ||
				'0' <= c && c <= '9' ||
				c == '_')
		}
		if i := strings.LastIndexFunc(name, notIdent); i >= 0 {
			name = name[i+1:]
		}
		if name != "" {
			base = name
			break
		}
	}
	return base + "_proto"
}

// buildPackage extracts metadata from the .proto files in a directory and
// constructs possibly several packages, then selects a package to generate
// a proto_library rule for.
func buildPackages(pc *ProtoConfig, dir, rel string, protoFiles, genFiles []string) []*Package {
	packageMap := make(map[string]*Package)
	for _, name := range protoFiles {
		info := protoFileInfo(dir, name)
		key := info.PackageName
		if pc.groupOption != "" {
			for _, opt := range info.Options {
				if opt.Key == pc.groupOption {
					key = opt.Value
					break
				}
			}
		}
		if packageMap[key] == nil {
			packageMap[key] = newPackage(info.PackageName)
		}
		packageMap[key].addFile(info)
	}

	switch pc.Mode {
	case DefaultMode:
		pkg, err := selectPackage(dir, rel, packageMap)
		if err != nil {
			log.Print(err)
		}
		if pkg == nil {
			return nil // empty rule created in generateEmpty
		}
		for _, name := range genFiles {
			pkg.addGenFile(dir, name)
		}
		return []*Package{pkg}

	case PackageMode:
		pkgs := make([]*Package, 0, len(packageMap))
		for _, pkg := range packageMap {
			pkgs = append(pkgs, pkg)
		}
		return pkgs

	default:
		return nil
	}
}

// selectPackage chooses a package to generate rules for.
func selectPackage(dir, rel string, packageMap map[string]*Package) (*Package, error) {
	if len(packageMap) == 0 {
		return nil, nil
	}
	if len(packageMap) == 1 {
		for _, pkg := range packageMap {
			return pkg, nil
		}
	}
	defaultPackageName := strings.Replace(rel, "/", "_", -1)
	for _, pkg := range packageMap {
		if pkgName := goPackageName(pkg); pkgName != "" && pkgName == defaultPackageName {
			return pkg, nil
		}
	}
	return nil, fmt.Errorf("%s: directory contains multiple proto packages. Gazelle can only generate a proto_library for one package.", dir)
}

// goPackageName guesses the identifier in package declarations at the top of
// the .pb.go files that will be generated for this package. "" is returned
// if the package name cannot be determined.
//
// TODO(jayconrod): remove all Go-specific functionality. This is here
// temporarily for compatibility.
func goPackageName(pkg *Package) string {
	if opt, ok := pkg.Options["go_package"]; ok {
		if i := strings.IndexByte(opt, ';'); i >= 0 {
			return opt[i+1:]
		} else if i := strings.LastIndexByte(opt, '/'); i >= 0 {
			return opt[i+1:]
		} else {
			return opt
		}
	}
	if pkg.Name != "" {
		return strings.Replace(pkg.Name, ".", "_", -1)
	}
	if len(pkg.Files) == 1 {
		for s := range pkg.Files {
			return strings.TrimSuffix(s, ".proto")
		}
	}
	return ""
}

// generateProto creates a new proto_library rule for a package. The rule may
// be empty if there are no sources.
func generateProto(pc *ProtoConfig, rel string, pkg *Package, shouldSetVisibility bool) *rule.Rule {
	var name string
	if pc.Mode == DefaultMode {
		name = RuleName(goPackageName(pkg), pc.GoPrefix, rel)
	} else {
		name = RuleName(pkg.Options[pc.groupOption], pkg.Name, rel)
	}
	r := rule.NewRule("proto_library", name)
	srcs := make([]string, 0, len(pkg.Files))
	for f := range pkg.Files {
		srcs = append(srcs, f)
	}
	sort.Strings(srcs)
	if len(srcs) > 0 {
		r.SetAttr("srcs", srcs)
	}
	r.SetPrivateAttr(PackageKey, *pkg)
	imports := make([]string, 0, len(pkg.Imports))
	for i := range pkg.Imports {
		imports = append(imports, i)
	}
	sort.Strings(imports)
	// NOTE: This attribute should not be used outside this extension. It's still
	// convenient for testing though.
	r.SetPrivateAttr(config.GazelleImportsKey, imports)
	for k, v := range pkg.Options {
		r.SetPrivateAttr(k, v)
	}
	if shouldSetVisibility {
		vis := rule.CheckInternalVisibility(rel, "//visibility:public")
		r.SetAttr("visibility", []string{vis})
	}
	if pc.StripImportPrefix != "" {
		r.SetAttr("strip_import_prefix", pc.StripImportPrefix)
	}
	if pc.ImportPrefix != "" {
		r.SetAttr("import_prefix", pc.ImportPrefix)
	}
	return r
}

// generateEmpty generates a list of proto_library rules that may be deleted.
// This is generated from existing proto_library rules with srcs lists that
// don't match any static or generated files.
func generateEmpty(f *rule.File, regularFiles, genFiles []string) []*rule.Rule {
	if f == nil {
		return nil
	}
	knownFiles := make(map[string]bool)
	for _, f := range regularFiles {
		knownFiles[f] = true
	}
	for _, f := range genFiles {
		knownFiles[f] = true
	}
	var empty []*rule.Rule
outer:
	for _, r := range f.Rules {
		if r.Kind() != "proto_library" {
			continue
		}
		srcs := r.AttrStrings("srcs")
		if len(srcs) == 0 && r.Attr("srcs") != nil {
			// srcs is not a string list; leave it alone
			continue
		}
		for _, src := range r.AttrStrings("srcs") {
			if knownFiles[src] {
				continue outer
			}
		}
		empty = append(empty, rule.NewRule("proto_library", r.Name()))
	}
	return empty
}
<commit_msg>fix typo (#1104)<commit_after>/* Copyright 2018 The Bazel Authors. All rights reserved.

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

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

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

package proto

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/bazelbuild/bazel-gazelle/config"
	"github.com/bazelbuild/bazel-gazelle/language"
	"github.com/bazelbuild/bazel-gazelle/rule"
)

func (_ *protoLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
	c := args.Config
	pc := GetProtoConfig(c)
	if !pc.Mode.ShouldGenerateRules() {
		// Don't create or delete proto rules in this mode. Any existing rules
		// are likely hand-written.
		return language.GenerateResult{}
	}

	var regularProtoFiles []string
	for _, name := range args.RegularFiles {
		if strings.HasSuffix(name, ".proto") {
			regularProtoFiles = append(regularProtoFiles, name)
		}
	}

	// Some of the generated files may have been consumed by other rules
	consumedFileSet := make(map[string]bool)
	for _, r := range args.OtherGen {
		if r.Kind() != "proto_library" {
			continue
		}
		for _, f := range r.AttrStrings("srcs") {
			consumedFileSet[f] = true
		}
	}

	// genProtoFilesNotConsumed represents only not consumed generated files.
	// genProtoFiles represents all generated files.
	// This is required for not generating empty rules for consumed generated
	// files.
	var genProtoFiles, genProtoFilesNotConsumed []string
	for _, name := range args.GenFiles {
		if strings.HasSuffix(name, ".proto") {
			genProtoFiles = append(genProtoFiles, name)
			if !consumedFileSet[name] {
				genProtoFilesNotConsumed = append(genProtoFilesNotConsumed, name)
			}
		}
	}
	pkgs := buildPackages(pc, args.Dir, args.Rel, regularProtoFiles, genProtoFilesNotConsumed)
	shouldSetVisibility := args.File == nil || !args.File.HasDefaultVisibility()
	var res language.GenerateResult
	for _, pkg := range pkgs {
		r := generateProto(pc, args.Rel, pkg, shouldSetVisibility)
		if r.IsEmpty(protoKinds[r.Kind()]) {
			res.Empty = append(res.Empty, r)
		} else {
			res.Gen = append(res.Gen, r)
		}
	}
	sort.SliceStable(res.Gen, func(i, j int) bool {
		return res.Gen[i].Name() < res.Gen[j].Name()
	})
	res.Imports = make([]interface{}, len(res.Gen))
	for i, r := range res.Gen {
		res.Imports[i] = r.PrivateAttr(config.GazelleImportsKey)
	}
	res.Empty = append(res.Empty, generateEmpty(args.File, regularProtoFiles, genProtoFiles)...)
	return res
}

// RuleName returns a name for a proto_library derived from the given strings.
// For each string, RuleName will look for a non-empty suffix of identifier
// characters and then append "_proto" to that.
func RuleName(names ...string) string {
	base := "root"
	for _, name := range names {
		notIdent := func(c rune) bool {
			return !('A' <= c && c <= 'Z' ||
				'a' <= c && c <= 'z' ||
				'0' <= c && c <= '9' ||
				c == '_')
		}
		if i := strings.LastIndexFunc(name, notIdent); i >= 0 {
			name = name[i+1:]
		}
		if name != "" {
			base = name
			break
		}
	}
	return base + "_proto"
}

// buildPackage extracts metadata from the .proto files in a directory and
// constructs possibly several packages, then selects a package to generate
// a proto_library rule for.
func buildPackages(pc *ProtoConfig, dir, rel string, protoFiles, genFiles []string) []*Package {
	packageMap := make(map[string]*Package)
	for _, name := range protoFiles {
		info := protoFileInfo(dir, name)
		key := info.PackageName
		if pc.groupOption != "" {
			for _, opt := range info.Options {
				if opt.Key == pc.groupOption {
					key = opt.Value
					break
				}
			}
		}
		if packageMap[key] == nil {
			packageMap[key] = newPackage(info.PackageName)
		}
		packageMap[key].addFile(info)
	}

	switch pc.Mode {
	case DefaultMode:
		pkg, err := selectPackage(dir, rel, packageMap)
		if err != nil {
			log.Print(err)
		}
		if pkg == nil {
			return nil // empty rule created in generateEmpty
		}
		for _, name := range genFiles {
			pkg.addGenFile(dir, name)
		}
		return []*Package{pkg}

	case PackageMode:
		pkgs := make([]*Package, 0, len(packageMap))
		for _, pkg := range packageMap {
			pkgs = append(pkgs, pkg)
		}
		return pkgs

	default:
		return nil
	}
}

// selectPackage chooses a package to generate rules for.
func selectPackage(dir, rel string, packageMap map[string]*Package) (*Package, error) {
	if len(packageMap) == 0 {
		return nil, nil
	}
	if len(packageMap) == 1 {
		for _, pkg := range packageMap {
			return pkg, nil
		}
	}
	defaultPackageName := strings.Replace(rel, "/", "_", -1)
	for _, pkg := range packageMap {
		if pkgName := goPackageName(pkg); pkgName != "" && pkgName == defaultPackageName {
			return pkg, nil
		}
	}
	return nil, fmt.Errorf("%s: directory contains multiple proto packages. Gazelle can only generate a proto_library for one package.", dir)
}

// goPackageName guesses the identifier in package declarations at the top of
// the .pb.go files that will be generated for this package. "" is returned
// if the package name cannot be determined.
//
// TODO(jayconrod): remove all Go-specific functionality. This is here
// temporarily for compatibility.
func goPackageName(pkg *Package) string {
	if opt, ok := pkg.Options["go_package"]; ok {
		if i := strings.IndexByte(opt, ';'); i >= 0 {
			return opt[i+1:]
		} else if i := strings.LastIndexByte(opt, '/'); i >= 0 {
			return opt[i+1:]
		} else {
			return opt
		}
	}
	if pkg.Name != "" {
		return strings.Replace(pkg.Name, ".", "_", -1)
	}
	if len(pkg.Files) == 1 {
		for s := range pkg.Files {
			return strings.TrimSuffix(s, ".proto")
		}
	}
	return ""
}

// generateProto creates a new proto_library rule for a package. The rule may
// be empty if there are no sources.
func generateProto(pc *ProtoConfig, rel string, pkg *Package, shouldSetVisibility bool) *rule.Rule {
	var name string
	if pc.Mode == DefaultMode {
		name = RuleName(goPackageName(pkg), pc.GoPrefix, rel)
	} else {
		name = RuleName(pkg.Options[pc.groupOption], pkg.Name, rel)
	}
	r := rule.NewRule("proto_library", name)
	srcs := make([]string, 0, len(pkg.Files))
	for f := range pkg.Files {
		srcs = append(srcs, f)
	}
	sort.Strings(srcs)
	if len(srcs) > 0 {
		r.SetAttr("srcs", srcs)
	}
	r.SetPrivateAttr(PackageKey, *pkg)
	imports := make([]string, 0, len(pkg.Imports))
	for i := range pkg.Imports {
		imports = append(imports, i)
	}
	sort.Strings(imports)
	// NOTE: This attribute should not be used outside this extension. It's still
	// convenient for testing though.
	r.SetPrivateAttr(config.GazelleImportsKey, imports)
	for k, v := range pkg.Options {
		r.SetPrivateAttr(k, v)
	}
	if shouldSetVisibility {
		vis := rule.CheckInternalVisibility(rel, "//visibility:public")
		r.SetAttr("visibility", []string{vis})
	}
	if pc.StripImportPrefix != "" {
		r.SetAttr("strip_import_prefix", pc.StripImportPrefix)
	}
	if pc.ImportPrefix != "" {
		r.SetAttr("import_prefix", pc.ImportPrefix)
	}
	return r
}

// generateEmpty generates a list of proto_library rules that may be deleted.
// This is generated from existing proto_library rules with srcs lists that
// don't match any static or generated files.
func generateEmpty(f *rule.File, regularFiles, genFiles []string) []*rule.Rule {
	if f == nil {
		return nil
	}
	knownFiles := make(map[string]bool)
	for _, f := range regularFiles {
		knownFiles[f] = true
	}
	for _, f := range genFiles {
		knownFiles[f] = true
	}
	var empty []*rule.Rule
outer:
	for _, r := range f.Rules {
		if r.Kind() != "proto_library" {
			continue
		}
		srcs := r.AttrStrings("srcs")
		if len(srcs) == 0 && r.Attr("srcs") != nil {
			// srcs is not a string list; leave it alone
			continue
		}
		for _, src := range r.AttrStrings("srcs") {
			if knownFiles[src] {
				continue outer
			}
		}
		empty = append(empty, rule.NewRule("proto_library", r.Name()))
	}
	return empty
}
<|endoftext|>


<commit_before>// +build integration

package rate

import (
	"testing"

	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/stretchr/testify/assert"
)

func TestRateLimitPluginRedisPolicy(t *testing.T) {
	rawConfig := map[string]interface{}{
		"limit":  "10-S",
		"policy": "redis",
		"redis": map[string]interface{}{
			"dsn":    "localhost",
			"prefix": "test",
		},
	}

	route := proxy.NewRoute(&proxy.Definition{})
	err := setupRateLimit(route, rawConfig)

	assert.Error(t, err)
}
<commit_msg>Fixed tests<commit_after>// +build integration

package rate

import (
	"testing"

	"github.com/hellofresh/janus/pkg/api"
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/stretchr/testify/assert"
)

func TestRateLimitPluginRedisPolicy(t *testing.T) {
	rawConfig := map[string]interface{}{
		"limit":  "10-S",
		"policy": "redis",
		"redis": map[string]interface{}{
			"dsn":    "localhost",
			"prefix": "test",
		},
	}

	def := api.NewDefinition()
	route := proxy.NewRoute(def.Proxy)
	err := setupRateLimit(def, route, rawConfig)

	assert.Error(t, err)
}
<|endoftext|>


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

// Package hipache provides a router implementation that store routes in Redis,
// as specified by Hipache (https://github.com/dotcloud/hipache).
//
// It does not provided any exported type, in order to use the router, you must
// import this package and get the router intance using the function
// router.Get.
//
// In order to use this router, you need to define the "hipache:domain"
// setting.
package hipache

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/router"
)

var conn redis.Conn

var errRouteNotFound = errors.New("Route not found")

func init() {
	router.Register("hipache", hipacheRouter{})
}

func connect() (redis.Conn, error) {
	if conn == nil {
		srv, err := config.GetString("hipache:redis-server")
		if err != nil {
			srv = "localhost:6379"
		}
		conn, err = redis.Dial("tcp", srv)
		if err != nil {
			return nil, err
		}
	}
	return conn, nil
}

type hipacheRouter struct{}

func (hipacheRouter) AddBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"add", err}
	}
	_, err = conn.Do("RPUSH", frontend, name)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("DEL", frontend)
	if err != nil {
		return &routeError{"remove", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

func (r hipacheRouter) AddRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.addRoute(frontend, address); err != nil {
		return &routeError{"add", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	return r.addRoute("frontend:"+cname, address)
}

func (hipacheRouter) addRoute(name, address string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"add", err}
	}
	_, err = conn.Do("RPUSH", name, address)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.removeElement(frontend, address); err != nil {
		return err
	}
	cname, err := r.getCName(name)
	if err != nil {
		return &routeError{"remove", err}
	}
	if cname == "" {
		return nil
	}
	return r.removeElement("frontend:"+cname, address)
}

func (hipacheRouter) getCName(name string) (string, error) {
	conn, err := connect()
	if err != nil {
		return "", &routeError{"getCName", err}
	}
	defer conn.Close()
	cname, err := redis.String(conn.Do("GET", "cname:"+name))
	if err != nil {
		return "", &routeError{"getCName", err}
	}
	return cname, nil
}

func (hipacheRouter) SetCName(cname, name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"addCName", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"addCName", err}
	}
	addresses, err := redis.Strings(conn.Do("LRANGE", frontend, 0, -1))
	if err != nil {
		return &routeError{"get", err}
	}
	_, err = conn.Do("SET", "cname:"+name, cname)
	if err != nil {
		return &routeError{"set", err}
	}
	frontend = "frontend:" + cname
	for _, r := range addresses {
		_, err := conn.Do("RPUSH", frontend, r)
		if err != nil {
			return &routeError{"addCName", err}
		}
	}
	return nil
}

func (r hipacheRouter) UnsetCName(cname, name string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"removeCName", err}
	}
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"removeCName", err}
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"removeCName", err}
	}
	return nil
}

func (hipacheRouter) Addr(name string) (string, error) {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return "", &routeError{"get", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return "", &routeError{"get", err}
	}
	reply, err := conn.Do("LRANGE", frontend, 0, 0)
	if err != nil {
		return "", &routeError{"get", err}
	}
	backends := reply.([]interface{})
	if len(backends) < 1 {
		return "", errRouteNotFound
	}
	return fmt.Sprintf("%s.%s", name, domain), nil
}

func (hipacheRouter) removeElement(name, address string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("LREM", name, 0, address)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

type routeError struct {
	op  string
	err error
}

func (e *routeError) Error() string {
	return fmt.Sprintf("Could not %s route: %s", e.op, e.err)
}
<commit_msg>router/hipache/router: deferring close of redis connections<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package hipache provides a router implementation that store routes in Redis,
// as specified by Hipache (https://github.com/dotcloud/hipache).
//
// It does not provided any exported type, in order to use the router, you must
// import this package and get the router intance using the function
// router.Get.
//
// In order to use this router, you need to define the "hipache:domain"
// setting.
package hipache

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/router"
)

var conn redis.Conn

var errRouteNotFound = errors.New("Route not found")

func init() {
	router.Register("hipache", hipacheRouter{})
}

func connect() (redis.Conn, error) {
	if conn == nil {
		srv, err := config.GetString("hipache:redis-server")
		if err != nil {
			srv = "localhost:6379"
		}
		conn, err = redis.Dial("tcp", srv)
		if err != nil {
			return nil, err
		}
	}
	return conn, nil
}

type hipacheRouter struct{}

func (hipacheRouter) AddBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"add", err}
	}
	defer conn.Close()
	_, err = conn.Do("RPUSH", frontend, name)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"remove", err}
	}
	defer conn.Close()
	_, err = conn.Do("DEL", frontend)
	if err != nil {
		return &routeError{"remove", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

func (r hipacheRouter) AddRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.addRoute(frontend, address); err != nil {
		return &routeError{"add", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	return r.addRoute("frontend:"+cname, address)
}

func (hipacheRouter) addRoute(name, address string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"add", err}
	}
	defer conn.Close()
	_, err = conn.Do("RPUSH", name, address)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.removeElement(frontend, address); err != nil {
		return err
	}
	cname, err := r.getCName(name)
	if err != nil {
		return &routeError{"remove", err}
	}
	if cname == "" {
		return nil
	}
	return r.removeElement("frontend:"+cname, address)
}

func (hipacheRouter) getCName(name string) (string, error) {
	conn, err := connect()
	if err != nil {
		return "", &routeError{"getCName", err}
	}
	defer conn.Close()
	cname, err := redis.String(conn.Do("GET", "cname:"+name))
	if err != nil {
		return "", &routeError{"getCName", err}
	}
	return cname, nil
}

func (hipacheRouter) SetCName(cname, name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"addCName", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return &routeError{"addCName", err}
	}
	defer conn.Close()
	addresses, err := redis.Strings(conn.Do("LRANGE", frontend, 0, -1))
	if err != nil {
		return &routeError{"get", err}
	}
	_, err = conn.Do("SET", "cname:"+name, cname)
	if err != nil {
		return &routeError{"set", err}
	}
	frontend = "frontend:" + cname
	for _, r := range addresses {
		_, err := conn.Do("RPUSH", frontend, r)
		if err != nil {
			return &routeError{"addCName", err}
		}
	}
	return nil
}

func (r hipacheRouter) UnsetCName(cname, name string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"removeCName", err}
	}
	defer conn.Close()
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"removeCName", err}
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"removeCName", err}
	}
	return nil
}

func (hipacheRouter) Addr(name string) (string, error) {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return "", &routeError{"get", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn, err := connect()
	if err != nil {
		return "", &routeError{"get", err}
	}
	defer conn.Close()
	reply, err := conn.Do("LRANGE", frontend, 0, 0)
	if err != nil {
		return "", &routeError{"get", err}
	}
	backends := reply.([]interface{})
	if len(backends) < 1 {
		return "", errRouteNotFound
	}
	return fmt.Sprintf("%s.%s", name, domain), nil
}

func (hipacheRouter) removeElement(name, address string) error {
	conn, err := connect()
	if err != nil {
		return &routeError{"remove", err}
	}
	defer conn.Close()
	_, err = conn.Do("LREM", name, 0, address)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

type routeError struct {
	op  string
	err error
}

func (e *routeError) Error() string {
	return fmt.Sprintf("Could not %s route: %s", e.op, e.err)
}
<|endoftext|>