<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

var (
	token           string
	channelName     string
	enableDM        bool
	apiToken        string
	mappingFilePath string
	port            string
)

var plateChars *regexp.Regexp = regexp.MustCompile(`[^a-zA-Z0-9]`)

func init() {
	flag.StringVar(&token, "token", "", "Command token for validation")
	flag.StringVar(&apiToken, "api-token", "", "Token required for sending direct messages")
	flag.BoolVar(&enableDM, "dm", false, "Also DM the car holder")
	flag.StringVar(&channelName, "channel", "", "Supported channel")
	flag.StringVar(&mappingFilePath, "mapping", "", "Path to a mapping file")
	flag.StringVar(&port, "port", "8080", "Port to run on")
	flag.Parse()

	if token == "" {
		log.Fatal("Please specify a token using -token")
	}
	if channelName == "" {
		log.Fatal("Please specify a channel using -channel")
	}
	if enableDM && apiToken == "" {
		log.Fatal("You have to specify an API token using -api-token if you want to send direct messages.")
	}
}

func main() {
	store, err := newMappingStore(mappingFilePath)
	if err != nil {
		log.Fatal(err.Error())
	}
	if err = store.Start(); err != nil {
		log.Fatal(err.Error())
	}
	defer store.Stop()
	if mappingFilePath == "" {
		http.HandleFunc("/mapping/", func(w http.ResponseWriter, r *http.Request) {
			defer r.Body.Close()
			if r.Method != "POST" {
				http.Error(w, "Method not supported", http.StatusMethodNotAllowed)
				return
			}
			if err := store.UpdateStore(r.Body); err != nil {
				log.Print(err.Error())
				if err == errInvalidCSV {
					http.Error(w, "Invalid CSV", http.StatusBadRequest)
					return
				}
				http.Error(w, "Server error", http.StatusInternalServerError)
			}
		})
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			r.Body.Close()
			http.Error(w, "Method not supported", http.StatusMethodNotAllowed)
			return
		}
		if err := r.ParseForm(); err != nil {
			log.Print(err.Error())
			http.Error(w, "Failed to parse request", 500)
		}
		if !isInChannel(r, channelName) {
			sendInlineResponse(w, fmt.Sprintf("This command is only supported in #%s", channelName))
			return
		}
		plate := extractPlate(r)
		if plate == "" {
			sendInlineResponse(w, "Please specify a plate number.")
			return
		}
		holder, found := store.LookupPlate(plate)
		if !found {
			sendInlineResponse(w, fmt.Sprintf("Holder for plate %s not found", plate))
			return
		}
		user := r.Form["user_name"][0]
		if user == holder {
			sendInlineResponse(w, "You have blocked yourself. Congratulations!")
			return
		}
		sendChannelResponse(w, fmt.Sprintf("<@%s>: Your :car: is blocking <@%s>! Please move it.", holder, user))
		if enableDM && apiToken != "" {
			msg := fmt.Sprintf("Your :car: is blocking <@%s>! Please move it.", user)
			sendDM(apiToken, holder, msg)
		}
	})
	log.Printf("Starting server on port %s", port)
	if err = http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil {
		log.Fatal(err.Error())
	}
}

func sendDM(token, blockingPerson, msg string) error {
	resp, err := http.PostForm("https://slack.com/api/chat.postMessage", url.Values{
		"token":   {token},
		"channel": {fmt.Sprintf("@%s", blockingPerson)},
		"text":    {msg},
	})
	if err != nil {
		return err
	}
	defer ioutil.NopCloser(resp.Body)
	if resp.StatusCode >= 300 {
		return fmt.Errorf("Unexpected response code received during DM-sending: %d", resp.StatusCode)
	}
	return nil
}

func extractPlate(r *http.Request) string {
	texts, ok := r.Form["text"]
	if !ok {
		return ""
	}
	if len(texts) != 1 {
		return ""
	}
	return strings.ToLower(plateChars.ReplaceAllString(strings.TrimSpace(texts[0]), ""))
}

func isInChannel(r *http.Request, requiredChannel string) bool {
	names, ok := r.Form["channel_name"]
	if !ok {
		return false
	}
	if len(names) < 1 {
		return false
	}
	return names[0] == requiredChannel
}

func sendInlineResponse(w http.ResponseWriter, msg string) {
	w.Write([]byte(msg))
}

type channelResponse struct {
	ResponseType string `json:"response_type"`
	Text         string `json:"text"`
}

func sendChannelResponse(w http.ResponseWriter, msg string) {
	resp := channelResponse{ResponseType: "in_channel", Text: msg}
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Printf("Failed to send channel response: %s", err.Error())
	}
}
<commit_msg>Support multiple input-channels<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

var (
	token           string
	rawChannelNames string
	channelNames    []string
	enableDM        bool
	apiToken        string
	mappingFilePath string
	port            string
)

var plateChars *regexp.Regexp = regexp.MustCompile(`[^a-zA-Z0-9]`)

func init() {
	flag.StringVar(&token, "token", "", "Command token for validation")
	flag.StringVar(&apiToken, "api-token", "", "Token required for sending direct messages")
	flag.BoolVar(&enableDM, "dm", false, "Also DM the car holder")
	flag.StringVar(&rawChannelNames, "channels", "", "Supported channels (comma-separated)")
	flag.StringVar(&mappingFilePath, "mapping", "", "Path to a mapping file")
	flag.StringVar(&port, "port", "8080", "Port to run on")
	flag.Parse()

	channelNames = strings.Split(rawChannelNames, ",")

	if token == "" {
		log.Fatal("Please specify a token using -token")
	}
	if len(channelNames) == 0 {
		log.Fatal("Please specify channels using -channels")
	}
	if enableDM && apiToken == "" {
		log.Fatal("You have to specify an API token using -api-token if you want to send direct messages.")
	}
}

func main() {
	store, err := newMappingStore(mappingFilePath)
	if err != nil {
		log.Fatal(err.Error())
	}
	if err = store.Start(); err != nil {
		log.Fatal(err.Error())
	}
	defer store.Stop()
	if mappingFilePath == "" {
		http.HandleFunc("/mapping/", func(w http.ResponseWriter, r *http.Request) {
			defer r.Body.Close()
			if r.Method != "POST" {
				http.Error(w, "Method not supported", http.StatusMethodNotAllowed)
				return
			}
			if err := store.UpdateStore(r.Body); err != nil {
				log.Print(err.Error())
				if err == errInvalidCSV {
					http.Error(w, "Invalid CSV", http.StatusBadRequest)
					return
				}
				http.Error(w, "Server error", http.StatusInternalServerError)
			}
		})
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			r.Body.Close()
			http.Error(w, "Method not supported", http.StatusMethodNotAllowed)
			return
		}
		if err := r.ParseForm(); err != nil {
			log.Print(err.Error())
			http.Error(w, "Failed to parse request", 500)
		}
		if !isInChannel(r, channelNames) {
			sendInlineResponse(w, fmt.Sprintf("This command is only supported in #%s", channelNames))
			return
		}
		plate := extractPlate(r)
		if plate == "" {
			sendInlineResponse(w, "Please specify a plate number.")
			return
		}
		holder, found := store.LookupPlate(plate)
		if !found {
			sendInlineResponse(w, fmt.Sprintf("Holder for plate %s not found", plate))
			return
		}
		user := r.Form["user_name"][0]
		if user == holder {
			sendInlineResponse(w, "You have blocked yourself. Congratulations!")
			return
		}
		sendChannelResponse(w, fmt.Sprintf("<@%s>: Your :car: is blocking <@%s>! Please move it.", holder, user))
		if enableDM && apiToken != "" {
			msg := fmt.Sprintf("Your :car: is blocking <@%s>! Please move it.", user)
			sendDM(apiToken, holder, msg)
		}
	})
	log.Printf("Starting server on port %s", port)
	if err = http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil {
		log.Fatal(err.Error())
	}
}

func sendDM(token, blockingPerson, msg string) error {
	resp, err := http.PostForm("https://slack.com/api/chat.postMessage", url.Values{
		"token":   {token},
		"channel": {fmt.Sprintf("@%s", blockingPerson)},
		"text":    {msg},
	})
	if err != nil {
		return err
	}
	defer ioutil.NopCloser(resp.Body)
	if resp.StatusCode >= 300 {
		return fmt.Errorf("Unexpected response code received during DM-sending: %d", resp.StatusCode)
	}
	return nil
}

func extractPlate(r *http.Request) string {
	texts, ok := r.Form["text"]
	if !ok {
		return ""
	}
	if len(texts) != 1 {
		return ""
	}
	return strings.ToLower(plateChars.ReplaceAllString(strings.TrimSpace(texts[0]), ""))
}

func isInChannel(r *http.Request, requiredChannels []string) bool {
	names, ok := r.Form["channel_name"]
	if !ok {
		return false
	}
	if len(names) < 1 {
		return false
	}
	for _, allowedChannel := range requiredChannels {
		if allowedChannel == names[0] {
			return true
		}
	}
	return false
}

func sendInlineResponse(w http.ResponseWriter, msg string) {
	w.Write([]byte(msg))
}

type channelResponse struct {
	ResponseType string `json:"response_type"`
	Text         string `json:"text"`
}

func sendChannelResponse(w http.ResponseWriter, msg string) {
	resp := channelResponse{ResponseType: "in_channel", Text: msg}
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		log.Printf("Failed to send channel response: %s", err.Error())
	}
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2009 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

package main

type Inst interface {
	Next()	*Inst;
}

type Regexp struct {
	code *[]Inst;
	start	Inst;
}

type Start struct {
	foo	*Inst;
}

func (start *Start) Next() *Inst { return nil }

func AddInst(Inst) *Inst {
	print("ok in addinst\n");
	return nil
}

func main() {
	re := new(Regexp);
	print("call addinst\n");
	var x Inst = AddInst(new(Start));
	print("return from  addinst\n");
}
<commit_msg>- removed extra package clause<commit_after>// errchk $G $D/$F.go

// Copyright 2009 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

type Inst interface {
	Next()	*Inst;
}

type Regexp struct {
	code *[]Inst;
	start	Inst;
}

type Start struct {
	foo	*Inst;
}

func (start *Start) Next() *Inst { return nil }

func AddInst(Inst) *Inst {
	print("ok in addinst\n");
	return nil
}

func main() {
	re := new(Regexp);
	print("call addinst\n");
	var x Inst = AddInst(new(Start));
	print("return from  addinst\n");
}
<|endoftext|>


<commit_before>package logbuf

import (
	"bufio"
	"fmt"
	"github.com/Symantec/Dominator/lib/url"
	"io"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
)

type countingWriter struct {
	count      uint64
	writer     io.Writer
	prefixLine string
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
	if w.prefixLine != "" {
		w.writer.Write([]byte(w.prefixLine))
		w.prefixLine = ""
	}
	n, err = w.writer.Write(p)
	if n > 0 {
		w.count += uint64(n)
	}
	return
}

func (lb *LogBuffer) addHttpHandlers() {
	http.HandleFunc("/logs", lb.httpListHandler)
	http.HandleFunc("/logs/dump", lb.httpDumpHandler)
	http.HandleFunc("/logs/showLast", lb.httpShowLastHandler)
}

func (lb *LogBuffer) httpListHandler(w http.ResponseWriter, req *http.Request) {
	if lb.logDir == "" {
		return
	}
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	names, err := lb.list(recentFirst)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	recentFirstString := ""
	if recentFirst {
		recentFirstString = "&recentFirst"
	}
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, name := range names {
			fmt.Fprintln(writer, name)
		}
		return
	}
	fmt.Fprintln(writer, "")
	fmt.Fprint(writer, "Logs: ")
	if recentFirst {
		fmt.Fprintf(writer, "showing recent first ")
		fmt.Fprintln(writer, `<a href="logs">show recent last</a><br>`)
	} else {
		fmt.Fprintf(writer, "showing recent last ")
		fmt.Fprintln(writer,
			`<a href="logs?recentFirst">show recent first</a><br>`)
	}
	showRecentLinks(writer, recentFirstString)
	fmt.Fprintln(writer, "
")
	currentName := ""
	lb.rwMutex.Lock()
	if lb.file != nil {
		currentName = path.Base(lb.file.Name())
	}
	lb.rwMutex.Unlock()
	for _, name := range names {
		if name == currentName {
			fmt.Fprintf(writer,
				"%s (current)
\n",
				recentFirstString, name)
		} else {
			fmt.Fprintf(writer, "%s
\n",
				name, recentFirstString, name)
		}
	}
	fmt.Fprintln(writer, "")
}

func showRecentLinks(w io.Writer, recentFirstString string) {
	fmt.Fprintf(w, "Show last: minute\n",
		recentFirstString)
	fmt.Fprintf(w, "           10 min\n",
		recentFirstString)
	fmt.Fprintf(w, "           hour\n",
		recentFirstString)
	fmt.Fprintf(w, "           day\n",
		recentFirstString)
	fmt.Fprintf(w, "           week\n",
		recentFirstString)
}

func (lb *LogBuffer) httpDumpHandler(w http.ResponseWriter, req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	name, ok := parsedQuery.Table["name"]
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	if name == "latest" {
		writer := bufio.NewWriter(w)
		defer writer.Flush()
		lb.Dump(writer, "", "", recentFirst)
		return
	}
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	if recentFirst {
		scanner := bufio.NewScanner(file)
		lines := make([]string, 0)
		for scanner.Scan() {
			line := scanner.Text()
			if len(line) < 1 {
				continue
			}
			lines = append(lines, line)
		}
		if err = scanner.Err(); err == nil {
			reverseStrings(lines)
			for _, line := range lines {
				fmt.Fprintln(writer, line)
			}
		}
	} else {
		_, err = io.Copy(writer, bufio.NewReader(file))
	}
	if err != nil {
		fmt.Fprintln(writer, err)
	}
	return
}

func (lb *LogBuffer) httpShowLastHandler(w http.ResponseWriter,
	req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	for flag := range parsedQuery.Flags {
		length := len(flag)
		if length < 2 {
			continue
		}
		unitChar := flag[length-1]
		var unit time.Duration
		switch unitChar {
		case 's':
			unit = time.Second
		case 'm':
			unit = time.Minute
		case 'h':
			unit = time.Hour
		case 'd':
			unit = time.Hour * 24
		case 'w':
			unit = time.Hour * 24 * 7
		default:
			continue
		}
		if val, err := strconv.ParseUint(flag[:length-1], 10, 64); err != nil {
			w.Header().Set("Content-Type", "text/plain; charset=utf-8")
			w.WriteHeader(http.StatusBadRequest)
			return
		} else {
			lb.showRecent(w, time.Duration(val)*unit, recentFirst)
			return
		}
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusBadRequest)
}

func (lb *LogBuffer) showRecent(w io.Writer, duration time.Duration,
	recentFirst bool) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	names, err := lb.list(true)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	earliestTime := time.Now().Add(-duration)
	// Get a list of names which may be recent enough.
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		startTime, err := time.ParseInLocation(timeLayout, name, time.Local)
		if err != nil {
			continue
		}
		tmpNames = append(tmpNames, name)
		if startTime.Before(earliestTime) {
			break
		}
	}
	names = tmpNames
	if !recentFirst {
		reverseStrings(names)
	}
	fmt.Fprintln(writer, "")
	cWriter := &countingWriter{writer: writer}
	lb.rwMutex.Lock()
	lb.writer.Flush()
	lb.rwMutex.Unlock()
	for _, name := range names {
		cWriter.count = 0
		lb.dumpSince(cWriter, name, earliestTime, "", "
\n", recentFirst)
		if cWriter.count > 0 {
			cWriter.prefixLine = "
\n"
		}
	}
	fmt.Fprintln(writer, "")
}

func (lb *LogBuffer) list(recentFirst bool) ([]string, error) {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return nil, err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return nil, err
	}
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		if strings.Count(name, ":") == 3 {
			tmpNames = append(tmpNames, name)
		}
	}
	names = tmpNames
	sort.Strings(names)
	if recentFirst {
		reverseStrings(names)
	}
	return names, nil
}

func (lb *LogBuffer) writeHtml(writer io.Writer) {
	fmt.Fprintln(writer, `<a href="logs">Logs:</a><br>`)
	fmt.Fprintln(writer, "
")
	lb.Dump(writer, "", "", false)
	fmt.Fprintln(writer, "
")
}
<commit_msg>Dump current file (if available) rather than buffer in lib/logbuf.<commit_after>package logbuf

import (
	"bufio"
	"fmt"
	"github.com/Symantec/Dominator/lib/url"
	"io"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
)

type countingWriter struct {
	count      uint64
	writer     io.Writer
	prefixLine string
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
	if w.prefixLine != "" {
		w.writer.Write([]byte(w.prefixLine))
		w.prefixLine = ""
	}
	n, err = w.writer.Write(p)
	if n > 0 {
		w.count += uint64(n)
	}
	return
}

func (lb *LogBuffer) addHttpHandlers() {
	http.HandleFunc("/logs", lb.httpListHandler)
	http.HandleFunc("/logs/dump", lb.httpDumpHandler)
	http.HandleFunc("/logs/showLast", lb.httpShowLastHandler)
}

func (lb *LogBuffer) httpListHandler(w http.ResponseWriter, req *http.Request) {
	if lb.logDir == "" {
		return
	}
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	names, err := lb.list(recentFirst)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	recentFirstString := ""
	if recentFirst {
		recentFirstString = "&recentFirst"
	}
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, name := range names {
			fmt.Fprintln(writer, name)
		}
		return
	}
	fmt.Fprintln(writer, "")
	fmt.Fprint(writer, "Logs: ")
	if recentFirst {
		fmt.Fprintf(writer, "showing recent first ")
		fmt.Fprintln(writer, `<a href="logs">show recent last</a><br>`)
	} else {
		fmt.Fprintf(writer, "showing recent last ")
		fmt.Fprintln(writer,
			`<a href="logs?recentFirst">show recent first</a><br>`)
	}
	showRecentLinks(writer, recentFirstString)
	fmt.Fprintln(writer, "
")
	currentName := ""
	lb.rwMutex.Lock()
	if lb.file != nil {
		currentName = path.Base(lb.file.Name())
	}
	lb.rwMutex.Unlock()
	for _, name := range names {
		if name == currentName {
			fmt.Fprintf(writer,
				"%s (current)
\n",
				recentFirstString, name)
		} else {
			fmt.Fprintf(writer, "%s
\n",
				name, recentFirstString, name)
		}
	}
	fmt.Fprintln(writer, "")
}

func showRecentLinks(w io.Writer, recentFirstString string) {
	fmt.Fprintf(w, "Show last: minute\n",
		recentFirstString)
	fmt.Fprintf(w, "           10 min\n",
		recentFirstString)
	fmt.Fprintf(w, "           hour\n",
		recentFirstString)
	fmt.Fprintf(w, "           day\n",
		recentFirstString)
	fmt.Fprintf(w, "           week\n",
		recentFirstString)
}

func (lb *LogBuffer) httpDumpHandler(w http.ResponseWriter, req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	name, ok := parsedQuery.Table["name"]
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	if name == "latest" {
		lbFilename := ""
		lb.rwMutex.Lock()
		if lb.file != nil {
			lbFilename = lb.file.Name()
		}
		lb.rwMutex.Unlock()
		if lbFilename == "" {
			writer := bufio.NewWriter(w)
			defer writer.Flush()
			lb.Dump(writer, "", "", recentFirst)
			return
		}
		name = path.Base(lbFilename)
	}
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	if recentFirst {
		scanner := bufio.NewScanner(file)
		lines := make([]string, 0)
		for scanner.Scan() {
			line := scanner.Text()
			if len(line) < 1 {
				continue
			}
			lines = append(lines, line)
		}
		if err = scanner.Err(); err == nil {
			reverseStrings(lines)
			for _, line := range lines {
				fmt.Fprintln(writer, line)
			}
		}
	} else {
		_, err = io.Copy(writer, bufio.NewReader(file))
	}
	if err != nil {
		fmt.Fprintln(writer, err)
	}
	return
}

func (lb *LogBuffer) httpShowLastHandler(w http.ResponseWriter,
	req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	for flag := range parsedQuery.Flags {
		length := len(flag)
		if length < 2 {
			continue
		}
		unitChar := flag[length-1]
		var unit time.Duration
		switch unitChar {
		case 's':
			unit = time.Second
		case 'm':
			unit = time.Minute
		case 'h':
			unit = time.Hour
		case 'd':
			unit = time.Hour * 24
		case 'w':
			unit = time.Hour * 24 * 7
		default:
			continue
		}
		if val, err := strconv.ParseUint(flag[:length-1], 10, 64); err != nil {
			w.Header().Set("Content-Type", "text/plain; charset=utf-8")
			w.WriteHeader(http.StatusBadRequest)
			return
		} else {
			lb.showRecent(w, time.Duration(val)*unit, recentFirst)
			return
		}
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusBadRequest)
}

func (lb *LogBuffer) showRecent(w io.Writer, duration time.Duration,
	recentFirst bool) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	names, err := lb.list(true)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	earliestTime := time.Now().Add(-duration)
	// Get a list of names which may be recent enough.
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		startTime, err := time.ParseInLocation(timeLayout, name, time.Local)
		if err != nil {
			continue
		}
		tmpNames = append(tmpNames, name)
		if startTime.Before(earliestTime) {
			break
		}
	}
	names = tmpNames
	if !recentFirst {
		reverseStrings(names)
	}
	fmt.Fprintln(writer, "")
	cWriter := &countingWriter{writer: writer}
	lb.rwMutex.Lock()
	lb.writer.Flush()
	lb.rwMutex.Unlock()
	for _, name := range names {
		cWriter.count = 0
		lb.dumpSince(cWriter, name, earliestTime, "", "
\n", recentFirst)
		if cWriter.count > 0 {
			cWriter.prefixLine = "
\n"
		}
	}
	fmt.Fprintln(writer, "")
}

func (lb *LogBuffer) list(recentFirst bool) ([]string, error) {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return nil, err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return nil, err
	}
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		if strings.Count(name, ":") == 3 {
			tmpNames = append(tmpNames, name)
		}
	}
	names = tmpNames
	sort.Strings(names)
	if recentFirst {
		reverseStrings(names)
	}
	return names, nil
}

func (lb *LogBuffer) writeHtml(writer io.Writer) {
	fmt.Fprintln(writer, `<a href="logs">Logs:</a><br>`)
	fmt.Fprintln(writer, "
")
	lb.Dump(writer, "", "", false)
	fmt.Fprintln(writer, "
")
}
<|endoftext|>


<commit_before>package logbuf

import (
	"bufio"
	"container/ring"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"syscall"
	"time"
)

const (
	dirPerms  = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP
	filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP

	timeLayout = "2006-01-02:15:04:05.999"
)

func newLogBuffer(length uint, dirname string, quota uint64) *LogBuffer {
	logBuffer := &LogBuffer{
		buffer: ring.New(int(length)),
		logDir: dirname,
		quota:  quota}
	if err := logBuffer.setupFileLogging(); err != nil {
		fmt.Fprintln(logBuffer, err)
	}
	logBuffer.addHttpHandlers()
	return logBuffer
}

func (lb *LogBuffer) setupFileLogging() error {
	if lb.logDir == "" {
		return nil
	}
	if err := lb.createLogDirectory(); err != nil {
		return err
	}
	writeNotifier := make(chan struct{}, 1)
	lb.writeNotifier = writeNotifier
	go lb.flushWhenIdle(writeNotifier)
	return nil
}

func (lb *LogBuffer) createLogDirectory() error {
	if fi, err := os.Stat(lb.logDir); err != nil {
		if err := os.Mkdir(lb.logDir, dirPerms); err != nil {
			return fmt.Errorf("error creating: %s: %s", lb.logDir, err)
		}
		fi, err = os.Stat(lb.logDir)
	} else if !fi.IsDir() {
		return errors.New(lb.logDir + ": is not a directory")
	}
	return lb.enforceQuota()
}

func (lb *LogBuffer) write(p []byte) (n int, err error) {
	if *alsoLogToStderr {
		os.Stderr.Write(p)
	}
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	lb.writeToLogFile(p)
	val := make([]byte, len(p))
	copy(val, p)
	lb.buffer.Value = val
	lb.buffer = lb.buffer.Next()
	return len(p), nil
}

// This should be called with the lock held.
func (lb *LogBuffer) writeToLogFile(p []byte) {
	if lb.writer == nil {
		return
	}
	lb.writer.Write(p)
	lb.writeNotifier <- struct{}{}
	lb.usage += uint64(len(p))
	if lb.usage <= lb.quota {
		return
	}
	lb.enforceQuota()
}

// This should be called with the lock held.
func (lb *LogBuffer) enforceQuota() error {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return err
	}
	sort.Strings(names)
	var usage uint64
	deletedLatestFile := false
	deleteRemainingFiles := false
	latestFile := true
	for index := len(names) - 1; index >= 0; index-- {
		filename := path.Join(lb.logDir, names[index])
		fi, err := os.Lstat(filename)
		if err == os.ErrNotExist {
			continue
		}
		if err != nil {
			return err
		}
		if fi.Mode().IsRegular() {
			size := uint64(fi.Size())
			if size < lb.quota>>10 {
				size = lb.quota >> 10 // Limit number of files to 1024.
			}
			if size+usage > lb.quota || deleteRemainingFiles {
				os.Remove(filename)
				deleteRemainingFiles = true
				if latestFile {
					deletedLatestFile = true
				}
			} else {
				usage += size
			}
			latestFile = false
		}
	}
	lb.usage = usage
	if deletedLatestFile && lb.file != nil {
		lb.writer.Flush()
		lb.writer = nil
		lb.file.Close()
		lb.file = nil
	}
	if lb.file == nil {
		filename := time.Now().Format(timeLayout)
		file, err := os.OpenFile(path.Join(lb.logDir, filename),
			os.O_CREATE|os.O_WRONLY, filePerms)
		if err != nil {
			return err
		}
		lb.file = file
		lb.writer = bufio.NewWriter(file)
		symlink := path.Join(lb.logDir, "latest")
		tmpSymlink := symlink + "~"
		os.Remove(tmpSymlink)
		os.Symlink(filename, tmpSymlink)
		os.Rename(tmpSymlink, symlink)
	}
	return nil
}

func (lb *LogBuffer) flushWhenIdle(writeNotifier <-chan struct{}) {
	timer := time.NewTimer(time.Second)
	for {
		select {
		case <-writeNotifier:
			timer.Reset(time.Second)
		case <-timer.C:
			lb.writer.Flush()
		}
	}
}

func (lb *LogBuffer) getEntries() [][]byte {
	lb.rwMutex.RLock()
	defer lb.rwMutex.RUnlock()
	entries := make([][]byte, 0, lb.buffer.Len())
	lb.buffer.Do(func(p interface{}) {
		if p != nil {
			entries = append(entries, p.([]byte))
		}
	})
	return entries
}

func (lb *LogBuffer) dump(writer io.Writer, prefix, postfix string,
	recentFirst bool) error {
	entries := lb.getEntries()
	if recentFirst {
		reverseEntries(entries)
	}
	for _, entry := range entries {
		writer.Write([]byte(prefix))
		writer.Write(entry)
		writer.Write([]byte(postfix))
	}
	return nil
}

func (lb *LogBuffer) dumpSince(writer io.Writer, name string,
	earliestTime time.Time, prefix, postfix string, recentFirst bool) error {
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		return err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	lines := make([]string, 0)
	timeFormat := "2006/01/02 15:04:05"
	minLength := len(timeFormat) + 2
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) < minLength {
			continue
		}
		timeString := line[:minLength-2]
		timeStamp, err := time.ParseInLocation(timeFormat, timeString,
			time.Local)
		if err != nil {
			continue
		}
		if timeStamp.Before(earliestTime) {
			continue
		}
		if recentFirst {
			lines = append(lines, line)
		} else {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	if recentFirst {
		reverseStrings(lines)
		for _, line := range lines {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	return nil
}

func reverseEntries(entries [][]byte) {
	length := len(entries)
	for index := 0; index < length/2; index++ {
		entries[index], entries[length-1-index] =
			entries[length-1-index], entries[index]
	}
}

func reverseStrings(strings []string) {
	length := len(strings)
	for index := 0; index < length/2; index++ {
		strings[index], strings[length-1-index] =
			strings[length-1-index], strings[index]
	}
}
<commit_msg>Fix race in lib/logbuf when flushing while idle.<commit_after>package logbuf

import (
	"bufio"
	"container/ring"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"syscall"
	"time"
)

const (
	dirPerms  = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP
	filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP

	timeLayout = "2006-01-02:15:04:05.999"
)

func newLogBuffer(length uint, dirname string, quota uint64) *LogBuffer {
	logBuffer := &LogBuffer{
		buffer: ring.New(int(length)),
		logDir: dirname,
		quota:  quota}
	if err := logBuffer.setupFileLogging(); err != nil {
		fmt.Fprintln(logBuffer, err)
	}
	logBuffer.addHttpHandlers()
	return logBuffer
}

func (lb *LogBuffer) setupFileLogging() error {
	if lb.logDir == "" {
		return nil
	}
	if err := lb.createLogDirectory(); err != nil {
		return err
	}
	writeNotifier := make(chan struct{}, 1)
	lb.writeNotifier = writeNotifier
	go lb.flushWhenIdle(writeNotifier)
	return nil
}

func (lb *LogBuffer) createLogDirectory() error {
	if fi, err := os.Stat(lb.logDir); err != nil {
		if err := os.Mkdir(lb.logDir, dirPerms); err != nil {
			return fmt.Errorf("error creating: %s: %s", lb.logDir, err)
		}
		fi, err = os.Stat(lb.logDir)
	} else if !fi.IsDir() {
		return errors.New(lb.logDir + ": is not a directory")
	}
	return lb.enforceQuota()
}

func (lb *LogBuffer) write(p []byte) (n int, err error) {
	if *alsoLogToStderr {
		os.Stderr.Write(p)
	}
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	lb.writeToLogFile(p)
	val := make([]byte, len(p))
	copy(val, p)
	lb.buffer.Value = val
	lb.buffer = lb.buffer.Next()
	return len(p), nil
}

// This should be called with the lock held.
func (lb *LogBuffer) writeToLogFile(p []byte) {
	if lb.writer == nil {
		return
	}
	lb.writer.Write(p)
	lb.writeNotifier <- struct{}{}
	lb.usage += uint64(len(p))
	if lb.usage <= lb.quota {
		return
	}
	lb.enforceQuota()
}

// This should be called with the lock held.
func (lb *LogBuffer) enforceQuota() error {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return err
	}
	sort.Strings(names)
	var usage uint64
	deletedLatestFile := false
	deleteRemainingFiles := false
	latestFile := true
	for index := len(names) - 1; index >= 0; index-- {
		filename := path.Join(lb.logDir, names[index])
		fi, err := os.Lstat(filename)
		if err == os.ErrNotExist {
			continue
		}
		if err != nil {
			return err
		}
		if fi.Mode().IsRegular() {
			size := uint64(fi.Size())
			if size < lb.quota>>10 {
				size = lb.quota >> 10 // Limit number of files to 1024.
			}
			if size+usage > lb.quota || deleteRemainingFiles {
				os.Remove(filename)
				deleteRemainingFiles = true
				if latestFile {
					deletedLatestFile = true
				}
			} else {
				usage += size
			}
			latestFile = false
		}
	}
	lb.usage = usage
	if deletedLatestFile && lb.file != nil {
		lb.writer.Flush()
		lb.writer = nil
		lb.file.Close()
		lb.file = nil
	}
	if lb.file == nil {
		filename := time.Now().Format(timeLayout)
		file, err := os.OpenFile(path.Join(lb.logDir, filename),
			os.O_CREATE|os.O_WRONLY, filePerms)
		if err != nil {
			return err
		}
		lb.file = file
		lb.writer = bufio.NewWriter(file)
		symlink := path.Join(lb.logDir, "latest")
		tmpSymlink := symlink + "~"
		os.Remove(tmpSymlink)
		os.Symlink(filename, tmpSymlink)
		os.Rename(tmpSymlink, symlink)
	}
	return nil
}

func (lb *LogBuffer) flushWhenIdle(writeNotifier <-chan struct{}) {
	timer := time.NewTimer(time.Second)
	for {
		select {
		case <-writeNotifier:
			timer.Reset(time.Second)
		case <-timer.C:
			lb.rwMutex.Lock()
			lb.writer.Flush()
			lb.rwMutex.Unlock()
		}
	}
}

func (lb *LogBuffer) getEntries() [][]byte {
	lb.rwMutex.RLock()
	defer lb.rwMutex.RUnlock()
	entries := make([][]byte, 0, lb.buffer.Len())
	lb.buffer.Do(func(p interface{}) {
		if p != nil {
			entries = append(entries, p.([]byte))
		}
	})
	return entries
}

func (lb *LogBuffer) dump(writer io.Writer, prefix, postfix string,
	recentFirst bool) error {
	entries := lb.getEntries()
	if recentFirst {
		reverseEntries(entries)
	}
	for _, entry := range entries {
		writer.Write([]byte(prefix))
		writer.Write(entry)
		writer.Write([]byte(postfix))
	}
	return nil
}

func (lb *LogBuffer) dumpSince(writer io.Writer, name string,
	earliestTime time.Time, prefix, postfix string, recentFirst bool) error {
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		return err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	lines := make([]string, 0)
	timeFormat := "2006/01/02 15:04:05"
	minLength := len(timeFormat) + 2
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) < minLength {
			continue
		}
		timeString := line[:minLength-2]
		timeStamp, err := time.ParseInLocation(timeFormat, timeString,
			time.Local)
		if err != nil {
			continue
		}
		if timeStamp.Before(earliestTime) {
			continue
		}
		if recentFirst {
			lines = append(lines, line)
		} else {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	if recentFirst {
		reverseStrings(lines)
		for _, line := range lines {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	return nil
}

func reverseEntries(entries [][]byte) {
	length := len(entries)
	for index := 0; index < length/2; index++ {
		entries[index], entries[length-1-index] =
			entries[length-1-index], entries[index]
	}
}

func reverseStrings(strings []string) {
	length := len(strings)
	for index := 0; index < length/2; index++ {
		strings[index], strings[length-1-index] =
			strings[length-1-index], strings[index]
	}
}
<|endoftext|>


<commit_before>package taskmanager

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/gincorp/gin/datastore"
	"github.com/gincorp/gin/workflow"

	"github.com/fatih/structs"
)

// MasterManager ...
// Container for Master Task manager configuration
type MasterManager struct {
	datastore datastore.Datastore
}

// NewMasterManager ...
// Initialise and return a Master Task Manager
func NewMasterManager(redisURI string) (m MasterManager) {
	var err error

	if m.datastore, err = datastore.NewDatastore(redisURI); err != nil {
		log.Fatal(err)
	}

	return
}

// Consume ...
// Handle json from the message queue; for a Master node these will be responses.
// Parse messages, update Workflow contexts, write to database and call next step
func (m MasterManager) Consume(body string) (output map[string]interface{}, err error) {
	var b interface{}
	var uuid string
	var wfr workflow.Runner

	if err = json.Unmarshal([]byte(body), &b); err != nil {
		return
	}

	output = b.(map[string]interface{})

	if output["InitWorkflow"] != nil {
		req := output["InitWorkflow"].(map[string]interface{})

		uuid, err = m.Load(output["UUID"].(string), req["Name"].(string), req["Variables"])
		if err != nil {
			return
		}

		if wfr, err = m.datastore.LoadWorkflowRunner(uuid); err != nil {
			return
		}

	} else {
		uuid = output["UUID"].(string)
		if wfr, err = m.datastore.LoadWorkflowRunner(uuid); err != nil {
			return
		}

		idx, step := wfr.Current()
		step.SetStatus(output)
		wfr.Workflow.Steps[idx] = step

		switch output["Register"].(type) {
		case string:
			register := output["Register"].(string)

			switch output["Data"].(type) {
			case map[string]interface{}:
				data := output["Data"].(map[string]interface{})
				wfr.Variables[register] = data

			default:
				log.Println("Not registering output: got garbage back")
			}
		}

		if output["Failed"].(bool) {
			wfr.Fail(fmt.Sprintf("Step %q failed. See below", wfr.Last))
			m.datastore.DumpWorkflowRunner(wfr)
			return
		}

	}

	m.datastore.DumpWorkflowRunner(wfr)

	s, done := m.Continue(wfr.UUID)
	if !done {
		output = structs.Map(s)
	}

	return
}

// Load a workflow from storage and create a WorkflowRunner state machine
func (m MasterManager) Load(u, name string, variables interface{}) (uuid string, err error) {
	wf, err := m.datastore.LoadWorkflow(name)
	if err != nil {
		return
	}

	wfr := workflow.NewRunner(u, wf)

	switch variables.(type) {
	case map[string]interface{}:
		wfr.Variables["Runtime"] = variables
	}

	wfr.Start()

	m.datastore.DumpWorkflowRunner(wfr)

	return wfr.UUID, nil
}

// Continue will, should there be a next step in the workflow, compile step templates
// and push the step to the emssage queue. Otherwise it'll set the relevant Workflow Runner
// to completed.
func (m MasterManager) Continue(uuid string) (step workflow.Step, done bool) {
	wfr, err := m.datastore.LoadWorkflowRunner(uuid)
	if err != nil {
		log.Print(err)
		return
	}

	step, done = wfr.Next()

	if done {
		wfr.End()
	} else {
		err := step.Compile(wfr.Variables)
		if err != nil {
			wfr.Fail(fmtError(step, err))
			m.datastore.DumpWorkflowRunner(wfr)

			return
		}

		step.UUID = wfr.UUID

		wfr.Last = step.Name
		m.datastore.DumpWorkflowRunner(wfr)

	}
	return step, done
}

func fmtError(step workflow.Step, err error) string {
	return fmt.Sprintf("%s: %s", step.Name, err.Error())
}
<commit_msg>Avoid sending garbage to job queue on error<commit_after>package taskmanager

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/gincorp/gin/datastore"
	"github.com/gincorp/gin/workflow"

	"github.com/fatih/structs"
)

// MasterManager ...
// Container for Master Task manager configuration
type MasterManager struct {
	datastore datastore.Datastore
}

// NewMasterManager ...
// Initialise and return a Master Task Manager
func NewMasterManager(redisURI string) (m MasterManager) {
	var err error

	if m.datastore, err = datastore.NewDatastore(redisURI); err != nil {
		log.Fatal(err)
	}

	return
}

// Consume ...
// Handle json from the message queue; for a Master node these will be responses.
// Parse messages, update Workflow contexts, write to database and call next step
func (m MasterManager) Consume(body string) (output map[string]interface{}, err error) {
	var b interface{}
	var uuid string
	var wfr workflow.Runner

	if err = json.Unmarshal([]byte(body), &b); err != nil {
		return
	}

	input := b.(map[string]interface{})

	if input["InitWorkflow"] != nil {
		req := input["InitWorkflow"].(map[string]interface{})

		uuid, err = m.Load(input["UUID"].(string), req["Name"].(string), req["Variables"])
		if err != nil {
			return
		}

		if wfr, err = m.datastore.LoadWorkflowRunner(uuid); err != nil {
			return
		}

	} else {
		uuid = input["UUID"].(string)
		if wfr, err = m.datastore.LoadWorkflowRunner(uuid); err != nil {
			return
		}

		idx, step := wfr.Current()
		step.SetStatus(input)
		wfr.Workflow.Steps[idx] = step

		switch input["Register"].(type) {
		case string:
			register := input["Register"].(string)

			switch input["Data"].(type) {
			case map[string]interface{}:
				data := input["Data"].(map[string]interface{})
				wfr.Variables[register] = data

			default:
				log.Println("Not registering input: got garbage back")
			}
		}

		if input["Failed"].(bool) {
			wfr.Fail(fmt.Sprintf("Step %q failed. See below", wfr.Last))
			m.datastore.DumpWorkflowRunner(wfr)
			return
		}

	}

	m.datastore.DumpWorkflowRunner(wfr)

	s, done := m.Continue(wfr.UUID)
	if !done {
		output = structs.Map(s)
	}

	return
}

// Load a workflow from storage and create a WorkflowRunner state machine
func (m MasterManager) Load(u, name string, variables interface{}) (uuid string, err error) {
	wf, err := m.datastore.LoadWorkflow(name)
	if err != nil {
		return
	}

	wfr := workflow.NewRunner(u, wf)

	switch variables.(type) {
	case map[string]interface{}:
		wfr.Variables["Runtime"] = variables
	}

	wfr.Start()

	m.datastore.DumpWorkflowRunner(wfr)

	return wfr.UUID, nil
}

// Continue will, should there be a next step in the workflow, compile step templates
// and push the step to the emssage queue. Otherwise it'll set the relevant Workflow Runner
// to completed.
func (m MasterManager) Continue(uuid string) (step workflow.Step, done bool) {
	wfr, err := m.datastore.LoadWorkflowRunner(uuid)
	if err != nil {
		log.Print(err)
		return
	}

	step, done = wfr.Next()

	if done {
		wfr.End()
	} else {
		err := step.Compile(wfr.Variables)
		if err != nil {
			wfr.Fail(fmtError(step, err))
			m.datastore.DumpWorkflowRunner(wfr)

			return
		}

		step.UUID = wfr.UUID

		wfr.Last = step.Name
		m.datastore.DumpWorkflowRunner(wfr)

	}
	return step, done
}

func fmtError(step workflow.Step, err error) string {
	return fmt.Sprintf("%s: %s", step.Name, err.Error())
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	// TODO: Only suppoprts IPv4 addresses. Also add a regexp for IPv6 addresses.
	FederatedIPAddrRegexp  = `(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
	FederatedDNS1123Regexp = `([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)*([a-z0-9]([-a-z0-9]*[a-z0-9])?)`
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			// Tests cannot proceed without this value, so fail early here.
			framework.Failf("FEDERATION_NAME environment variable must be set")
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	It("should be able to discover a federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		svcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})

	It("should be able to discover a non-local federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		// Delete a federated service shard in the default e2e Kubernetes cluster.
		err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)

		localSvcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
		}
		for _, name := range localSvcDNSNames {
			discoverService(f, name, false)
		}

		svcDNSNames := []string{
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *api.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]
		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_internalclientset.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By("Creating a federated service")
	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: api.ServiceSpec{
			Selector: labels,
			Ports: []api.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	_, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, service, 1)
	}
}

func discoverService(f *framework.Framework, name string, exists bool) {
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: []string{"sh", "-c", "nslookup", name},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
	if exists {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`Name:\s+` + FederatedDNS1123Regexp + `\nAddress \d+:\s+` + FederatedIPAddrRegexp + `\s+` + FederatedDNS1123Regexp,
		})
	} else {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`nslookup: can't resolve '` + name + `'`,
		})
	}
}
<commit_msg>choosing a default federation name in test instead of failing<commit_after>/*
Copyright 2016 The Kubernetes 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 e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	DefaultFederationName = "federation"

	// TODO: Only suppoprts IPv4 addresses. Also add a regexp for IPv6 addresses.
	FederatedIPAddrRegexp  = `(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
	FederatedDNS1123Regexp = `([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)*([a-z0-9]([-a-z0-9]*[a-z0-9])?)`
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			federationName = DefaultFederationName
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	It("should be able to discover a federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		svcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})

	It("should be able to discover a non-local federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		// Delete a federated service shard in the default e2e Kubernetes cluster.
		err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)

		localSvcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
		}
		for _, name := range localSvcDNSNames {
			discoverService(f, name, false)
		}

		svcDNSNames := []string{
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *api.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]
		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_internalclientset.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By("Creating a federated service")
	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: api.ServiceSpec{
			Selector: labels,
			Ports: []api.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	_, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, service, 1)
	}
}

func discoverService(f *framework.Framework, name string, exists bool) {
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: []string{"sh", "-c", "nslookup", name},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
	if exists {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`Name:\s+` + FederatedDNS1123Regexp + `\nAddress \d+:\s+` + FederatedIPAddrRegexp + `\s+` + FederatedDNS1123Regexp,
		})
	} else {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`nslookup: can't resolve '` + name + `'`,
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"net/http"
)

// 一个分组信息，可用于控制一组路由项是否启用。
//  g := srv.Group("admin")
//  g.Get("/admin", h)
//  g.Get("/admin/login", h)
//  g.Stop() // 所有通过g绑定的路由都将停止解析。
type Group struct {
	name      string
	isRunning bool
	mux       *ServeMux
}

// 获取当前分组的名称
func (g *Group) Name() string {
	return g.name
}

// 当前分组的路由是否处于运行状态
func (g *Group) IsRunning() bool {
	return g.isRunning
}

// 将当前分组改为运行状态
func (g *Group) Start() {
	g.isRunning = true
}

// 将当前分组改为暂停状态。
func (g *Group) Stop() {
	g.isRunning = false
}

// Add相当于ServeMux.Add(pattern, h, "POST"...)
func (g *Group) Add(pattern string, h http.Handler, methods ...string) *Group {
	g.mux.add(g, pattern, h, methods...)
	return g
}

// Get相当于ServeMux.Get(pattern, h)
func (g *Group) Get(pattern string, h http.Handler) *Group {
	g.mux.add(g, pattern, h, "GET")
	return g
}

// Post相当于ServeMux.Post(pattern, h)
func (g *Group) Post(pattern string, h http.Handler) *Group {
	g.mux.add(g, pattern, h, "POST")
	return g
}

// Delete相当于ServeMux.Delete(pattern, h)
func (g *Group) Delete(pattern string, h http.Handler) *Group {
	g.mux.add(g, pattern, h, "DELETE")
	return g
}

// Put相当于ServeMux.Put(pattern, h)
func (g *Group) Put(pattern string, h http.Handler) *Group {
	g.mux.add(g, pattern, h, "PUT")
	return g
}

// Any相当于ServeMux.Any(pattern, h)
func (g *Group) Any(pattern string, h http.Handler) *Group {
	g.mux.add(g, pattern, h)
	return g
}

// AddFunc功能同ServeMux.AddFunc(pattern, fun, ...)
func (g *Group) AddFunc(pattern string, fun func(http.ResponseWriter, *http.Request), methods ...string) *Group {
	g.mux.addFunc(g, pattern, fun, methods...)
	return g
}

// GetFunc相当于ServeMux.GetFunc(pattern, func)
func (g *Group) GetFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	g.mux.addFunc(g, pattern, fun, "GET")
	return g
}

// PutFunc相当于ServeMux.PutFunc(pattern, func)
func (g *Group) PutFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	g.mux.addFunc(g, pattern, fun, "PUT")
	return g
}

// PostFunc相当于ServeMux.PostFunc(pattern, func)
func (g *Group) PostFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	g.mux.addFunc(g, pattern, fun, "POST")
	return g
}

// DeleteFunc相当于ServeMux.DeleteFunc(pattern, func)
func (g *Group) DeleteFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	g.mux.addFunc(g, pattern, fun, "DELETE")
	return g
}

// AnyFunc相当于ServeMux.AnyFunc(pattern, func)
func (g *Group) AnyFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	g.mux.addFunc(g, pattern, fun)
	return g
}

// AnyFunc相当于ServeMux.Remove(pattern, methods...)
func (g *Group) Remove(pattern string, methods ...string) {
	g.mux.Remove(pattern, methods...)
}

// 指定名称的分组是否存在。
func (mux *ServeMux) HasGroup(name string) bool {
	mux.groupsMu.Lock()
	_, found := mux.groups[name]
	mux.groupsMu.Unlock()

	return found
}

// 声明或是获取一组路由，可以控制该组的路由是否启用。name为分组的名称。
//  g := srv.Group("admin")
//  g.Get("/admin", h)
//  g.Get("/admin/login", h)
//  g.Stop() // 所有通过g绑定的路由都将停止解析。
func (mux *ServeMux) Group(name string) *Group {
	mux.groupsMu.Lock()
	defer mux.groupsMu.Unlock()

	if g, found := mux.groups[name]; found {
		return g
	}

	g := &Group{
		name:      name,
		mux:       mux,
		isRunning: true,
	}
	mux.groups[name] = g
	return g
}

// 获取所有的分组列表。
func (mux *ServeMux) Groups() map[string]*Group {
	return mux.groups
}
<commit_msg>精简Group的方法调用代码<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"net/http"
)

// 一个分组信息，可用于控制一组路由项是否启用。
//  g := srv.Group("admin")
//  g.Get("/admin", h)
//  g.Get("/admin/login", h)
//  g.Stop() // 所有通过g绑定的路由都将停止解析。
type Group struct {
	name      string
	isRunning bool
	mux       *ServeMux
}

// 获取当前分组的名称
func (g *Group) Name() string {
	return g.name
}

// 当前分组的路由是否处于运行状态
func (g *Group) IsRunning() bool {
	return g.isRunning
}

// 将当前分组改为运行状态
func (g *Group) Start() {
	g.isRunning = true
}

// 将当前分组改为暂停状态。
func (g *Group) Stop() {
	g.isRunning = false
}

// Add相当于ServeMux.Add(pattern, h, "POST"...)
func (g *Group) Add(pattern string, h http.Handler, methods ...string) *Group {
	g.mux.add(g, pattern, h, methods...)
	return g
}

// Get相当于ServeMux.Get(pattern, h)
func (g *Group) Get(pattern string, h http.Handler) *Group {
	return g.Add(pattern, h, "GET")
}

// Post相当于ServeMux.Post(pattern, h)
func (g *Group) Post(pattern string, h http.Handler) *Group {
	return g.Add(pattern, h, "POST")
}

// Delete相当于ServeMux.Delete(pattern, h)
func (g *Group) Delete(pattern string, h http.Handler) *Group {
	return g.Add(pattern, h, "DELETE")
}

// Put相当于ServeMux.Put(pattern, h)
func (g *Group) Put(pattern string, h http.Handler) *Group {
	return g.Add(pattern, h, "PUT")
}

// Any相当于ServeMux.Any(pattern, h)
func (g *Group) Any(pattern string, h http.Handler) *Group {
	return g.Add(pattern, h)
}

// AddFunc功能同ServeMux.AddFunc(pattern, fun, ...)
func (g *Group) AddFunc(pattern string, fun func(http.ResponseWriter, *http.Request), methods ...string) *Group {
	g.mux.addFunc(g, pattern, fun, methods...)
	return g
}

// GetFunc相当于ServeMux.GetFunc(pattern, func)
func (g *Group) GetFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	return g.AddFunc(pattern, fun, "GET")
}

// PutFunc相当于ServeMux.PutFunc(pattern, func)
func (g *Group) PutFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	return g.AddFunc(pattern, fun, "PUT")
}

// PostFunc相当于ServeMux.PostFunc(pattern, func)
func (g *Group) PostFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	return g.AddFunc(pattern, fun, "POST")
}

// DeleteFunc相当于ServeMux.DeleteFunc(pattern, func)
func (g *Group) DeleteFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	return g.AddFunc(pattern, fun, "DELETE")
}

// AnyFunc相当于ServeMux.AnyFunc(pattern, func)
func (g *Group) AnyFunc(pattern string, fun func(http.ResponseWriter, *http.Request)) *Group {
	return g.AddFunc(pattern, fun)
}

// AnyFunc相当于ServeMux.Remove(pattern, methods...)
func (g *Group) Remove(pattern string, methods ...string) {
	g.mux.Remove(pattern, methods...)
}

// 指定名称的分组是否存在。
func (mux *ServeMux) HasGroup(name string) bool {
	mux.groupsMu.Lock()
	_, found := mux.groups[name]
	mux.groupsMu.Unlock()

	return found
}

// 声明或是获取一组路由，可以控制该组的路由是否启用。name为分组的名称。
//  g := srv.Group("admin")
//  g.Get("/admin", h)
//  g.Get("/admin/login", h)
//  g.Stop() // 所有通过g绑定的路由都将停止解析。
func (mux *ServeMux) Group(name string) *Group {
	mux.groupsMu.Lock()
	defer mux.groupsMu.Unlock()

	if g, found := mux.groups[name]; found {
		return g
	}

	g := &Group{
		name:      name,
		mux:       mux,
		isRunning: true,
	}
	mux.groups[name] = g
	return g
}

// 获取所有的分组列表。
func (mux *ServeMux) Groups() map[string]*Group {
	return mux.groups
}
<|endoftext|>


<commit_before>/*
Copyright 2021 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 (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/testgrid/config"
	"github.com/GoogleCloudPlatform/testgrid/util/gcs"

	"github.com/sirupsen/logrus"
)

type options struct {
	configPath    string
	creds         string
	printFieldUse bool
}

func gatherOptions() options {
	var o options
	if len(os.Args) > 1 {
		o.configPath = os.Args[1]
	}
	flag.StringVar(&o.configPath, "path", o.configPath, "Local or cloud config to read")
	flag.StringVar(&o.creds, "gcp-service-account", "", "/path/to/gcp/creds (use local creds if empty)")
	flag.BoolVar(&o.printFieldUse, "print-field-use", false, "If True, print all config fields and # of uses of each")
	flag.Parse()
	return o
}

func main() {
	log := logrus.WithField("component", "config-printer")
	opt := gatherOptions()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	storageClient, err := gcs.ClientWithCreds(ctx, opt.creds)
	if err != nil {
		log.WithError(err).Info("Can't make cloud storage client; proceeding")
	}

	cfg, err := config.Read(ctx, opt.configPath, storageClient)
	if err != nil {
		logrus.WithError(err).WithField("path", opt.configPath).Fatal("Can't read from path")
	}
	b, err := json.Marshal(cfg)
	if err != nil {
		logrus.WithError(err).Fatal("Can't marshal JSON")
	}

	fmt.Printf("%s\n", b)

	if opt.printFieldUse {
		err = printFieldNames(b)
		if err != nil {
			logrus.WithError(err).Fatal("Error printing fields")
		}
	}
}

func printFieldNames(b []byte) error {
	type Dashboard struct {
		DashboardTabs []map[string]interface{} `json:"dashboard_tab"`
	}
	type Config struct {
		TestGroups      []map[string]interface{} `json:"test_groups"`
		Dashboards      []Dashboard              `json:"dashboards"`
		DashboardGroups []map[string]interface{} `json:"dashboard_groups"`
	}
	var output Config
	err := json.Unmarshal(b, &output)
	if err != nil {
		return err
	}

	fmt.Printf("%d test groups, %d dashboards, %d dashboard groups\n", len(output.TestGroups), len(output.Dashboards), len(output.DashboardGroups))

	testGroupFields := map[string]int64{}
	for _, value := range output.TestGroups {
		for k := range value {
			testGroupFields[k]++
		}
	}
	fmt.Printf("Test Groups (%d fields):\n", len(testGroupFields))
	for k, v := range testGroupFields {
		fmt.Printf("%s,%d\n", k, v)
	}

	tabFields := map[string]int64{}
	for _, dashboard := range output.Dashboards {
		for _, val := range dashboard.DashboardTabs {
			for k := range val {
				tabFields[k]++
			}
		}
	}
	fmt.Printf("Dashboard Tabs (%d fields):\n", len(tabFields))
	for k, v := range tabFields {
		fmt.Printf("%s,%d\n", k, v)
	}
	fmt.Printf("Test Groups (%d fields):\n", len(testGroupFields))
	for k, v := range testGroupFields {
		fmt.Printf("%s,%d\n", k, v)
	}
	return nil
}
<commit_msg>Pretty-print config by default<commit_after>/*
Copyright 2021 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 (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"github.com/GoogleCloudPlatform/testgrid/config"
	"github.com/GoogleCloudPlatform/testgrid/util/gcs"

	"github.com/sirupsen/logrus"
)

type options struct {
	configPath    string
	creds         string
	printFieldUse bool
	compressed    bool
}

func gatherOptions() options {
	var o options
	if len(os.Args) > 1 {
		o.configPath = os.Args[1]
	}
	flag.StringVar(&o.configPath, "path", o.configPath, "Local or cloud config to read")
	flag.StringVar(&o.creds, "gcp-service-account", "", "/path/to/gcp/creds (use local creds if empty)")
	flag.BoolVar(&o.printFieldUse, "print-field-use", false, "If True, print all config fields and # of uses of each")
	flag.BoolVar(&o.compressed, "compressed", false, "Disables pretty-printing, removing all whitespace between fields")
	flag.Parse()
	return o
}

func main() {
	log := logrus.WithField("component", "config-printer")
	opt := gatherOptions()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	storageClient, err := gcs.ClientWithCreds(ctx, opt.creds)
	if err != nil {
		log.WithError(err).Info("Can't make cloud storage client; proceeding")
	}

	cfg, err := config.Read(ctx, opt.configPath, storageClient)
	if err != nil {
		logrus.WithError(err).WithField("path", opt.configPath).Fatal("Can't read from path")
	}
	b, err := json.Marshal(cfg)
	if err != nil {
		logrus.WithError(err).Fatal("Can't marshal JSON")
	}

	if opt.compressed {
		fmt.Printf("%s\n", b)
	} else {
		var out bytes.Buffer
		json.Indent(&out, b, "", "  ")
		out.WriteTo(os.Stdout)
	}

	if opt.printFieldUse {
		err = printFieldNames(b)
		if err != nil {
			logrus.WithError(err).Fatal("Error printing fields")
		}
	}
}

func printFieldNames(b []byte) error {
	type Dashboard struct {
		DashboardTabs []map[string]interface{} `json:"dashboard_tab"`
	}
	type Config struct {
		TestGroups      []map[string]interface{} `json:"test_groups"`
		Dashboards      []Dashboard              `json:"dashboards"`
		DashboardGroups []map[string]interface{} `json:"dashboard_groups"`
	}
	var output Config
	err := json.Unmarshal(b, &output)
	if err != nil {
		return err
	}

	fmt.Printf("%d test groups, %d dashboards, %d dashboard groups\n", len(output.TestGroups), len(output.Dashboards), len(output.DashboardGroups))

	testGroupFields := map[string]int64{}
	for _, value := range output.TestGroups {
		for k := range value {
			testGroupFields[k]++
		}
	}
	fmt.Printf("Test Groups (%d fields):\n", len(testGroupFields))
	for k, v := range testGroupFields {
		fmt.Printf("%s,%d\n", k, v)
	}

	tabFields := map[string]int64{}
	for _, dashboard := range output.Dashboards {
		for _, val := range dashboard.DashboardTabs {
			for k := range val {
				tabFields[k]++
			}
		}
	}
	fmt.Printf("Dashboard Tabs (%d fields):\n", len(tabFields))
	for k, v := range tabFields {
		fmt.Printf("%s,%d\n", k, v)
	}
	fmt.Printf("Test Groups (%d fields):\n", len(testGroupFields))
	for k, v := range testGroupFields {
		fmt.Printf("%s,%d\n", k, v)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package urlexpander provides a library to expand shortened urls from services like goo.gl, bitly.com, tinyurl.com
package urlexpander

import (
	"net/url"
	"time"

	"github.com/bluele/gcache"
)

type Config struct {
	// User agent string used when translating shortened url.
	UserAgent string

	// Maximum length of shortened url. It is assumed that no shortened url is longer than that.
	ShortUrlMaxLength int

	// Expanded urls are cached for repeated use. Using this option cache capacity can be set.
	CacheCapacity int

	// Expanded urls are cached for repeated use. Using this option expiration timeout can be set.
	CacheExpiration time.Duration
}

func newDefaultConfig() Config {
	return Config{
		UserAgent:         "Mozilla/5.0 (compatible; UrlExpander/1.0)",
		ShortUrlMaxLength: 32,
		CacheCapacity:     100000,
		CacheExpiration:   1 * time.Hour,
	}
}

type UrlExpander interface {
	// Expand given shortened url to its original form.
	// Return either an expanded url as a string or an error.
	ExpandUrl(shortenedUrl string) (string, error)
}

type expander struct {
	Config  Config
	fetcher *fetcher
	cache   gcache.Cache
}

// Create a new UrlExpander with default config
func New() UrlExpander {
	return NewFromConfig(newDefaultConfig())
}

// Create a new UrlExpander with provided config
func NewFromConfig(config Config) UrlExpander {
	return &expander{
		Config:  config,
		fetcher: newFetcher(config.UserAgent),
		cache:   gcache.New(config.CacheCapacity).LRU().Expiration(config.CacheExpiration).Build()}
}

func (exp expander) ExpandUrl(shortenedUrl string) (string, error) {
	// Check if the given string is not exceeding configured length limit.
	if len(shortenedUrl) > exp.Config.ShortUrlMaxLength {
		return "", ErrLongUrl
	}

	u, err := url.ParseRequestURI(shortenedUrl)
	if err != nil {
		return "", ErrInvalidUrl
	}

	// check if given url is present in cache
	uString := u.String()
	r, err := exp.cache.GetIFPresent(uString)
	if err == nil {
		// item is present in cache
		return r.(string), nil
	}

	expanded, err := exp.fetcher.fetchLocationHeader(uString)
	if err != nil {
		return "", err
	}

	// set expanded url to cache
	exp.cache.Set(uString, expanded)

	return expanded, nil
}
<commit_msg>#! Avoid copying struct<commit_after>// Package urlexpander provides a library to expand shortened urls from services like goo.gl, bitly.com, tinyurl.com
package urlexpander

import (
	"net/url"
	"time"

	"github.com/bluele/gcache"
)

type Config struct {
	// User agent string used when translating shortened url.
	UserAgent string

	// Maximum length of shortened url. It is assumed that no shortened url is longer than that.
	ShortUrlMaxLength int

	// Expanded urls are cached for repeated use. Using this option cache capacity can be set.
	CacheCapacity int

	// Expanded urls are cached for repeated use. Using this option expiration timeout can be set.
	CacheExpiration time.Duration
}

func newDefaultConfig() Config {
	return Config{
		UserAgent:         "Mozilla/5.0 (compatible; UrlExpander/1.0)",
		ShortUrlMaxLength: 32,
		CacheCapacity:     100000,
		CacheExpiration:   1 * time.Hour,
	}
}

type UrlExpander interface {
	// Expand given shortened url to its original form.
	// Return either an expanded url as a string or an error.
	ExpandUrl(shortenedUrl string) (string, error)
}

type expander struct {
	Config  Config
	fetcher *fetcher
	cache   gcache.Cache
}

// Create a new UrlExpander with default config
func New() UrlExpander {
	return NewFromConfig(newDefaultConfig())
}

// Create a new UrlExpander with provided config
func NewFromConfig(config Config) UrlExpander {
	return &expander{
		Config:  config,
		fetcher: newFetcher(config.UserAgent),
		cache:   gcache.New(config.CacheCapacity).LRU().Expiration(config.CacheExpiration).Build()}
}

func (exp *expander) ExpandUrl(shortenedUrl string) (string, error) {
	// Check if the given string is not exceeding configured length limit.
	if len(shortenedUrl) > exp.Config.ShortUrlMaxLength {
		return "", ErrLongUrl
	}

	u, err := url.ParseRequestURI(shortenedUrl)
	if err != nil {
		return "", ErrInvalidUrl
	}

	// check if given url is present in cache
	uString := u.String()
	r, err := exp.cache.GetIFPresent(uString)
	if err == nil {
		// item is present in cache
		return r.(string), nil
	}

	expanded, err := exp.fetcher.fetchLocationHeader(uString)
	if err != nil {
		return "", err
	}

	// set expanded url to cache
	exp.cache.Set(uString, expanded)

	return expanded, nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"

	"golang.org/x/net/context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/influx"
	"github.com/influxdata/chronograf/influx/queries"
)

// QueryRequest is query that will be converted to a queryConfig
type QueryRequest struct {
	ID    string `json:"id"`
	Query string `json:"query"`
}

// QueriesRequest converts all queries to queryConfigs with the help
// of the template variables
type QueriesRequest struct {
	Queries      []QueryRequest          `json:"queries"`
	TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"`
}

// QueryResponse is the return result of a QueryRequest including
// the raw query, the templated query, the queryConfig and the queryAST
type QueryResponse struct {
	ID             string                   `json:"id"`
	Query          string                   `json:"query"`
	QueryConfig    chronograf.QueryConfig   `json:"queryConfig"`
	QueryAST       *queries.SelectStatement `json:"queryAST,omitempty"`
	QueryTemplated *string                  `json:"queryTemplated,omitempty"`
	TemplateVars   chronograf.TemplateVars  `json:"tempVars,omitempty"`
}

// QueriesResponse is the response for a QueriesRequest
type QueriesResponse struct {
	Queries []QueryResponse `json:"queries"`
}

// Queries parses InfluxQL and returns mostly importantly a structured QueryConfig
func (s *Service) Queries(w http.ResponseWriter, r *http.Request) {
	srcID, err := paramID("id", r)
	if err != nil {
		Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
		return
	}

	ctx := r.Context()
	src, err := s.SourcesStore.Get(ctx, srcID)
	if err != nil {
		notFound(w, srcID, s.Logger)
		return
	}

	var req QueriesRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		invalidJSON(w, s.Logger)
		return
	}

	res := QueriesResponse{
		Queries: make([]QueryResponse, len(req.Queries)),
	}

	for i, q := range req.Queries {
		qr := QueryResponse{
			ID:    q.ID,
			Query: q.Query,
		}

		query := influx.TemplateReplace(q.Query, req.TemplateVars)
		qc := ToQueryConfig(query)
		if err := s.DefaultRP(ctx, &qc, &src); err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}
		qr.QueryConfig = qc

		if stmt, err := queries.ParseSelect(query); err == nil {
			qr.QueryAST = stmt
		}

		if len(req.TemplateVars) > 0 {
			qr.TemplateVars = req.TemplateVars
			qr.QueryConfig.RawText = &qr.Query
			qr.QueryTemplated = &query
		}

		qr.QueryConfig.ID = q.ID
		res.Queries[i] = qr
	}

	encodeJSON(w, http.StatusOK, res, s.Logger)
}

// DefaultRP will add the default retention policy to the QC if one has not been specified
func (s *Service) DefaultRP(ctx context.Context, qc *chronograf.QueryConfig, src *chronograf.Source) error {
	// Only need to find the default RP IFF the qc's rp is empty
	if qc.RetentionPolicy != "" {
		return nil
	}

	// For queries without databases, measurements, or fields we will not
	// be able to find an RP
	if qc.Database == "" || qc.Measurement == "" || len(qc.Fields) == 0 {
		return nil
	}

	db := s.Databases
	if err := db.Connect(ctx, src); err != nil {
		return fmt.Errorf("Unable to connect to source: %v", err)
	}

	rps, err := db.AllRP(ctx, qc.Database)
	if err != nil {
		return fmt.Errorf("Unable to load RPs from DB %s: %v", qc.Database, err)
	}

	for _, rp := range rps {
		if rp.Default {
			qc.RetentionPolicy = rp.Name
			return nil
		}
	}

	return nil
}
<commit_msg>Update queries endpoint comment<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"

	"golang.org/x/net/context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/influx"
	"github.com/influxdata/chronograf/influx/queries"
)

// QueryRequest is query that will be converted to a queryConfig
type QueryRequest struct {
	ID    string `json:"id"`
	Query string `json:"query"`
}

// QueriesRequest converts all queries to queryConfigs with the help
// of the template variables
type QueriesRequest struct {
	Queries      []QueryRequest          `json:"queries"`
	TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"`
}

// QueryResponse is the return result of a QueryRequest including
// the raw query, the templated query, the queryConfig and the queryAST
type QueryResponse struct {
	ID             string                   `json:"id"`
	Query          string                   `json:"query"`
	QueryConfig    chronograf.QueryConfig   `json:"queryConfig"`
	QueryAST       *queries.SelectStatement `json:"queryAST,omitempty"`
	QueryTemplated *string                  `json:"queryTemplated,omitempty"`
	TemplateVars   chronograf.TemplateVars  `json:"tempVars,omitempty"`
}

// QueriesResponse is the response for a QueriesRequest
type QueriesResponse struct {
	Queries []QueryResponse `json:"queries"`
}

// Queries analyzes InfluxQL to produce front-end friendly QueryConfig
func (s *Service) Queries(w http.ResponseWriter, r *http.Request) {
	srcID, err := paramID("id", r)
	if err != nil {
		Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
		return
	}

	ctx := r.Context()
	src, err := s.SourcesStore.Get(ctx, srcID)
	if err != nil {
		notFound(w, srcID, s.Logger)
		return
	}

	var req QueriesRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		invalidJSON(w, s.Logger)
		return
	}

	res := QueriesResponse{
		Queries: make([]QueryResponse, len(req.Queries)),
	}

	for i, q := range req.Queries {
		qr := QueryResponse{
			ID:    q.ID,
			Query: q.Query,
		}

		query := influx.TemplateReplace(q.Query, req.TemplateVars)
		qc := ToQueryConfig(query)
		if err := s.DefaultRP(ctx, &qc, &src); err != nil {
			Error(w, http.StatusBadRequest, err.Error(), s.Logger)
			return
		}
		qr.QueryConfig = qc

		if stmt, err := queries.ParseSelect(query); err == nil {
			qr.QueryAST = stmt
		}

		if len(req.TemplateVars) > 0 {
			qr.TemplateVars = req.TemplateVars
			qr.QueryConfig.RawText = &qr.Query
			qr.QueryTemplated = &query
		}

		qr.QueryConfig.ID = q.ID
		res.Queries[i] = qr
	}

	encodeJSON(w, http.StatusOK, res, s.Logger)
}

// DefaultRP will add the default retention policy to the QC if one has not been specified
func (s *Service) DefaultRP(ctx context.Context, qc *chronograf.QueryConfig, src *chronograf.Source) error {
	// Only need to find the default RP IFF the qc's rp is empty
	if qc.RetentionPolicy != "" {
		return nil
	}

	// For queries without databases, measurements, or fields we will not
	// be able to find an RP
	if qc.Database == "" || qc.Measurement == "" || len(qc.Fields) == 0 {
		return nil
	}

	db := s.Databases
	if err := db.Connect(ctx, src); err != nil {
		return fmt.Errorf("Unable to connect to source: %v", err)
	}

	rps, err := db.AllRP(ctx, qc.Database)
	if err != nil {
		return fmt.Errorf("Unable to load RPs from DB %s: %v", qc.Database, err)
	}

	for _, rp := range rps {
		if rp.Default {
			qc.RetentionPolicy = rp.Name
			return nil
		}
	}

	return nil
}
<|endoftext|>