<commit_before>package main

import (
        "flag"
        "time"
        "log"
	"io"
        "io/ioutil"
	"net/smtp"
	"net/mail"
	"strings"
	"bytes"
	"os"
	"path"
	"path/filepath"
	"encoding/json"
)

type Config struct {
	SpoolDir string
	SmtpAddr string
	SmtpUser string
	SmtpPassword string
	PlainUser string
	PlainPassword string
	MD5User string
	MD5Password string
	SmtpAuth smtp.Auth
	Freq int
	MaxAttempts int
	JsonPath string
}

var config Config

/*
	0 -> just pushed
	1 -> still in progress
	2 -> sent
*/

type SentStatus struct {
	Address string
	Status int
	Attempts int
	NextAttempt time.Time
}

type MailStatus struct {
	From string
	To []*SentStatus
	Cc []*SentStatus
	Bcc []*SentStatus
	Attempts int
	Enqueued time.Time
}

var status map[string]MailStatus

func parse_options() {
	flag.StringVar(&config.SmtpAddr, "smtpaddr", "127.0.0.1:25", "address of the smtp address to use in the addr:port format")
	flag.StringVar(&config.PlainUser, "smtpuser", "", "username for smtp plain authentication")
	flag.StringVar(&config.PlainPassword, "smtppassword", "", "password for smtp plain authentication")
	flag.StringVar(&config.MD5User, "smtpmd5user", "", "username for smtp cram md5 authentication")
	flag.StringVar(&config.MD5Password, "smtpmd5password", "", "password for smtp cram md5 authentication")
	flag.IntVar(&config.Freq, "freq", 10, "frequency of spool directory scans")
	flag.IntVar(&config.MaxAttempts, "attempts", 100, "max attempts for failed SMTP transactions before giving up")
	flag.StringVar(&config.JsonPath, "json", "", "path of the json status file")
	flag.Parse()
	if flag.NArg() < 1 {
		log.Fatal("please specify a spool directory")
	}
	config.SpoolDir = flag.Args()[0]
}

func send_mail(ss *SentStatus, file string, from string, to string, msg *[]byte) {
	log.Println(file,"sending mail to", to, "attempt:", ss.Attempts)
	dest := []string{to}
	err := smtp.SendMail(config.SmtpAddr, config.SmtpAuth, from, dest, *msg)
	if err != nil {
		log.Println(file,"SMTP error, mail to", to, err)
		ss.Status = 0
		ss.Attempts++
		if ss.Attempts >= config.MaxAttempts {
			log.Println(file, "max SMTP attempts reached for",to, "... giving up")
			ss.Status = 2
		}
		if (ss.Attempts > 30) {
			ss.NextAttempt = time.Now().Add(time.Duration(30) * time.Duration(60) * time.Second)
		} else {
			ss.NextAttempt = time.Now().Add(time.Duration(ss.Attempts) * time.Duration(60) * time.Second)
		}
		return
	}
	ss.Status = 2
	log.Println(file, "successfully sent to", to)
}

func read_json(file string) {
	f, err := os.Open(file)
	if err != nil {
		return
	}
	var buffer bytes.Buffer
	_, err = io.Copy(&buffer, f)
	f.Close()
	if err != nil {
		return
	}
	err = json.Unmarshal(buffer.Bytes(), &status)
	if err != nil {
		log.Println(err)
	} else {
		// reset transactions in progress
		for key, _ := range status {
			for i, ss := range status[key].To {
				if ss.Status == 1 {
					status[key].To[i].Status = 0
				}
			}
			for i, ss := range status[key].Cc {
				if ss.Status == 1 {
					status[key].Cc[i].Status = 0
				}
			}
			for i, ss := range status[key].Bcc {
				if ss.Status == 1 {
					status[key].Bcc[i].Status = 0
				}
			}
		}
	}
}

func write_json(file string, j []byte) {
	f, err := os.Create(file)

	if (err != nil) {
		log.Println(err)
		return
	}

	_, err = f.Write(j)
	if err != nil {
		log.Println(err)
	}
	f.Close()
}

func try_again(file string, msg *mail.Message) {

	// update status
	js, err := json.MarshalIndent(status, "", "\t")
	if err != nil {
		log.Println(file, err)
	} else {
		// here we save the file
		write_json(config.JsonPath, js)
	}

	in_progress := false

	mail_status := status[file]

	// rebuild message (strip Bcc)
	var buffer bytes.Buffer
	for key,_ := range msg.Header {
		if key == "Bcc" {
			continue
		}
		buffer.WriteString(key)
		buffer.WriteString(": ")
		header_line := strings.Join(msg.Header[key], ",")
		buffer.WriteString(header_line)
		buffer.WriteString("\r\n")
	}

	buffer.WriteString("\r\n")
	_, err = io.Copy(&buffer, msg.Body)
	if (err != nil) {
		log.Println(file,"unable to reassemble the mail message", err);
		return
	}

	b := buffer.Bytes()

	// manage To
	for i, send_status := range mail_status.To {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.To[i].Status = 1
					go send_mail(mail_status.To[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	// manage Cc
	for i, send_status := range mail_status.Cc {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.Cc[i].Status = 1
					go send_mail(mail_status.Cc[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	// manage Bcc
	for i, send_status := range mail_status.Bcc {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.Bcc[i].Status = 1
					go send_mail(mail_status.Bcc[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	if in_progress == false {
		// first we try to remove the file, on error we avoid to respool the file
		err := os.Remove(file)
		if err != nil {
			log.Println(file,"unable to remove mail file,", err)
			return
		}
		// ok we can now remove the item from the status
		delete(status, file)
	}
}

func parse_mail(file string) {
	f, err := os.Open(file)
	if err != nil {
		log.Println(file,"unable to open mail file,", err)
		return
	}
	defer f.Close()
	msg, err := mail.ReadMessage(f)
	if err != nil {
		log.Println(file,"unable to parse mail file,", err)
		return
	}

	mail_status := MailStatus{}
	mail_status.To = make([]*SentStatus, 0)
	mail_status.Cc = make([]*SentStatus, 0)
	mail_status.Bcc = make([]*SentStatus, 0)

	if _,ok := msg.Header["From"]; ok {
		mail_status.From = msg.Header["From"][0]
	}

	if _,ok := msg.Header["To"]; ok {
		to_addresses, err := msg.Header.AddressList("To")
		if err != nil {
			log.Println(file,"unable to parse mail \"To\" header,", err)
			return
		}
		for _,addr := range to_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.To = append(mail_status.To, &ss)
		}
	}

	if _,ok := msg.Header["Cc"]; ok {
		cc_addresses, err := msg.Header.AddressList("Cc")
		if err != nil {
			log.Println(file,"unable to parse mail \"Cc\" header,", err)
			return
		}
		for _,addr := range cc_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.Cc = append(mail_status.Cc, &ss)
		}
	}

	if _,ok := msg.Header["Bcc"]; ok {
		bcc_addresses, err := msg.Header.AddressList("Bcc")
		if err != nil {
			log.Println(file,"unable to parse mail \"Bcc\" header,", err)
			return
		}
		for _,addr := range bcc_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.Bcc = append(mail_status.Bcc, &ss)
		}
	}

	// is the mail already collected ?
	if _,ok := status[file]; ok {
		try_again(file, msg)
		return
	}

	mail_status.Enqueued = time.Now()
	status[file] = mail_status
	try_again(file, msg)
}

func scan_spooldir(dir string) {
	d, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Println("unable to access spool directory,", err)
		return
	}
	for _, entry := range d {
		if entry.IsDir() {
			continue
		}
		if strings.HasPrefix(entry.Name(), ".") {
			continue
		}
		abs,err := filepath.Abs(path.Join(config.SpoolDir, entry.Name()))
		if err != nil {
			log.Println("unable to get absolute path,", err)
			continue
		}
		parse_mail(path.Clean(abs))
	}
}

func main() {
	parse_options()
	if config.PlainUser != "" {
		config.SmtpAuth = smtp.PlainAuth("", config.PlainUser, config.PlainPassword, strings.Split(config.SmtpAddr, ":")[0])
	} else if config.MD5User != "" {
		config.SmtpAuth = smtp.CRAMMD5Auth(config.MD5User, config.MD5Password)
	}
	log.Println("--- starting spoolgore on directory", config.SpoolDir, "---")
	if config.JsonPath == "" {
		config.JsonPath = path.Join(config.SpoolDir, ".spoolgore.js")
	}
	status = make(map[string]MailStatus)
	read_json(config.JsonPath)
	timer := time.NewTimer(time.Second * time.Duration(config.Freq))
	for {
		select {
			case <- timer.C:
				scan_spooldir(config.SpoolDir)
				timer.Reset(time.Second * time.Duration(config.Freq))
		}
	}
}
<commit_msg>added signal management<commit_after>package main

import (
        "flag"
        "time"
        "log"
	"io"
        "io/ioutil"
	"net/smtp"
	"net/mail"
	"strings"
	"bytes"
	"os"
	"path"
	"path/filepath"
	"encoding/json"
	"os/signal"
	"syscall"
)

type Config struct {
	SpoolDir string
	SmtpAddr string
	SmtpUser string
	SmtpPassword string
	PlainUser string
	PlainPassword string
	MD5User string
	MD5Password string
	SmtpAuth smtp.Auth
	Freq int
	MaxAttempts int
	JsonPath string
}

var config Config

/*
	0 -> just pushed
	1 -> still in progress
	2 -> sent
*/

type SentStatus struct {
	Address string
	Status int
	Attempts int
	NextAttempt time.Time
}

type MailStatus struct {
	From string
	To []*SentStatus
	Cc []*SentStatus
	Bcc []*SentStatus
	Attempts int
	Enqueued time.Time
}

var status map[string]MailStatus

func parse_options() {
	flag.StringVar(&config.SmtpAddr, "smtpaddr", "127.0.0.1:25", "address of the smtp address to use in the addr:port format")
	flag.StringVar(&config.PlainUser, "smtpuser", "", "username for smtp plain authentication")
	flag.StringVar(&config.PlainPassword, "smtppassword", "", "password for smtp plain authentication")
	flag.StringVar(&config.MD5User, "smtpmd5user", "", "username for smtp cram md5 authentication")
	flag.StringVar(&config.MD5Password, "smtpmd5password", "", "password for smtp cram md5 authentication")
	flag.IntVar(&config.Freq, "freq", 10, "frequency of spool directory scans")
	flag.IntVar(&config.MaxAttempts, "attempts", 100, "max attempts for failed SMTP transactions before giving up")
	flag.StringVar(&config.JsonPath, "json", "", "path of the json status file")
	flag.Parse()
	if flag.NArg() < 1 {
		log.Fatal("please specify a spool directory")
	}
	config.SpoolDir = flag.Args()[0]
}

func send_mail(ss *SentStatus, file string, from string, to string, msg *[]byte) {
	log.Println(file,"sending mail to", to, "attempt:", ss.Attempts)
	dest := []string{to}
	err := smtp.SendMail(config.SmtpAddr, config.SmtpAuth, from, dest, *msg)
	if err != nil {
		log.Println(file,"SMTP error, mail to", to, err)
		ss.Status = 0
		ss.Attempts++
		if ss.Attempts >= config.MaxAttempts {
			log.Println(file, "max SMTP attempts reached for",to, "... giving up")
			ss.Status = 2
		}
		if (ss.Attempts > 30) {
			ss.NextAttempt = time.Now().Add(time.Duration(30) * time.Duration(60) * time.Second)
		} else {
			ss.NextAttempt = time.Now().Add(time.Duration(ss.Attempts) * time.Duration(60) * time.Second)
		}
		return
	}
	ss.Status = 2
	log.Println(file, "successfully sent to", to)
}

func spool_flush() {
	num := 0
	now := time.Now()
	for key, _ := range status {
		for i, _ := range status[key].To {
			status[key].To[i].NextAttempt = now
			num += 1
                }
		for i, _ := range status[key].Cc {
			status[key].Cc[i].NextAttempt = now
			num += 1
                }
		for i, _ := range status[key].Bcc {
			status[key].Bcc[i].NextAttempt = now
			num += 1
                }
	}
	log.Printf("spool directory flushed (%d messages in the queue)", num)
	scan_spooldir(config.SpoolDir)
}

func read_json(file string) {
	f, err := os.Open(file)
	if err != nil {
		return
	}
	var buffer bytes.Buffer
	_, err = io.Copy(&buffer, f)
	f.Close()
	if err != nil {
		return
	}
	err = json.Unmarshal(buffer.Bytes(), &status)
	if err != nil {
		log.Println(err)
	} else {
		// reset transactions in progress
		for key, _ := range status {
			for i, ss := range status[key].To {
				if ss.Status == 1 {
					status[key].To[i].Status = 0
				}
			}
			for i, ss := range status[key].Cc {
				if ss.Status == 1 {
					status[key].Cc[i].Status = 0
				}
			}
			for i, ss := range status[key].Bcc {
				if ss.Status == 1 {
					status[key].Bcc[i].Status = 0
				}
			}
		}
	}
}

func write_json(file string, j []byte) {
	f, err := os.Create(file)

	if (err != nil) {
		log.Println(err)
		return
	}

	_, err = f.Write(j)
	if err != nil {
		log.Println(err)
	}
	f.Close()
}

func check_status() {
	changed := false
	for key, _ := range status {
		if _, err := os.Stat(key); os.IsNotExist(err) {
			delete(status, key)
			changed = true
		}
	}

	if changed == true {
		update_json()
	}
}

func update_json() {
	// update status
	js, err := json.MarshalIndent(status, "", "\t")
	if err != nil {
		log.Println(err)
	} else {
		// here we save the file
		write_json(config.JsonPath, js)
	}
}

func try_again(file string, msg *mail.Message) {

	update_json()

	in_progress := false

	mail_status := status[file]

	// rebuild message (strip Bcc)
	var buffer bytes.Buffer
	for key,_ := range msg.Header {
		if key == "Bcc" {
			continue
		}
		buffer.WriteString(key)
		buffer.WriteString(": ")
		header_line := strings.Join(msg.Header[key], ",")
		buffer.WriteString(header_line)
		buffer.WriteString("\r\n")
	}

	buffer.WriteString("\r\n")
	_, err := io.Copy(&buffer, msg.Body)
	if (err != nil) {
		log.Println(file,"unable to reassemble the mail message", err);
		return
	}

	b := buffer.Bytes()

	// manage To
	for i, send_status := range mail_status.To {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.To[i].Status = 1
					go send_mail(mail_status.To[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	// manage Cc
	for i, send_status := range mail_status.Cc {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.Cc[i].Status = 1
					go send_mail(mail_status.Cc[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	// manage Bcc
	for i, send_status := range mail_status.Bcc {
		s := send_status.Status
		switch s {
			case 0:
				in_progress = true
				if send_status.NextAttempt.Equal(time.Now()) == true || send_status.NextAttempt.Before(time.Now()) == true {
					// do not use send_status here !!!
					mail_status.Bcc[i].Status = 1
					go send_mail(mail_status.Bcc[i], file, mail_status.From, send_status.Address, &b)
				}
			case 1:
				in_progress = true
		}
	}

	if in_progress == false {
		// first we try to remove the file, on error we avoid to respool the file
		err := os.Remove(file)
		if err != nil {
			log.Println(file,"unable to remove mail file,", err)
			return
		}
		// ok we can now remove the item from the status
		delete(status, file)
		update_json()
	}
}

func parse_mail(file string) {
	f, err := os.Open(file)
	if err != nil {
		log.Println(file,"unable to open mail file,", err)
		return
	}
	defer f.Close()
	msg, err := mail.ReadMessage(f)
	if err != nil {
		log.Println(file,"unable to parse mail file,", err)
		return
	}

	mail_status := MailStatus{}
	mail_status.To = make([]*SentStatus, 0)
	mail_status.Cc = make([]*SentStatus, 0)
	mail_status.Bcc = make([]*SentStatus, 0)

	if _,ok := msg.Header["From"]; ok {
		mail_status.From = msg.Header["From"][0]
	}

	if _,ok := msg.Header["To"]; ok {
		to_addresses, err := msg.Header.AddressList("To")
		if err != nil {
			log.Println(file,"unable to parse mail \"To\" header,", err)
			return
		}
		for _,addr := range to_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.To = append(mail_status.To, &ss)
		}
	}

	if _,ok := msg.Header["Cc"]; ok {
		cc_addresses, err := msg.Header.AddressList("Cc")
		if err != nil {
			log.Println(file,"unable to parse mail \"Cc\" header,", err)
			return
		}
		for _,addr := range cc_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.Cc = append(mail_status.Cc, &ss)
		}
	}

	if _,ok := msg.Header["Bcc"]; ok {
		bcc_addresses, err := msg.Header.AddressList("Bcc")
		if err != nil {
			log.Println(file,"unable to parse mail \"Bcc\" header,", err)
			return
		}
		for _,addr := range bcc_addresses {
			ss := SentStatus{Address: addr.Address, Status:0, NextAttempt: time.Now()}
			mail_status.Bcc = append(mail_status.Bcc, &ss)
		}
	}

	// is the mail already collected ?
	if _,ok := status[file]; ok {
		try_again(file, msg)
		return
	}

	mail_status.Enqueued = time.Now()
	status[file] = mail_status
	try_again(file, msg)
}

func scan_spooldir(dir string) {
	d, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Println("unable to access spool directory,", err)
		return
	}
	for _, entry := range d {
		if entry.IsDir() {
			continue
		}
		if strings.HasPrefix(entry.Name(), ".") {
			continue
		}
		abs,err := filepath.Abs(path.Join(config.SpoolDir, entry.Name()))
		if err != nil {
			log.Println("unable to get absolute path,", err)
			continue
		}
		parse_mail(path.Clean(abs))
	}

	// cleanup the json often
	check_status()
}

func main() {
	parse_options()
	if config.PlainUser != "" {
		config.SmtpAuth = smtp.PlainAuth("", config.PlainUser, config.PlainPassword, strings.Split(config.SmtpAddr, ":")[0])
	} else if config.MD5User != "" {
		config.SmtpAuth = smtp.CRAMMD5Auth(config.MD5User, config.MD5Password)
	}
	log.Printf("--- starting Spoolgore (pid: %d) on directory %s ---", os.Getpid(), config.SpoolDir)
	if config.JsonPath == "" {
		config.JsonPath = path.Join(config.SpoolDir, ".spoolgore.js")
	}
	status = make(map[string]MailStatus)
	read_json(config.JsonPath)
	timer := time.NewTimer(time.Second * time.Duration(config.Freq))
	urg := make(chan os.Signal, 1)
	hup := make(chan os.Signal, 1)
	tstp := make(chan os.Signal, 1)
	signal.Notify(urg, syscall.SIGURG)
	signal.Notify(hup, syscall.SIGHUP)
	signal.Notify(tstp, syscall.SIGTSTP)
	blocked := false
	for {
		select {
			case <- timer.C:
				if blocked == false {
					scan_spooldir(config.SpoolDir)
				}
				timer.Reset(time.Second * time.Duration(config.Freq))
			case <-urg:
				spool_flush()
				blocked = false
			case <-hup:
				read_json(config.JsonPath)
				blocked = false
				log.Println("status reloaded")
			case <-tstp:
				blocked = true
				log.Println("Spoolgore is suspended, send SIGHUP or SIGURG to unpause it")
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"regexp"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceAwsRoute53ResolverEndpoint_Basic(t *testing.T) {
	name := acctest.RandomWithPrefix("tf-acc-test")
	rInt := acctest.RandInt()
	direction := "INBOUND"
	resourceName := "aws_route53_resolver_endpoint.foo"
	datasourceName := "data.aws_route53_resolver_endpoint.foo"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config:      testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent,
				ExpectError: regexp.MustCompile("The ID provided could not be found"),
			},
			{
				Config: testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt, direction, name),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
					resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
					resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
					resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsRoute53ResolverEndpoint_Filter(t *testing.T) {
	name := acctest.RandomWithPrefix("tf-acc-test")
	rInt := acctest.RandInt()
	direction := "OUTBOUND"
	resourceName := "aws_route53_resolver_endpoint.foo"
	datasourceName := "data.aws_route53_resolver_endpoint.foo"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config:      testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter,
				ExpectError: regexp.MustCompile("Your query returned no results. Please change your search criteria and try again"),
			},
			{
				Config: testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt, direction, name),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
					resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
					resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
					resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
				),
			},
		},
	})
}

func testAccDataSourceRoute53ResolverEndpointConfig_base(rInt int) string {
	return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(`
resource "aws_vpc" "foo" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "terraform-testacc-r53-resolver-vpc-%d"
  }
}

resource "aws_subnet" "sn1" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 0)
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "tf-acc-r53-resolver-sn1-%d"
  }
}

resource "aws_subnet" "sn2" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 1)
  availability_zone = data.aws_availability_zones.available.names[1]

  tags = {
    Name = "tf-acc-r53-resolver-sn2-%d"
  }
}

resource "aws_subnet" "sn3" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 2)
  availability_zone = data.aws_availability_zones.available.names[2]

  tags = {
    Name = "tf-acc-r53-resolver-sn3-%d"
  }
}

resource "aws_security_group" "sg1" {
  vpc_id = aws_vpc.foo.id
  name   = "tf-acc-r53-resolver-sg1-%d"

  tags = {
    Name = "tf-acc-r53-resolver-sg1-%d"
  }
}

resource "aws_security_group" "sg2" {
  vpc_id = aws_vpc.foo.id
  name   = "tf-acc-r53-resolver-sg2-%d"

  tags = {
    Name = "tf-acc-r53-resolver-sg2-%d"
  }
}
`, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt)
}

func testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt int, direction, name string) string {
	return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
  direction = "%s"
  name      = "%s"

  security_group_ids = [
    aws_security_group.sg1.id,
    aws_security_group.sg2.id,
  ]

  ip_address {
    subnet_id = aws_subnet.sn1.id
  }

  ip_address {
    subnet_id = aws_subnet.sn2.id
    ip        = cidrhost(aws_subnet.sn2.cidr_block, 8)
  }

  tags = {
    Environment = "production"
    Usage       = "original"
  }
}

data "aws_route53_resolver_endpoint" "foo" {
  resolver_endpoint_id = aws_route53_resolver_endpoint.foo.id
}
`, direction, name))
}

func testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt int, direction, name string) string {
	return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
  direction = "%s"
  name      = "%s"

  security_group_ids = [
    aws_security_group.sg1.id,
    aws_security_group.sg2.id,
  ]

  ip_address {
    subnet_id = aws_subnet.sn1.id
  }

  ip_address {
    subnet_id = aws_subnet.sn2.id
    ip        = cidrhost(aws_subnet.sn2.cidr_block, 8)
  }

  tags = {
    Environment = "production"
    Usage       = "original"
  }
}

data "aws_route53_resolver_endpoint" "foo" {
  filter {
    name   = "Name"
    values = [aws_route53_resolver_endpoint.foo.name]
  }

  filter {
    name = "SecurityGroupIds"
    values = [aws_security_group.sg1.id, aws_security_group.sg2.id]
  }
}
`, direction, name))
}

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent = `
data "aws_route53_resolver_endpoint" "foo" {
  resolver_endpoint_id = "rslvr-in-8g85830108dd4c82b"
}
`

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter = `
data "aws_route53_resolver_endpoint" "foo" {
  filter {
    name = "Name"
    values = ["None-Existent-Resource"]
  }
}
`
<commit_msg>TF Testing formatting<commit_after>package aws

import (
	"fmt"
	"regexp"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceAwsRoute53ResolverEndpoint_Basic(t *testing.T) {
	name := acctest.RandomWithPrefix("tf-acc-test")
	rInt := acctest.RandInt()
	direction := "INBOUND"
	resourceName := "aws_route53_resolver_endpoint.foo"
	datasourceName := "data.aws_route53_resolver_endpoint.foo"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config:      testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent,
				ExpectError: regexp.MustCompile("The ID provided could not be found"),
			},
			{
				Config: testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt, direction, name),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
					resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
					resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
					resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsRoute53ResolverEndpoint_Filter(t *testing.T) {
	name := acctest.RandomWithPrefix("tf-acc-test")
	rInt := acctest.RandInt()
	direction := "OUTBOUND"
	resourceName := "aws_route53_resolver_endpoint.foo"
	datasourceName := "data.aws_route53_resolver_endpoint.foo"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config:      testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter,
				ExpectError: regexp.MustCompile("Your query returned no results. Please change your search criteria and try again"),
			},
			{
				Config: testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt, direction, name),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"),
					resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"),
					resource.TestCheckResourceAttrPair(datasourceName, "resolver_endpoint_id", resourceName, "id"),
					resource.TestCheckResourceAttr(datasourceName, "ip_addresses.#", "2"),
				),
			},
		},
	})
}

func testAccDataSourceRoute53ResolverEndpointConfig_base(rInt int) string {
	return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(`
resource "aws_vpc" "foo" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "terraform-testacc-r53-resolver-vpc-%d"
  }
}

resource "aws_subnet" "sn1" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 0)
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name = "tf-acc-r53-resolver-sn1-%d"
  }
}

resource "aws_subnet" "sn2" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 1)
  availability_zone = data.aws_availability_zones.available.names[1]

  tags = {
    Name = "tf-acc-r53-resolver-sn2-%d"
  }
}

resource "aws_subnet" "sn3" {
  vpc_id            = aws_vpc.foo.id
  cidr_block        = cidrsubnet(aws_vpc.foo.cidr_block, 2, 2)
  availability_zone = data.aws_availability_zones.available.names[2]

  tags = {
    Name = "tf-acc-r53-resolver-sn3-%d"
  }
}

resource "aws_security_group" "sg1" {
  vpc_id = aws_vpc.foo.id
  name   = "tf-acc-r53-resolver-sg1-%d"

  tags = {
    Name = "tf-acc-r53-resolver-sg1-%d"
  }
}

resource "aws_security_group" "sg2" {
  vpc_id = aws_vpc.foo.id
  name   = "tf-acc-r53-resolver-sg2-%d"

  tags = {
    Name = "tf-acc-r53-resolver-sg2-%d"
  }
}
`, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt)
}

func testAccDataSourceRoute53ResolverEndpointConfig_initial(rInt int, direction, name string) string {
	return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
  direction = "%s"
  name      = "%s"

  security_group_ids = [
    aws_security_group.sg1.id,
    aws_security_group.sg2.id,
  ]

  ip_address {
    subnet_id = aws_subnet.sn1.id
  }

  ip_address {
    subnet_id = aws_subnet.sn2.id
    ip        = cidrhost(aws_subnet.sn2.cidr_block, 8)
  }

  tags = {
    Environment = "production"
    Usage       = "original"
  }
}

data "aws_route53_resolver_endpoint" "foo" {
  resolver_endpoint_id = aws_route53_resolver_endpoint.foo.id
}
`, direction, name))
}

func testAccDataSourceRoute53ResolverEndpointConfig_filter(rInt int, direction, name string) string {
	return composeConfig(testAccDataSourceRoute53ResolverEndpointConfig_base(rInt), fmt.Sprintf(`
resource "aws_route53_resolver_endpoint" "foo" {
  direction = "%s"
  name      = "%s"

  security_group_ids = [
    aws_security_group.sg1.id,
    aws_security_group.sg2.id,
  ]

  ip_address {
    subnet_id = aws_subnet.sn1.id
  }

  ip_address {
    subnet_id = aws_subnet.sn2.id
    ip        = cidrhost(aws_subnet.sn2.cidr_block, 8)
  }

  tags = {
    Environment = "production"
    Usage       = "original"
  }
}

data "aws_route53_resolver_endpoint" "foo" {
  filter {
    name   = "Name"
    values = [aws_route53_resolver_endpoint.foo.name]
  }

  filter {
    name   = "SecurityGroupIds"
    values = [aws_security_group.sg1.id, aws_security_group.sg2.id]
  }
}
`, direction, name))
}

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistent = `
data "aws_route53_resolver_endpoint" "foo" {
  resolver_endpoint_id = "rslvr-in-8g85830108dd4c82b"
}
`

const testAccDataSourceAwsRoute53ResolverEndpointConfig_NonExistentFilter = `
data "aws_route53_resolver_endpoint" "foo" {
  filter {
    name   = "Name"
    values = ["None-Existent-Resource"]
  }
}
`
<|endoftext|>


<commit_before>/*
Copyright 2017 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.
*/

// +k8s:deepcopy-gen=package
// +groupName=samplecontroller.k8s.io

// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1
<commit_msg>Update doc.go in staging/src/k8s.io/<commit_after>/*
Copyright 2017 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.
*/

// +k8s:deepcopy-gen=package
// +groupName=samplecontroller.k8s.io

// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1 // import "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1"
<|endoftext|>


<commit_before>package microsoft

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

func newMockAccessToken(expiresIn int) *accessToken {
	return &accessToken{
		Token:     "token",
		Type:      "token_type",
		Scope:     "token_scope",
		ExpiresIn: fmt.Sprintf("%d", expiresIn),
		ExpiresAt: time.Now().Add(time.Duration(expiresIn) * time.Second),
	}
}

func newMockAuthenticator(token *accessToken) *authenticator {
	// make buffered accessToken channel an pre-fill it with nil
	tokenChan := make(chan *accessToken, 1)
	tokenChan <- token

	// return new authenticator that uses the above accessToken channel
	return &authenticator{
		accessTokenChan: tokenChan,
	}
}

func newMockAuthenticationProvider() *mockAuthenticationProvider {
	return &mockAuthenticationProvider{
		refreshAccessToken: func(token *accessToken) error {
			return nil
		},
	}
}

type mockAuthenticationProvider struct {
	refreshAccessToken func(token *accessToken) error
}

func (p *mockAuthenticationProvider) RefreshAccessToken(token *accessToken) error {
	return p.refreshAccessToken(token)
}

func newMockLanguageProvider() *mockLanguageProvider {
	return &mockLanguageProvider{
		callCounter: make(map[string]int),
		codes:       make([]string, 0),
		names:       make([]string, 0),
	}
}

type mockLanguageProvider struct {
	callCounter map[string]int
	codes       []string
	names       []string
}

func (p *mockLanguageProvider) Codes() ([]string, error) {
	p.callCounter["Codes"]++
	return p.codes, nil
}

func (p *mockLanguageProvider) Names(codes []string) ([]string, error) {
	p.callCounter["Names"]++
	return p.names, nil
}

func newMockTranslationProvider(text, from, to, translation string, t *testing.T) *mockTranslationProvider {
	return &mockTranslationProvider{
		text:        text,
		from:        from,
		to:          to,
		translation: translation,
		t:           t,
	}
}

type mockTranslationProvider struct {
	text        string
	from        string
	to          string
	translation string
	t           *testing.T
}

func (p *mockTranslationProvider) Translate(text, from, to string) (string, error) {
	if p.text != text {
		p.t.Fatalf("Unexpected text value: `%s`", text)
	}

	if p.from != from {
		p.t.Fatalf("Unexpected from value: `%s`", from)
	}

	if p.to != to {
		p.t.Fatalf("Unexpected to value: `%s`", to)
	}
	return p.translation, nil
}

func newMockRouter() *mockRouter {
	return &mockRouter{
		authURL:          "auth",
		translationURL:   "translation",
		languageNamesURL: "languages_names",
		languageCodesURL: "languages_codes",
	}
}

type mockRouter struct {
	authURL          string
	translationURL   string
	languageNamesURL string
	languageCodesURL string
}

func (m *mockRouter) AuthURL() string {
	return m.authURL
}

func (m *mockRouter) TranslationURL() string {
	return m.translationURL
}

func (m *mockRouter) LanguageNamesURL() string {
	return m.languageNamesURL
}

func (m *mockRouter) LanguageCodesURL() string {
	return m.languageCodesURL
}
<commit_msg>Mocks have been moved into corresponding test files<commit_after><|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type pageResult struct {
	Error error
}

func redirectToFile(w http.ResponseWriter, r *http.Request, release Release) {
	url := ""
	if release.DownloadUrl() != "" {
		println("DownloadUrl exists")
		url = release.DownloadUrl()
	} else {
		url = "/file/" + release.Path()
	}
	log.Println("Redirecting to: " + url)
	http.Redirect(w, r, url, http.StatusFound)
}

func downloadPage(url, file string) pageResult {
	log.Println("File doesn't exist yet. Downloading: " + url)
	res, err := http.Get(url)
	if err != nil {
		return pageResult{Error: err}
	}

	if file != "" {
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return pageResult{Error: err}
		}
		err = ioutil.WriteFile(file, body, 0644)
		if err != nil {
			return pageResult{Error: err}
		}
	}
	return pageResult{Error: nil}
}

// If a file exists, just serve it directly. Otherwise, download, then serve.
func cachedFileHandler(w http.ResponseWriter, r *http.Request) {
	paths := strings.Split(r.URL.Path, "/")
	name := paths[len(paths)-1]
	log.Println("Request for cached file: " + name)
	release := Release{
		Name:     r.FormValue("package"),
		Version:  r.FormValue("release"),
		DataDir:  Config.DataDir,
		Filename: name,
	}

	if release.Exists() {
		log.Println(release.Filename + " already exists. Redirecting to download.")
		redirectToFile(w, r, release)
		return
	}

	original := r.FormValue("original")
	res := downloadPage(original, release.ReleaseFilePath())

	if res.Error != nil {
		http.Error(w, "Unable to download page", http.StatusInternalServerError)
		return
	}

	redirectToFile(w, r, release)
}
<commit_msg>Have to return to finish the request<commit_after>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type pageResult struct {
	Error error
}

func redirectToFile(w http.ResponseWriter, r *http.Request, release Release) {
	url := ""
	if release.DownloadUrl() != "" {
		println("DownloadUrl exists")
		url = release.DownloadUrl()
	} else {
		url = "/file/" + release.Path()
	}
	log.Println("Redirecting to: " + url)
	http.Redirect(w, r, url, http.StatusFound)
	return
}

func downloadPage(url, file string) pageResult {
	log.Println("File doesn't exist yet. Downloading: " + url)
	res, err := http.Get(url)
	if err != nil {
		return pageResult{Error: err}
	}

	if file != "" {
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return pageResult{Error: err}
		}
		err = ioutil.WriteFile(file, body, 0644)
		if err != nil {
			return pageResult{Error: err}
		}
	}
	return pageResult{Error: nil}
}

// If a file exists, just serve it directly. Otherwise, download, then serve.
func cachedFileHandler(w http.ResponseWriter, r *http.Request) {
	paths := strings.Split(r.URL.Path, "/")
	name := paths[len(paths)-1]
	log.Println("Request for cached file: " + name)
	release := Release{
		Name:     r.FormValue("package"),
		Version:  r.FormValue("release"),
		DataDir:  Config.DataDir,
		Filename: name,
	}

	if release.Exists() {
		log.Println(release.Filename + " already exists. Redirecting to download.")
		redirectToFile(w, r, release)
		return
	}

	original := r.FormValue("original")
	res := downloadPage(original, release.ReleaseFilePath())

	if res.Error != nil {
		http.Error(w, "Unable to download page", http.StatusInternalServerError)
		return
	}

	redirectToFile(w, r, release)
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 The Jaeger 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 mappings

import (
	"bytes"
	"embed"
	"strings"

	"github.com/jaegertracing/jaeger/pkg/es"
)

//go:embed *.json
// MAPPINGS contains embeded index templates.
var MAPPINGS embed.FS

// MappingBuilder holds parameters required to render an elasticsearch index template
type MappingBuilder struct {
	TemplateBuilder es.TemplateBuilder
	Shards          int64
	Replicas        int64
	EsVersion       uint
	IndexPrefix     string
	UseILM          bool
}

// GetMapping returns the rendered mapping based on elasticsearch version
func (mb *MappingBuilder) GetMapping(mapping string) (string, error) {
	if mb.EsVersion == 7 {
		return mb.fixMapping(mapping + "-7.json")
	}
	return mb.fixMapping(mapping + ".json")
}

// GetSpanServiceMappings returns span and service mappings
func (mb *MappingBuilder) GetSpanServiceMappings() (string, string, error) {
	spanMapping, err := mb.GetMapping("jaeger-span")
	if err != nil {
		return "", "", err
	}
	serviceMapping, err := mb.GetMapping("jaeger-service")
	if err != nil {
		return "", "", err
	}
	return spanMapping, serviceMapping, nil
}

// GetDependenciesMappings returns dependencies mappings
func (mb *MappingBuilder) GetDependenciesMappings() (string, error) {
	return mb.GetMapping("jaeger-dependencies")
}

func loadMapping(name string) string {
	s, _ := MAPPINGS.ReadFile(name)
	return string(s)
}

func (mb *MappingBuilder) fixMapping(mapping string) (string, error) {

	tmpl, err := mb.TemplateBuilder.Parse(loadMapping(mapping))
	if err != nil {
		return "", err
	}
	writer := new(bytes.Buffer)

	if mb.IndexPrefix != "" && !strings.HasSuffix(mb.IndexPrefix, "-") {
		mb.IndexPrefix += "-"
	}
	if err := tmpl.Execute(writer, mb); err != nil {
		return "", err
	}

	return writer.String(), nil
}
<commit_msg>Fix typo in comment in mapping.go (#2939)<commit_after>// Copyright (c) 2021 The Jaeger 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 mappings

import (
	"bytes"
	"embed"
	"strings"

	"github.com/jaegertracing/jaeger/pkg/es"
)

//go:embed *.json
// MAPPINGS contains embedded index templates.
var MAPPINGS embed.FS

// MappingBuilder holds parameters required to render an elasticsearch index template
type MappingBuilder struct {
	TemplateBuilder es.TemplateBuilder
	Shards          int64
	Replicas        int64
	EsVersion       uint
	IndexPrefix     string
	UseILM          bool
}

// GetMapping returns the rendered mapping based on elasticsearch version
func (mb *MappingBuilder) GetMapping(mapping string) (string, error) {
	if mb.EsVersion == 7 {
		return mb.fixMapping(mapping + "-7.json")
	}
	return mb.fixMapping(mapping + ".json")
}

// GetSpanServiceMappings returns span and service mappings
func (mb *MappingBuilder) GetSpanServiceMappings() (string, string, error) {
	spanMapping, err := mb.GetMapping("jaeger-span")
	if err != nil {
		return "", "", err
	}
	serviceMapping, err := mb.GetMapping("jaeger-service")
	if err != nil {
		return "", "", err
	}
	return spanMapping, serviceMapping, nil
}

// GetDependenciesMappings returns dependencies mappings
func (mb *MappingBuilder) GetDependenciesMappings() (string, error) {
	return mb.GetMapping("jaeger-dependencies")
}

func loadMapping(name string) string {
	s, _ := MAPPINGS.ReadFile(name)
	return string(s)
}

func (mb *MappingBuilder) fixMapping(mapping string) (string, error) {

	tmpl, err := mb.TemplateBuilder.Parse(loadMapping(mapping))
	if err != nil {
		return "", err
	}
	writer := new(bytes.Buffer)

	if mb.IndexPrefix != "" && !strings.HasSuffix(mb.IndexPrefix, "-") {
		mb.IndexPrefix += "-"
	}
	if err := tmpl.Execute(writer, mb); err != nil {
		return "", err
	}

	return writer.String(), nil
}
<|endoftext|>


<commit_before>package main

const (
	// The location of the kd logfile.
	LogFilePath = "/Library/Logs/kd.log"
)
<commit_msg>config: Changing the log location, due to permission reqs.<commit_after>package main

const (
	// The location of the kd logfile.
	//LogFilePath = "/Library/Logs/kd.log"
	LogFilePath = "./kd.log"
)
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	yaml "gopkg.in/yaml.v2"

	"github.com/hnakamur/ltsvlog"
	"github.com/masa23/goloba"
	"github.com/masa23/goloba/api"
)

const (
	usage = `Usage argtest [GlobalOptions] <Command> [Options]
Commands:
  info     show information
  weight   change destination weight

Globals Options:
`
	subcommandOptionsUsageFormat = "\nOptions for subcommand \"%s\":\n"
)

type cliApp struct {
	config     *cliConfig
	httpClient *http.Client
}

type cliConfig struct {
	Timeout    time.Duration     `yaml:"timeout"`
	APIServers []apiServerConfig `yaml:"api_servers"`
}

type apiServerConfig struct {
	URL string `yaml:"url"`
}

func main() {
	config := flag.String("config", "/etc/goloba/golobactl.yml", "config file")
	flag.Usage = func() {
		fmt.Print(usage)
		flag.PrintDefaults()
	}
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	conf, err := loadConfig(*config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load config file; %v\n", err)
		os.Exit(1)
	}

	app := &cliApp{
		config:     conf,
		httpClient: &http.Client{Timeout: conf.Timeout},
	}
	switch args[0] {
	case "info":
		app.infoCommand(args[1:])
	case "weight":
		app.weightCommand(args[1:])
	default:
		flag.Usage()
		os.Exit(1)
	}
}

func loadConfig(file string) (*cliConfig, error) {
	buf, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, ltsvlog.WrapErr(err, func(err error) error {
			return fmt.Errorf("failed to read config file, err=%v", err)
		}).String("configFile", file).Stack("")
	}
	var c cliConfig
	err = yaml.Unmarshal(buf, &c)
	if err != nil {
		return nil, ltsvlog.WrapErr(err, func(err error) error {
			return fmt.Errorf("failed to parse config file, err=%v", err)
		}).String("configFile", file).Stack("")
	}
	return &c, nil
}

func subcommandUsageFunc(subcommand string, fs *flag.FlagSet) func() {
	return func() {
		flag.Usage()
		fmt.Printf(subcommandOptionsUsageFormat, subcommand)
		fs.PrintDefaults()
	}
}

func (a *cliApp) infoCommand(args []string) {
	fs := flag.NewFlagSet("info", flag.ExitOnError)
	fs.Usage = subcommandUsageFunc("info", fs)
	format := fs.String("format", "text", "result format, 'text' or 'json'")
	fs.Parse(args)

	var wg sync.WaitGroup
	for _, s := range a.config.APIServers {
		wg.Add(1)
		s := s
		go func() {
			defer wg.Done()

			u := fmt.Sprintf("%s/info", s.URL)
			resp, err := a.httpClient.Get(u)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send request; %v\n", err)
				return
			}
			defer resp.Body.Close()

			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
					return fmt.Errorf("failed to read response from goloba API server")
				}).String("serverURL", s.URL).Stack(""))
			}
			switch *format {
			case "json":
				fmt.Printf("%s:\n%s\n", s.URL, string(data))
			case "text":
				var info api.Info
				err = json.Unmarshal(data, &info)
				if err != nil {
					ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
						return fmt.Errorf("failed to unmarshal JSON response from goloba API server")
					}).String("serverURL", s.URL).Stack(""))
				}
				// ipvsadm output:
				// [root@lbvm01 ~]# ipvsadm -Ln
				// IP Virtual Server version 1.2.1 (size=4096)
				// Prot LocalAddress:Port Scheduler Flags
				//   -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
				// TCP  192.168.122.2:80 wrr
				//   -> 192.168.122.62:80            Route   100    0          0
				//   -> 192.168.122.240:80           Route   500    0          0
				// TCP  192.168.122.2:443 wrr
				//   -> 192.168.122.62:443           Masq    10     0          0
				//   -> 192.168.122.240:443          Masq    20     0          0
				//
				// goloba output:
				// [root@lbvm01 ~]# curl localhost:8880/info
				// Prot LocalAddress:Port Scheduler Flags
				//   -> RemoteAddress:Port           Forward CfgWeight CurWeight ActiveConn InActConn Detached Locked
				// tcp  192.168.122.2:80 wrr
				//   -> 192.168.122.62:80            droute  100       100       0          0         true     false
				//   -> 192.168.122.240:80           droute  500       500       0          0         false    false
				// tcp  192.168.122.2:443 wrr
				//   -> 192.168.122.62:443           masq    10        0         0          0         true     false
				//   -> 192.168.122.240:443          masq    20        20        0          0         false    false
				var buf []byte
				buf = append(append(buf, s.URL...), '\n')
				buf = append(buf, "Prot LocalAddress:Port Scheduler Flags\n"...)
				buf = append(buf, "  -> RemoteAddress:Port           Forward CfgWeight CurWeight ActiveConn InActConn Detached Locked\n"...)
				for _, sr := range info.Services {
					buf = append(buf, fmt.Sprintf("%-4s %s:%d %s\n", sr.Protocol, sr.Address, sr.Port, sr.Schedule)...)
					for _, d := range sr.Destinations {
						hostPort := net.JoinHostPort(d.Address, strconv.Itoa(int(d.Port)))
						buf = append(buf, fmt.Sprintf("  -> %-28s %-7s %-9d %-9d %-10d %-9d %-8v %v\n", hostPort, d.Forward, d.ConfigWeight, d.CurrentWeight, d.ActiveConn, d.InactiveConn, d.Detached, d.Locked)...)
					}
				}
				os.Stdout.Write(buf)
			}
		}()
	}
	wg.Wait()
}

func (a *cliApp) weightCommand(args []string) {
	fs := flag.NewFlagSet("weight", flag.ExitOnError)
	fs.Usage = subcommandUsageFunc("weight", fs)
	serviceAddr := fs.String("s", "", "service address in <IPAddress>:<port> form")
	destAddr := fs.String("d", "", "destination address in <IPAddress>:<port> form")
	weight := fs.Uint("w", 100, fmt.Sprintf("destination weight 0-%d", goloba.MaxWeight))
	lock := fs.Bool("lock", false, "lock weight regardless of future healthcheck results")
	fs.Parse(args)

	if goloba.MaxWeight < *weight {
		fs.Usage()
		os.Exit(1)
	}

	var wg sync.WaitGroup
	for _, s := range a.config.APIServers {
		wg.Add(1)
		s := s
		go func() {
			defer wg.Done()

			u := fmt.Sprintf("%s/weight?service=%s&dest=%s&weight=%d&lock=%v",
				s.URL, url.QueryEscape(*serviceAddr), url.QueryEscape(*destAddr), *weight, *lock)
			resp, err := a.httpClient.Get(u)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send request; %v\n", err)
				return
			}
			defer resp.Body.Close()

			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
					return fmt.Errorf("failed to read response from goloba API server")
				}).String("serverURL", s.URL).Stack(""))
			}
			fmt.Printf("%s:\n%s\n", s.URL, string(data))
		}()
	}
	wg.Wait()
}
<commit_msg>Move Detached and Locked columns to right after CurWeight in Info output<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	yaml "gopkg.in/yaml.v2"

	"github.com/hnakamur/ltsvlog"
	"github.com/masa23/goloba"
	"github.com/masa23/goloba/api"
)

const (
	usage = `Usage argtest [GlobalOptions] <Command> [Options]
Commands:
  info     show information
  weight   change destination weight

Globals Options:
`
	subcommandOptionsUsageFormat = "\nOptions for subcommand \"%s\":\n"
)

type cliApp struct {
	config     *cliConfig
	httpClient *http.Client
}

type cliConfig struct {
	Timeout    time.Duration     `yaml:"timeout"`
	APIServers []apiServerConfig `yaml:"api_servers"`
}

type apiServerConfig struct {
	URL string `yaml:"url"`
}

func main() {
	config := flag.String("config", "/etc/goloba/golobactl.yml", "config file")
	flag.Usage = func() {
		fmt.Print(usage)
		flag.PrintDefaults()
	}
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
		os.Exit(1)
	}

	conf, err := loadConfig(*config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load config file; %v\n", err)
		os.Exit(1)
	}

	app := &cliApp{
		config:     conf,
		httpClient: &http.Client{Timeout: conf.Timeout},
	}
	switch args[0] {
	case "info":
		app.infoCommand(args[1:])
	case "weight":
		app.weightCommand(args[1:])
	default:
		flag.Usage()
		os.Exit(1)
	}
}

func loadConfig(file string) (*cliConfig, error) {
	buf, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, ltsvlog.WrapErr(err, func(err error) error {
			return fmt.Errorf("failed to read config file, err=%v", err)
		}).String("configFile", file).Stack("")
	}
	var c cliConfig
	err = yaml.Unmarshal(buf, &c)
	if err != nil {
		return nil, ltsvlog.WrapErr(err, func(err error) error {
			return fmt.Errorf("failed to parse config file, err=%v", err)
		}).String("configFile", file).Stack("")
	}
	return &c, nil
}

func subcommandUsageFunc(subcommand string, fs *flag.FlagSet) func() {
	return func() {
		flag.Usage()
		fmt.Printf(subcommandOptionsUsageFormat, subcommand)
		fs.PrintDefaults()
	}
}

func (a *cliApp) infoCommand(args []string) {
	fs := flag.NewFlagSet("info", flag.ExitOnError)
	fs.Usage = subcommandUsageFunc("info", fs)
	format := fs.String("format", "text", "result format, 'text' or 'json'")
	fs.Parse(args)

	var wg sync.WaitGroup
	for _, s := range a.config.APIServers {
		wg.Add(1)
		s := s
		go func() {
			defer wg.Done()

			u := fmt.Sprintf("%s/info", s.URL)
			resp, err := a.httpClient.Get(u)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send request; %v\n", err)
				return
			}
			defer resp.Body.Close()

			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
					return fmt.Errorf("failed to read response from goloba API server")
				}).String("serverURL", s.URL).Stack(""))
			}
			switch *format {
			case "json":
				fmt.Printf("%s:\n%s\n", s.URL, string(data))
			case "text":
				var info api.Info
				err = json.Unmarshal(data, &info)
				if err != nil {
					ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
						return fmt.Errorf("failed to unmarshal JSON response from goloba API server")
					}).String("serverURL", s.URL).Stack(""))
				}
				// ipvsadm output:
				// [root@lbvm01 ~]# ipvsadm -Ln
				// IP Virtual Server version 1.2.1 (size=4096)
				// Prot LocalAddress:Port Scheduler Flags
				//   -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
				// TCP  192.168.122.2:80 wrr
				//   -> 192.168.122.62:80            Route   100    0          0
				//   -> 192.168.122.240:80           Route   500    0          0
				// TCP  192.168.122.2:443 wrr
				//   -> 192.168.122.62:443           Masq    10     0          0
				//   -> 192.168.122.240:443          Masq    20     0          0
				//
				// goloba output:
				// [root@lbvm01 ~]# curl localhost:8880/info
				// Prot LocalAddress:Port Scheduler Flags
				//   -> RemoteAddress:Port           Forward CfgWeight CurWeight Detached Locked ActiveConn InActConn
				// tcp  192.168.122.2:80 wrr
				//   -> 192.168.122.62:80            droute  100       100       true     false  0          0
				//   -> 192.168.122.240:80           droute  500       500       false    false  0          0
				// tcp  192.168.122.2:443 wrr
				//   -> 192.168.122.62:443           masq    10        0         true     false  0          0
				//   -> 192.168.122.240:443          masq    20        20        false    false  0          0
				var buf []byte
				buf = append(append(buf, s.URL...), '\n')
				buf = append(buf, "Prot LocalAddress:Port Scheduler Flags\n"...)
				buf = append(buf, "  -> RemoteAddress:Port           Forward CfgWeight CurWeight ActiveConn InActConn Detached Locked\n"...)
				for _, sr := range info.Services {
					buf = append(buf, fmt.Sprintf("%-4s %s:%d %s\n", sr.Protocol, sr.Address, sr.Port, sr.Schedule)...)
					for _, d := range sr.Destinations {
						hostPort := net.JoinHostPort(d.Address, strconv.Itoa(int(d.Port)))
						buf = append(buf, fmt.Sprintf("  -> %-28s %-7s %-9d %-9d %-8v %-6v %-10d %-9d\n", hostPort, d.Forward, d.ConfigWeight, d.CurrentWeight, d.Detached, d.Locked, d.ActiveConn, d.InactiveConn)...)
					}
				}
				os.Stdout.Write(buf)
			}
		}()
	}
	wg.Wait()
}

func (a *cliApp) weightCommand(args []string) {
	fs := flag.NewFlagSet("weight", flag.ExitOnError)
	fs.Usage = subcommandUsageFunc("weight", fs)
	serviceAddr := fs.String("s", "", "service address in <IPAddress>:<port> form")
	destAddr := fs.String("d", "", "destination address in <IPAddress>:<port> form")
	weight := fs.Uint("w", 100, fmt.Sprintf("destination weight 0-%d", goloba.MaxWeight))
	lock := fs.Bool("lock", false, "lock weight regardless of future healthcheck results")
	fs.Parse(args)

	if goloba.MaxWeight < *weight {
		fs.Usage()
		os.Exit(1)
	}

	var wg sync.WaitGroup
	for _, s := range a.config.APIServers {
		wg.Add(1)
		s := s
		go func() {
			defer wg.Done()

			u := fmt.Sprintf("%s/weight?service=%s&dest=%s&weight=%d&lock=%v",
				s.URL, url.QueryEscape(*serviceAddr), url.QueryEscape(*destAddr), *weight, *lock)
			resp, err := a.httpClient.Get(u)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send request; %v\n", err)
				return
			}
			defer resp.Body.Close()

			data, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ltsvlog.Err(ltsvlog.WrapErr(err, func(err error) error {
					return fmt.Errorf("failed to read response from goloba API server")
				}).String("serverURL", s.URL).Stack(""))
			}
			fmt.Printf("%s:\n%s\n", s.URL, string(data))
		}()
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package gosql

import (
	"fmt"
	. "github.com/suboat/go-sql-kit"
)

type SQLLimit struct {
	RuleLimit
	LimitRoot
}

func NewSQLLimit() *SQLLimit {
	return new(SQLLimit)
}

func (s *SQLLimit) String() string {
	if s.Value == nil {
	} else if v, ok := s.Value.(*LimitValue); ok {
		return s.valueString(v)
	}
	return ""
}

func (s *SQLLimit) valueString(v *LimitValue) string {
	if v == nil {
	} else if v.Limit = s.Limit(v.Limit); v.IsLimited() {
		return s.ValueString(v)
	}
	return ""
}

func (s *SQLLimit) ValueString(v *LimitValue) string {
	sql := fmt.Sprintf(`LIMIT %v`, v.Limit)
	v.Skip = v.Skip + v.Limit*v.Page
	if v.Skip > 0 {
		sql += fmt.Sprintf(` OFFSET %v`, v.Skip)
	}
	return sql
}

func (s *SQLLimit) JSONtoSQLString(str string) (string, error) {
	if err := s.ParseJSONString(str); err != nil {
		return "", err
	}
	return s.String(), nil
}

func (s *SQLLimit) SQLString(str string) (string, error) {
	if err := s.Parse(str); err != nil {
		return "", err
	}
	return s.String(), nil
}
<commit_msg>feat: add func for limit params<commit_after>package gosql

import (
	"fmt"
	. "github.com/suboat/go-sql-kit"
)

type SQLLimit struct {
	RuleLimit
	LimitRoot
	limit int
	skip  int
}

func NewSQLLimit() *SQLLimit {
	return new(SQLLimit)
}

func (s *SQLLimit) String() string {
	if s.Value == nil {
	} else if v, ok := s.Value.(*LimitValue); ok {
		return s.valueString(v)
	}
	return ""
}

func (s *SQLLimit) valueString(v *LimitValue) string {
	if v == nil {
	} else if v.Limit = s.Limit(v.Limit); v.IsLimited() {
		s.limit = v.Limit
		s.skip = v.Skip + v.Limit*v.Page
		return s.ValueString()
	}
	return ""
}

func (s *SQLLimit) ValueString() string {
	if s.limit <= 0 {
		return ""
	}
	sql := fmt.Sprintf(`LIMIT %v`, s.limit)
	if s.skip > 0 {
		sql += fmt.Sprintf(` OFFSET %v`, s.skip)
	}
	return sql
}

func (s *SQLLimit) JSONtoSQLString(str string) (string, error) {
	if err := s.ParseJSONString(str); err != nil {
		return "", err
	}
	return s.String(), nil
}

func (s *SQLLimit) SQLString(str string) (string, error) {
	if err := s.Parse(str); err != nil {
		return "", err
	}
	return s.String(), nil
}

func (s *SQLLimit) GetLimit() int {
	return s.limit
}

func (s *SQLLimit) GetSkip() int {
	return s.skip
}
<|endoftext|>


<commit_before>package conditions

import (
	"fmt"
	"strings"
	"time"

	gocontext "context"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana/pkg/util/errutil"
)

func init() {
	alerting.RegisterCondition("query", func(model *simplejson.Json, index int) (alerting.Condition, error) {
		return newQueryCondition(model, index)
	})
}

// QueryCondition is responsible for issue and query, reduce the
// timeseries into single values and evaluate if they are firing or not.
type QueryCondition struct {
	Index         int
	Query         AlertQuery
	Reducer       *queryReducer
	Evaluator     AlertEvaluator
	Operator      string
	HandleRequest tsdb.HandleRequestFunc
}

// AlertQuery contains information about what datasource a query
// should be sent to and the query object.
type AlertQuery struct {
	Model        *simplejson.Json
	DatasourceID int64
	From         string
	To           string
}

// Eval evaluates the `QueryCondition`.
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) {
	timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To)

	seriesList, err := c.executeQuery(context, timeRange)
	if err != nil {
		return nil, err
	}

	emptySeriesCount := 0
	evalMatchCount := 0
	var matches []*alerting.EvalMatch

	for _, series := range seriesList {
		reducedValue := c.Reducer.Reduce(series)
		evalMatch := c.Evaluator.Eval(reducedValue)

		if !reducedValue.Valid {
			emptySeriesCount++
		}

		if context.IsTestRun {
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %s", c.Index, evalMatch, series.Name, reducedValue),
			})
		}

		if evalMatch {
			evalMatchCount++

			matches = append(matches, &alerting.EvalMatch{
				Metric: series.Name,
				Value:  reducedValue,
				Tags:   series.Tags,
			})
		}
	}

	// handle no series special case
	if len(seriesList) == 0 {
		// eval condition for null value
		evalMatch := c.Evaluator.Eval(null.FloatFromPtr(nil))

		if context.IsTestRun {
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition: Eval: %v, Query Returned No Series (reduced to null/no value)", evalMatch),
			})
		}

		if evalMatch {
			evalMatchCount++
			matches = append(matches, &alerting.EvalMatch{Metric: "NoData", Value: null.FloatFromPtr(nil)})
		}
	}

	return &alerting.ConditionResult{
		Firing:      evalMatchCount > 0,
		NoDataFound: emptySeriesCount == len(seriesList),
		Operator:    c.Operator,
		EvalMatches: matches,
	}, nil
}

func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) {
	getDsInfo := &models.GetDataSourceByIdQuery{
		Id:    c.Query.DatasourceID,
		OrgId: context.Rule.OrgID,
	}

	if err := bus.Dispatch(getDsInfo); err != nil {
		return nil, fmt.Errorf("Could not find datasource %v", err)
	}

	req := c.getRequestForAlertRule(getDsInfo.Result, timeRange, context.IsDebug)
	result := make(tsdb.TimeSeriesSlice, 0)

	if context.IsDebug {
		data := simplejson.New()
		if req.TimeRange != nil {
			data.Set("from", req.TimeRange.GetFromAsMsEpoch())
			data.Set("to", req.TimeRange.GetToAsMsEpoch())
		}

		type queryDto struct {
			RefID         string           `json:"refId"`
			Model         *simplejson.Json `json:"model"`
			Datasource    *simplejson.Json `json:"datasource"`
			MaxDataPoints int64            `json:"maxDataPoints"`
			IntervalMs    int64            `json:"intervalMs"`
		}

		queries := []*queryDto{}
		for _, q := range req.Queries {
			queries = append(queries, &queryDto{
				RefID: q.RefId,
				Model: q.Model,
				Datasource: simplejson.NewFromAny(map[string]interface{}{
					"id":   q.DataSource.Id,
					"name": q.DataSource.Name,
				}),
				MaxDataPoints: q.MaxDataPoints,
				IntervalMs:    q.IntervalMs,
			})
		}

		data.Set("queries", queries)

		context.Logs = append(context.Logs, &alerting.ResultLogEntry{
			Message: fmt.Sprintf("Condition[%d]: Query", c.Index),
			Data:    data,
		})
	}

	resp, err := c.HandleRequest(context.Ctx, getDsInfo.Result, req)
	if err != nil {
		if err == gocontext.DeadlineExceeded {
			return nil, fmt.Errorf("Alert execution exceeded the timeout")
		}

		return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
	}

	for _, v := range resp.Results {
		if v.Error != nil {
			return nil, fmt.Errorf("tsdb.HandleRequest() response error %v", v)
		}

		// If there are dataframes but no series on the result
		useDataframes := v.Dataframes != nil && (v.Series == nil || len(v.Series) == 0)

		if useDataframes { // convert the dataframes to tsdb.TimeSeries
			frames, err := v.Dataframes.Decoded()
			if err != nil {
				return nil, errutil.Wrap("tsdb.HandleRequest() failed to unmarshal arrow dataframes from bytes", err)
			}

			for _, frame := range frames {
				ss, err := tsdb.FrameToSeriesSlice(frame)
				if err != nil {
					return nil, errutil.Wrapf(err, `tsdb.HandleRequest() failed to convert dataframe "%v" to tsdb.TimeSeriesSlice`, frame.Name)
				}
				result = append(result, ss...)
			}
		} else {
			result = append(result, v.Series...)
		}

		queryResultData := map[string]interface{}{}

		if context.IsTestRun {
			queryResultData["series"] = result
		}

		if context.IsDebug && v.Meta != nil {
			queryResultData["meta"] = v.Meta
		}

		if context.IsTestRun || context.IsDebug {
			if useDataframes {
				queryResultData["fromDataframe"] = true
			}
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
				Data:    simplejson.NewFromAny(queryResultData),
			})
		}
	}

	return result, nil
}

func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, timeRange *tsdb.TimeRange, debug bool) *tsdb.TsdbQuery {
	req := &tsdb.TsdbQuery{
		TimeRange: timeRange,
		Queries: []*tsdb.Query{
			{
				RefId:      "A",
				Model:      c.Query.Model,
				DataSource: datasource,
			},
		},
		Headers: map[string]string{
			"FromAlert": "true",
		},
		Debug: debug,
	}

	return req
}

func newQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
	condition := QueryCondition{}
	condition.Index = index
	condition.HandleRequest = tsdb.HandleRequest

	queryJSON := model.Get("query")

	condition.Query.Model = queryJSON.Get("model")
	condition.Query.From = queryJSON.Get("params").MustArray()[1].(string)
	condition.Query.To = queryJSON.Get("params").MustArray()[2].(string)

	if err := validateFromValue(condition.Query.From); err != nil {
		return nil, err
	}

	if err := validateToValue(condition.Query.To); err != nil {
		return nil, err
	}

	condition.Query.DatasourceID = queryJSON.Get("datasourceId").MustInt64()

	reducerJSON := model.Get("reducer")
	condition.Reducer = newSimpleReducer(reducerJSON.Get("type").MustString())

	evaluatorJSON := model.Get("evaluator")
	evaluator, err := NewAlertEvaluator(evaluatorJSON)
	if err != nil {
		return nil, fmt.Errorf("error in condition %v: %v", index, err)
	}
	condition.Evaluator = evaluator

	operatorJSON := model.Get("operator")
	operator := operatorJSON.Get("type").MustString("and")
	condition.Operator = operator

	return &condition, nil
}

func validateFromValue(from string) error {
	fromRaw := strings.Replace(from, "now-", "", 1)

	_, err := time.ParseDuration("-" + fromRaw)
	return err
}

func validateToValue(to string) error {
	if to == "now" {
		return nil
	} else if strings.HasPrefix(to, "now-") {
		withoutNow := strings.Replace(to, "now-", "", 1)

		_, err := time.ParseDuration("-" + withoutNow)
		if err == nil {
			return nil
		}
	}

	_, err := time.ParseDuration(to)
	return err
}
<commit_msg>Alerting: copy queryType prop to tsdb.Query (#27053)<commit_after>package conditions

import (
	"fmt"
	"strings"
	"time"

	gocontext "context"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana/pkg/util/errutil"
)

func init() {
	alerting.RegisterCondition("query", func(model *simplejson.Json, index int) (alerting.Condition, error) {
		return newQueryCondition(model, index)
	})
}

// QueryCondition is responsible for issue and query, reduce the
// timeseries into single values and evaluate if they are firing or not.
type QueryCondition struct {
	Index         int
	Query         AlertQuery
	Reducer       *queryReducer
	Evaluator     AlertEvaluator
	Operator      string
	HandleRequest tsdb.HandleRequestFunc
}

// AlertQuery contains information about what datasource a query
// should be sent to and the query object.
type AlertQuery struct {
	Model        *simplejson.Json
	DatasourceID int64
	From         string
	To           string
}

// Eval evaluates the `QueryCondition`.
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) {
	timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To)

	seriesList, err := c.executeQuery(context, timeRange)
	if err != nil {
		return nil, err
	}

	emptySeriesCount := 0
	evalMatchCount := 0
	var matches []*alerting.EvalMatch

	for _, series := range seriesList {
		reducedValue := c.Reducer.Reduce(series)
		evalMatch := c.Evaluator.Eval(reducedValue)

		if !reducedValue.Valid {
			emptySeriesCount++
		}

		if context.IsTestRun {
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %s", c.Index, evalMatch, series.Name, reducedValue),
			})
		}

		if evalMatch {
			evalMatchCount++

			matches = append(matches, &alerting.EvalMatch{
				Metric: series.Name,
				Value:  reducedValue,
				Tags:   series.Tags,
			})
		}
	}

	// handle no series special case
	if len(seriesList) == 0 {
		// eval condition for null value
		evalMatch := c.Evaluator.Eval(null.FloatFromPtr(nil))

		if context.IsTestRun {
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition: Eval: %v, Query Returned No Series (reduced to null/no value)", evalMatch),
			})
		}

		if evalMatch {
			evalMatchCount++
			matches = append(matches, &alerting.EvalMatch{Metric: "NoData", Value: null.FloatFromPtr(nil)})
		}
	}

	return &alerting.ConditionResult{
		Firing:      evalMatchCount > 0,
		NoDataFound: emptySeriesCount == len(seriesList),
		Operator:    c.Operator,
		EvalMatches: matches,
	}, nil
}

func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) {
	getDsInfo := &models.GetDataSourceByIdQuery{
		Id:    c.Query.DatasourceID,
		OrgId: context.Rule.OrgID,
	}

	if err := bus.Dispatch(getDsInfo); err != nil {
		return nil, fmt.Errorf("Could not find datasource %v", err)
	}

	req := c.getRequestForAlertRule(getDsInfo.Result, timeRange, context.IsDebug)
	result := make(tsdb.TimeSeriesSlice, 0)

	if context.IsDebug {
		data := simplejson.New()
		if req.TimeRange != nil {
			data.Set("from", req.TimeRange.GetFromAsMsEpoch())
			data.Set("to", req.TimeRange.GetToAsMsEpoch())
		}

		type queryDto struct {
			RefID         string           `json:"refId"`
			Model         *simplejson.Json `json:"model"`
			Datasource    *simplejson.Json `json:"datasource"`
			MaxDataPoints int64            `json:"maxDataPoints"`
			IntervalMs    int64            `json:"intervalMs"`
		}

		queries := []*queryDto{}
		for _, q := range req.Queries {
			queries = append(queries, &queryDto{
				RefID: q.RefId,
				Model: q.Model,
				Datasource: simplejson.NewFromAny(map[string]interface{}{
					"id":   q.DataSource.Id,
					"name": q.DataSource.Name,
				}),
				MaxDataPoints: q.MaxDataPoints,
				IntervalMs:    q.IntervalMs,
			})
		}

		data.Set("queries", queries)

		context.Logs = append(context.Logs, &alerting.ResultLogEntry{
			Message: fmt.Sprintf("Condition[%d]: Query", c.Index),
			Data:    data,
		})
	}

	resp, err := c.HandleRequest(context.Ctx, getDsInfo.Result, req)
	if err != nil {
		if err == gocontext.DeadlineExceeded {
			return nil, fmt.Errorf("Alert execution exceeded the timeout")
		}

		return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
	}

	for _, v := range resp.Results {
		if v.Error != nil {
			return nil, fmt.Errorf("tsdb.HandleRequest() response error %v", v)
		}

		// If there are dataframes but no series on the result
		useDataframes := v.Dataframes != nil && (v.Series == nil || len(v.Series) == 0)

		if useDataframes { // convert the dataframes to tsdb.TimeSeries
			frames, err := v.Dataframes.Decoded()
			if err != nil {
				return nil, errutil.Wrap("tsdb.HandleRequest() failed to unmarshal arrow dataframes from bytes", err)
			}

			for _, frame := range frames {
				ss, err := tsdb.FrameToSeriesSlice(frame)
				if err != nil {
					return nil, errutil.Wrapf(err, `tsdb.HandleRequest() failed to convert dataframe "%v" to tsdb.TimeSeriesSlice`, frame.Name)
				}
				result = append(result, ss...)
			}
		} else {
			result = append(result, v.Series...)
		}

		queryResultData := map[string]interface{}{}

		if context.IsTestRun {
			queryResultData["series"] = result
		}

		if context.IsDebug && v.Meta != nil {
			queryResultData["meta"] = v.Meta
		}

		if context.IsTestRun || context.IsDebug {
			if useDataframes {
				queryResultData["fromDataframe"] = true
			}
			context.Logs = append(context.Logs, &alerting.ResultLogEntry{
				Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
				Data:    simplejson.NewFromAny(queryResultData),
			})
		}
	}

	return result, nil
}

func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, timeRange *tsdb.TimeRange, debug bool) *tsdb.TsdbQuery {
	queryModel := c.Query.Model
	req := &tsdb.TsdbQuery{
		TimeRange: timeRange,
		Queries: []*tsdb.Query{
			{
				RefId:      "A",
				Model:      queryModel,
				DataSource: datasource,
				QueryType:  queryModel.Get("queryType").MustString(""),
			},
		},
		Headers: map[string]string{
			"FromAlert": "true",
		},
		Debug: debug,
	}

	return req
}

func newQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
	condition := QueryCondition{}
	condition.Index = index
	condition.HandleRequest = tsdb.HandleRequest

	queryJSON := model.Get("query")

	condition.Query.Model = queryJSON.Get("model")
	condition.Query.From = queryJSON.Get("params").MustArray()[1].(string)
	condition.Query.To = queryJSON.Get("params").MustArray()[2].(string)

	if err := validateFromValue(condition.Query.From); err != nil {
		return nil, err
	}

	if err := validateToValue(condition.Query.To); err != nil {
		return nil, err
	}

	condition.Query.DatasourceID = queryJSON.Get("datasourceId").MustInt64()

	reducerJSON := model.Get("reducer")
	condition.Reducer = newSimpleReducer(reducerJSON.Get("type").MustString())

	evaluatorJSON := model.Get("evaluator")
	evaluator, err := NewAlertEvaluator(evaluatorJSON)
	if err != nil {
		return nil, fmt.Errorf("error in condition %v: %v", index, err)
	}
	condition.Evaluator = evaluator

	operatorJSON := model.Get("operator")
	operator := operatorJSON.Get("type").MustString("and")
	condition.Operator = operator

	return &condition, nil
}

func validateFromValue(from string) error {
	fromRaw := strings.Replace(from, "now-", "", 1)

	_, err := time.ParseDuration("-" + fromRaw)
	return err
}

func validateToValue(to string) error {
	if to == "now" {
		return nil
	} else if strings.HasPrefix(to, "now-") {
		withoutNow := strings.Replace(to, "now-", "", 1)

		_, err := time.ParseDuration("-" + withoutNow)
		if err == nil {
			return nil
		}
	}

	_, err := time.ParseDuration(to)
	return err
}
<|endoftext|>


<commit_before>// Package browse provides middleware for listing files in a directory
// when directory path is requested instead of a specific file.
package browse

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/mholt/caddy/middleware"
)

// Browse is an http.Handler that can show a file listing when
// directories in the given paths are specified.
type Browse struct {
	Next    http.HandlerFunc
	Root    string
	Configs []BrowseConfig
}

// BrowseConfig is a configuration for browsing in a particular path.
type BrowseConfig struct {
	PathScope string
	Template  *template.Template
}

// A Listing is used to fill out a template.
type Listing struct {
	// The name of the directory (the last element of the path)
	Name string

	// The full path of the request
	Path string

	// Whether the parent directory is browsable
	CanGoUp bool

	// The items (files and folders) in the path
	Items []FileInfo
}

// FileInfo is the info about a particular file or directory
type FileInfo struct {
	IsDir   bool
	Name    string
	Size    int64
	URL     string
	ModTime time.Time
	Mode    os.FileMode
}

// New creates a new instance of browse middleware.
func New(c middleware.Controller) (middleware.Middleware, error) {
	configs, err := parse(c)
	if err != nil {
		return nil, err
	}

	browse := Browse{
		Root:    c.Root(),
		Configs: configs,
	}

	return func(next http.HandlerFunc) http.HandlerFunc {
		browse.Next = next
		return browse.ServeHTTP
	}, nil
}

// ServeHTTP implements the http.Handler interface.
func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	filename := b.Root + r.URL.Path

	info, err := os.Stat(filename)
	if err != nil {
		// TODO: 404 Not Found
		b.Next(w, r)
		return
	}

	if !info.IsDir() {
		b.Next(w, r)
		return
	}

	for _, bc := range b.Configs {
		if !middleware.Path(r.URL.Path).Matches(bc.PathScope) {
			continue
		}

		file, err := os.Open(b.Root + r.URL.Path)
		if err != nil {
			panic(err) // TODO
		}
		defer file.Close()

		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		files, err := file.Readdir(-1)
		if err != nil || len(files) == 0 {
			// TODO - second condition may not be necessary? See docs...
		}

		// Assemble listing of directory contents
		var fileinfos []FileInfo
		for _, f := range files {
			name := f.Name()
			if f.IsDir() {
				name += "/"
			}
			url := url.URL{Path: name}

			fileinfos = append(fileinfos, FileInfo{
				IsDir:   f.IsDir(),
				Name:    f.Name(),
				Size:    f.Size(),
				URL:     url.String(),
				ModTime: f.ModTime(),
				Mode:    f.Mode(),
			})
		}

		// Determine if user can browse up another folder
		var canGoUp bool
		curPath := strings.TrimSuffix(r.URL.Path, "/")
		for _, other := range b.Configs {
			if strings.HasPrefix(path.Dir(curPath), other.PathScope) {
				canGoUp = true
				break
			}
		}

		listing := Listing{
			Name:    path.Base(r.URL.Path),
			Path:    r.URL.Path,
			CanGoUp: canGoUp,
			Items:   fileinfos,
		}

		err = bc.Template.Execute(w, listing)
		if err != nil {
			panic(err) // TODO
		}

		return
	}

	// Didn't qualify; pass-thru
	b.Next(w, r)
}

// parse returns a list of browsing configurations
func parse(c middleware.Controller) ([]BrowseConfig, error) {
	var configs []BrowseConfig

	appendCfg := func(bc BrowseConfig) error {
		for _, c := range configs {
			if c.PathScope == bc.PathScope {
				return fmt.Errorf("Duplicate browsing config for %s", c.PathScope)
			}
		}
		configs = append(configs, bc)
		return nil
	}

	for c.Next() {
		var bc BrowseConfig

		if !c.NextArg() {
			bc.PathScope = "/"
			err := appendCfg(bc)
			if err != nil {
				return configs, err
			}
			continue
		}

		bc.PathScope = c.Val()

		if !c.NextArg() {
			err := appendCfg(bc)
			if err != nil {
				return configs, err
			}
			continue
		}

		tplFile := c.Val()
		var tplText string

		if tplFile != "" {
			tplBytes, err := ioutil.ReadFile(tplFile)
			if err != nil {
				return configs, err
			}
			tplText = string(tplBytes)
		} else {
			tplText = defaultTemplate
		}

		tpl, err := template.New("listing").Parse(tplText)
		if err != nil {
			return configs, err
		}
		bc.Template = tpl

		err = appendCfg(bc)
		if err != nil {
			return configs, err
		}
	}

	return configs, nil
}

const defaultTemplate = `
{{range .}}
	{{.Name}}

{{end}}
`
<commit_msg>Bug fixes and improvements for browse middleware<commit_after>// Package browse provides middleware for listing files in a directory
// when directory path is requested instead of a specific file.
package browse

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/mholt/caddy/middleware"
)

// Browse is an http.Handler that can show a file listing when
// directories in the given paths are specified.
type Browse struct {
	Next    http.HandlerFunc
	Root    string
	Configs []BrowseConfig
}

// BrowseConfig is a configuration for browsing in a particular path.
type BrowseConfig struct {
	PathScope string
	Template  *template.Template
}

// A Listing is used to fill out a template.
type Listing struct {
	// The name of the directory (the last element of the path)
	Name string

	// The full path of the request
	Path string

	// Whether the parent directory is browsable
	CanGoUp bool

	// The items (files and folders) in the path
	Items []FileInfo
}

// FileInfo is the info about a particular file or directory
type FileInfo struct {
	IsDir   bool
	Name    string
	Size    int64
	URL     string
	ModTime time.Time
	Mode    os.FileMode
}

var IndexPages = []string{
	"index.html",
	"index.htm",
	"default.html",
	"default.htm",
}

// New creates a new instance of browse middleware.
func New(c middleware.Controller) (middleware.Middleware, error) {
	configs, err := parse(c)
	if err != nil {
		return nil, err
	}

	browse := Browse{
		Root:    c.Root(),
		Configs: configs,
	}

	return func(next http.HandlerFunc) http.HandlerFunc {
		browse.Next = next
		return browse.ServeHTTP
	}, nil
}

// ServeHTTP implements the http.Handler interface.
func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	filename := b.Root + r.URL.Path

	info, err := os.Stat(filename)
	if err != nil {
		// TODO: 404 Not Found
		b.Next(w, r)
		return
	}

	if !info.IsDir() {
		b.Next(w, r)
		return
	}

	// See if there's a browse configuration to match the path
	for _, bc := range b.Configs {
		if !middleware.Path(r.URL.Path).Matches(bc.PathScope) {
			continue
		}

		// Browsing navigation gets messed up if browsing a directory
		// that doesn't end in "/" (which it should, anyway)
		if r.URL.Path[len(r.URL.Path)-1] != '/' {
			http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
			return
		}

		// Load directory contents
		file, err := os.Open(b.Root + r.URL.Path)
		if err != nil {
			panic(err) // TODO
		}
		defer file.Close()

		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		files, err := file.Readdir(-1)
		if err != nil || len(files) == 0 {
			// TODO - second condition may not be necessary? See docs...
		}

		// Assemble listing of directory contents
		var fileinfos []FileInfo
		var abort bool // we bail early if we find an index file
		for _, f := range files {
			name := f.Name()

			// Directory is not browseable if it contains index file
			for _, indexName := range IndexPages {
				if name == indexName {
					abort = true
					break
				}
			}
			if abort {
				break
			}

			if f.IsDir() {
				name += "/"
			}
			url := url.URL{Path: name}

			fileinfos = append(fileinfos, FileInfo{
				IsDir:   f.IsDir(),
				Name:    f.Name(),
				Size:    f.Size(),
				URL:     url.String(),
				ModTime: f.ModTime(),
				Mode:    f.Mode(),
			})
		}
		if abort {
			// this dir has an index file, so not browsable
			continue
		}

		// Determine if user can browse up another folder
		var canGoUp bool
		curPath := strings.TrimSuffix(r.URL.Path, "/")
		for _, other := range b.Configs {
			if strings.HasPrefix(path.Dir(curPath), other.PathScope) {
				canGoUp = true
				break
			}
		}

		listing := Listing{
			Name:    path.Base(r.URL.Path),
			Path:    r.URL.Path,
			CanGoUp: canGoUp,
			Items:   fileinfos,
		}

		err = bc.Template.Execute(w, listing)
		if err != nil {
			panic(err) // TODO
		}

		return
	}

	// Didn't qualify; pass-thru
	b.Next(w, r)
}

// parse returns a list of browsing configurations
func parse(c middleware.Controller) ([]BrowseConfig, error) {
	var configs []BrowseConfig

	appendCfg := func(bc BrowseConfig) error {
		for _, c := range configs {
			if c.PathScope == bc.PathScope {
				return fmt.Errorf("Duplicate browsing config for %s", c.PathScope)
			}
		}
		configs = append(configs, bc)
		return nil
	}

	for c.Next() {
		var bc BrowseConfig

		if !c.NextArg() {
			bc.PathScope = "/"
			err := appendCfg(bc)
			if err != nil {
				return configs, err
			}
			continue
		}

		bc.PathScope = c.Val()

		if !c.NextArg() {
			err := appendCfg(bc)
			if err != nil {
				return configs, err
			}
			continue
		}

		tplFile := c.Val()
		var tplText string

		if tplFile != "" {
			tplBytes, err := ioutil.ReadFile(tplFile)
			if err != nil {
				return configs, err
			}
			tplText = string(tplBytes)
		} else {
			tplText = defaultTemplate
		}

		tpl, err := template.New("listing").Parse(tplText)
		if err != nil {
			return configs, err
		}
		bc.Template = tpl

		err = appendCfg(bc)
		if err != nil {
			return configs, err
		}
	}

	return configs, nil
}

const defaultTemplate = `
{{range .}}
	{{.Name}}

{{end}}
`
<|endoftext|>


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

package main

import (
	"os"

	"golang.org/x/text/message/pipeline"
)

const printerType = "golang.org/x/text/message.Printer"

// TODO:
// - merge information into existing files
// - handle different file formats (PO, XLIFF)
// - handle features (gender, plural)
// - message rewriting

func init() {
	overwrite = cmdRewrite.Flag.Bool("w", false, "write files in place")
}

var (
	overwrite *bool
)

var cmdRewrite = &Command{
	Run:       runRewrite,
	UsageLine: "rewrite <package>",
	Short:     "rewrites fmt functions to use a message Printer",
	Long: `
rewrite is typically done once for a project. It rewrites all usages of
fmt to use x/text's message package whenever a message.Printer is in scope.
It rewrites Print and Println calls with constant strings to the equivalent
using Printf to allow translators to reorder arguments.
`,
}

func runRewrite(cmd *Command, _ *pipeline.Config, args []string) error {
	w := os.Stdout
	if *overwrite {
		w = nil
	}
	pkg := "."
	switch len(args) {
	case 0:
	case 1:
		pkg = args[0]
	default:
		return errorf("can only specify at most one package")
	}
	return pipeline.Rewrite(w, pkg)
}
<commit_msg>cmd/gotext: fix "go format failed: invalid argument"<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io"
	"os"

	"golang.org/x/text/message/pipeline"
)

const printerType = "golang.org/x/text/message.Printer"

// TODO:
// - merge information into existing files
// - handle different file formats (PO, XLIFF)
// - handle features (gender, plural)
// - message rewriting

func init() {
	overwrite = cmdRewrite.Flag.Bool("w", false, "write files in place")
}

var (
	overwrite *bool
)

var cmdRewrite = &Command{
	Run:       runRewrite,
	UsageLine: "rewrite <package>",
	Short:     "rewrites fmt functions to use a message Printer",
	Long: `
rewrite is typically done once for a project. It rewrites all usages of
fmt to use x/text's message package whenever a message.Printer is in scope.
It rewrites Print and Println calls with constant strings to the equivalent
using Printf to allow translators to reorder arguments.
`,
}

func runRewrite(cmd *Command, _ *pipeline.Config, args []string) error {
	var w io.Writer
	if !*overwrite {
		w = os.Stdout
	}
	pkg := "."
	switch len(args) {
	case 0:
	case 1:
		pkg = args[0]
	default:
		return errorf("can only specify at most one package")
	}
	return pipeline.Rewrite(w, pkg)
}
<|endoftext|>