<commit_before>//
//GTK3 implementation of newyearsbot
//
package main

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
	"github.com/ugjka/newyearsbot/nyb"
)

var icon = "../icon.png"

func main() {
	var st Status
	st.logStopper = make(chan bool)
	var mv Window
	mv.ircServer = "irc.freenode.net:7000"
	mv.ircUseTLS = true
	mv.ircTrigger = "hny"
	bot := &nyb.Settings{}

	st.onClose = func() {
		st.logStopper <- true
		bot.Stop()
		st.Close()
		mv.setActive()
	}

	mv.startBot = func() {
		bot = &nyb.Settings{}
		bot.IrcChans = mv.ircChannels
		bot.IrcNick = mv.ircNick
		bot.IrcTrigger = mv.ircTrigger
		bot.IrcServer = mv.ircServer
		bot.UseTLS = mv.ircUseTLS
		bot.OSM = mv.ircOSM
		bot.Email = mv.ircEmail
		bot.Stopper = make(chan bool)
		bot.LogCh = nyb.NewLogChan()
		bot.IrcObj = nyb.NewIrcObj()
		go bot.Start()
	}
	mv.onClose = func() {
		gtk.MainQuit()
	}
	mv.onHide = func() {
		if st.isOpen {
			return
		}
		mv.setInactive()
		st.Open()
		mv.startBot()
		go func(s *Status) {
			for {
				var logmsg string
				select {
				case <-s.logStopper:
					return
				case logmsg = <-bot.LogCh:
					_, err := glib.IdleAdd(s.addMessage, logmsg)
					fatal(err)
				}
			}
		}(&st)
	}
	gtk.Init(nil)
	mv.open()
	gtk.Main()
}

//Window contains top level window
type Window struct {
	ircChannels []string
	ircServer   string
	ircUseTLS   bool
	ircNick     string
	ircTrigger  string
	ircOSM      bool
	ircEmail    string

	onClose  func()
	onHide   func()
	startBot func()

	isOpen bool

	window  *gtk.Window
	chans   *gtk.Entry
	server  *gtk.Entry
	nick    *gtk.Entry
	trigger *gtk.Entry
	tls     *gtk.CheckButton
	start   *gtk.Button
	stop    *gtk.Button
	osm     *gtk.CheckButton
	email   *gtk.Entry
}

func (w *Window) open() {
	if w.isOpen {
		return
	}
	w.initWidgets()
	w.fillInputs()
	w.isOpen = true
}

func (w *Window) close() {
	if !w.isOpen {
		return
	}
	w.window.Destroy()
	w.isOpen = false
}

func (w *Window) fillInputs() {
	w.nick.SetText(w.ircNick)
	chans := ""
	for i, ch := range w.ircChannels {
		chans += ch
		if i != len(w.ircChannels)-1 {
			chans += ", "
		}
	}
	w.chans.SetText(chans)
	w.trigger.SetText(w.ircTrigger)
	w.server.SetText(w.ircServer)
	w.tls.SetActive(w.ircUseTLS)
}

func (w *Window) initWidgets() {
	var err error
	w.window, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	fatal(err)
	w.window.SetTitle("New Year Irc Party Bot")
	w.window.SetPosition(gtk.WIN_POS_CENTER)
	w.window.SetSizeRequest(640, 320)
	w.window.SetBorderWidth(6)
	w.window.SetIconFromFile(icon)
	_, err = w.window.Connect("destroy", w.windowDestroyed)
	fatal(err)
	grid, err := gtk.GridNew()
	fatal(err)
	grid.SetColumnHomogeneous(true)
	grid.SetColumnSpacing(6)
	grid.SetRowSpacing(6)
	w.window.Add(grid)
	config, err := gtk.FrameNew("Configuration:")
	fatal(err)
	config.SetBorderWidth(6)
	grid.Attach(config, 0, 0, 1, 1)
	grid2, err := gtk.GridNew()
	fatal(err)
	grid2.SetColumnHomogeneous(true)
	grid2.SetColumnSpacing(6)
	grid2.SetRowSpacing(6)
	grid2.SetBorderWidth(6)
	grid2.Attach(labelNew("Irc nick:"), 0, 0, 1, 1)
	w.nick, err = gtk.EntryNew()
	grid2.Attach(w.nick, 0, 1, 1, 1)
	grid2.Attach(labelNew("Bot trigger for queries:"), 0, 2, 1, 1)
	w.trigger, err = gtk.EntryNew()
	fatal(err)
	grid2.Attach(w.trigger, 0, 3, 1, 1)
	grid2.Attach(labelNew("Irc channels (comma seperated):"), 0, 4, 1, 1)
	w.chans, err = gtk.EntryNew()
	fatal(err)
	grid2.Attach(w.chans, 0, 5, 1, 1)
	grid2.Attach(labelNew("Irc server (host:port):"), 0, 6, 1, 1)
	w.server, err = gtk.EntryNew()
	fatal(err)
	w.server.SetText(w.ircServer)
	grid2.Attach(w.server, 0, 7, 1, 1)
	grid2.Attach(labelNew("Use TLS:"), 0, 8, 1, 1)
	w.tls, err = gtk.CheckButtonNew()
	fatal(err)
	w.tls.SetActive(w.ircUseTLS)
	w.tls.SetHAlign(gtk.ALIGN_END)
	grid2.Attach(w.tls, 0, 9, 1, 1)
	grid2.Attach(labelNew("Use Open Street Map:"), 0, 10, 1, 1)
	w.osm, err = gtk.CheckButtonNew()
	fatal(err)
	w.osm.SetActive(w.ircOSM)
	w.osm.SetHAlign(gtk.ALIGN_END)
	grid2.Attach(w.osm, 0, 11, 1, 1)
	grid2.Attach(labelNew("Open Street Map refferer Email:"), 0, 12, 1, 1)
	w.email, err = gtk.EntryNew()
	fatal(err)
	w.email.SetText(w.ircEmail)
	grid2.Attach(w.email, 0, 13, 1, 1)
	config.Add(grid2)
	w.start, err = gtk.ButtonNew()
	fatal(err)
	w.start.SetLabel("Start")
	w.start.SetHAlign(gtk.ALIGN_CENTER)
	w.start.Connect("clicked", w.startClicked)
	grid.Attach(w.start, 0, 1, 1, 2)
	w.window.ShowAll()
}

func (w *Window) setInactive() {
	w.window.SetVisible(false)
}

func (w *Window) setActive() {
	w.window.SetVisible(true)
}

func (w *Window) startClicked() {
	if err := w.validateInputs(); err != nil {
		msg := gtk.MessageDialogNew(w.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
			gtk.BUTTONS_CLOSE, "%s", err.Error())
		_, err := msg.Connect("response", func() {
			msg.Destroy()
		})
		fatal(err)
		msg.ShowAll()
	} else {
		var err error
		w.ircNick, err = w.nick.GetText()
		fatal(err)
		chans, err := w.chans.GetText()
		fatal(err)
		w.ircChannels = make([]string, 0)
		for _, ch := range strings.Split(chans, ", ") {
			w.ircChannels = append(w.ircChannels, ch)
		}
		w.ircServer, err = w.server.GetText()
		fatal(err)
		w.ircUseTLS = w.tls.GetActive()
		w.ircTrigger, err = w.trigger.GetText()
		fatal(err)
		w.ircOSM = w.osm.GetActive()
		w.ircEmail, err = w.email.GetText()
		fatal(err)
		w.onHide()
	}

}

func (w *Window) validateInputs() error {
	nickreg := regexp.MustCompile("^\\w+$")
	nick, err := w.nick.GetText()
	fatal(err)
	if nick == "" {
		return fmt.Errorf("Empty nick")
	}
	if !nickreg.MatchString(nick) {
		return fmt.Errorf("Nick contains non alpha numeric characters")
	}
	chanreg := regexp.MustCompile("^#+\\w+$")
	chans, err := w.chans.GetText()
	fatal(err)
	for _, ch := range strings.Split(chans, ", ") {
		if !chanreg.MatchString(ch) || len(ch) <= 1 {
			return fmt.Errorf("Invalid channel name: %s", ch)
		}
	}
	serverreg := regexp.MustCompile("^\\S+:\\d+$")
	server, err := w.server.GetText()
	fatal(err)
	if !serverreg.MatchString(server) {
		return fmt.Errorf("Invalid irc server name")
	}
	triggerreg := regexp.MustCompile("^\\w+$")
	trigger, err := w.trigger.GetText()
	if len(trigger) <= 0 {
		return fmt.Errorf("Empty trigger")
	}
	fatal(err)
	if !triggerreg.MatchString(trigger) {
		return (fmt.Errorf("Trigger contains nonalphanumeric characters"))
	}
	return nil
}

func (w *Window) windowDestroyed() {
	w.onClose()
}

func fatal(err error) {
	if err != nil {
		panic(err)
	}
}

func labelNew(s string) *gtk.Label {
	l, err := gtk.LabelNew(s)
	fatal(err)
	l.SetHAlign(gtk.ALIGN_START)
	return l
}
<commit_msg>adjust the gui for the new changes<commit_after>//
//GTK3 implementation of newyearsbot
//
package main

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/badoux/checkmail"
	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
	"github.com/ugjka/newyearsbot/nyb"
	"mvdan.cc/xurls"
)

var icon = "../icon.png"

func main() {
	var st Status
	st.logStopper = make(chan bool)
	var mv Window
	mv.ircServer = "irc.freenode.net:7000"
	mv.ircUseTLS = true
	mv.ircTrigger = "hny"
	mv.ircNominatim = "http://nominatim.openstreetmap.org"
	bot := &nyb.Settings{}

	st.onClose = func() {
		st.logStopper <- true
		bot.Stop()
		st.Close()
		mv.setActive()
	}

	mv.startBot = func() {
		bot = &nyb.Settings{}
		bot.IrcChans = mv.ircChannels
		bot.IrcNick = mv.ircNick
		bot.IrcTrigger = mv.ircTrigger
		bot.IrcServer = mv.ircServer
		bot.UseTLS = mv.ircUseTLS
		bot.Email = mv.ircEmail
		bot.Stopper = make(chan bool)
		bot.LogCh = nyb.NewLogChan()
		bot.IrcObj = nyb.NewIrcObj()
		bot.Nominatim = mv.ircNominatim
		go bot.Start()
	}
	mv.onClose = func() {
		gtk.MainQuit()
	}
	mv.onHide = func() {
		if st.isOpen {
			return
		}
		mv.setInactive()
		st.Open()
		mv.startBot()
		go func(s *Status) {
			for {
				var logmsg string
				select {
				case <-s.logStopper:
					return
				case logmsg = <-bot.LogCh:
					_, err := glib.IdleAdd(s.addMessage, logmsg)
					fatal(err)
				}
			}
		}(&st)
	}
	gtk.Init(nil)
	mv.open()
	gtk.Main()
}

//Window contains top level window
type Window struct {
	ircChannels  []string
	ircServer    string
	ircUseTLS    bool
	ircNick      string
	ircTrigger   string
	ircEmail     string
	ircNominatim string

	onClose  func()
	onHide   func()
	startBot func()

	isOpen bool

	window    *gtk.Window
	chans     *gtk.Entry
	server    *gtk.Entry
	nick      *gtk.Entry
	trigger   *gtk.Entry
	tls       *gtk.CheckButton
	start     *gtk.Button
	stop      *gtk.Button
	email     *gtk.Entry
	nominatim *gtk.Entry
}

func (w *Window) open() {
	if w.isOpen {
		return
	}
	w.initWidgets()
	w.fillInputs()
	w.isOpen = true
}

func (w *Window) close() {
	if !w.isOpen {
		return
	}
	w.window.Destroy()
	w.isOpen = false
}

func (w *Window) fillInputs() {
	w.nick.SetText(w.ircNick)
	chans := ""
	for i, ch := range w.ircChannels {
		chans += ch
		if i != len(w.ircChannels)-1 {
			chans += ", "
		}
	}
	w.chans.SetText(chans)
	w.trigger.SetText(w.ircTrigger)
	w.server.SetText(w.ircServer)
	w.tls.SetActive(w.ircUseTLS)
	w.nominatim.SetText(w.ircNominatim)
}

func (w *Window) initWidgets() {
	var err error
	w.window, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	fatal(err)
	w.window.SetTitle("New Year Irc Party Bot")
	w.window.SetPosition(gtk.WIN_POS_CENTER)
	w.window.SetSizeRequest(640, 320)
	w.window.SetBorderWidth(6)
	w.window.SetIconFromFile(icon)
	_, err = w.window.Connect("destroy", w.windowDestroyed)
	fatal(err)
	grid, err := gtk.GridNew()
	fatal(err)
	grid.SetColumnHomogeneous(true)
	grid.SetColumnSpacing(6)
	grid.SetRowSpacing(6)
	w.window.Add(grid)
	config, err := gtk.FrameNew("Configuration:")
	fatal(err)
	config.SetBorderWidth(6)
	grid.Attach(config, 0, 0, 1, 1)
	grid2, err := gtk.GridNew()
	fatal(err)
	grid2.SetColumnHomogeneous(true)
	grid2.SetColumnSpacing(6)
	grid2.SetRowSpacing(6)
	grid2.SetBorderWidth(6)
	grid2.Attach(labelNew("Irc nick:"), 0, 0, 1, 1)
	w.nick, err = gtk.EntryNew()
	grid2.Attach(w.nick, 0, 1, 1, 1)
	grid2.Attach(labelNew("Bot trigger for queries:"), 0, 2, 1, 1)
	w.trigger, err = gtk.EntryNew()
	fatal(err)
	grid2.Attach(w.trigger, 0, 3, 1, 1)
	grid2.Attach(labelNew("Irc channels (comma seperated):"), 0, 4, 1, 1)
	w.chans, err = gtk.EntryNew()
	fatal(err)
	grid2.Attach(w.chans, 0, 5, 1, 1)
	grid2.Attach(labelNew("Irc server (host:port):"), 0, 6, 1, 1)
	w.server, err = gtk.EntryNew()
	fatal(err)
	w.server.SetText(w.ircServer)
	grid2.Attach(w.server, 0, 7, 1, 1)
	grid2.Attach(labelNew("Use TLS:"), 0, 8, 1, 1)
	w.tls, err = gtk.CheckButtonNew()
	fatal(err)
	w.tls.SetActive(w.ircUseTLS)
	w.tls.SetHAlign(gtk.ALIGN_END)
	grid2.Attach(w.tls, 0, 9, 1, 1)
	grid2.Attach(labelNew("Nominatim server:"), 0, 10, 1, 1)
	w.nominatim, err = gtk.EntryNew()
	fatal(err)
	w.nominatim.SetText(w.ircNominatim)
	grid2.Attach(w.nominatim, 0, 11, 1, 1)
	grid2.Attach(labelNew("Nominatim refferer Email:"), 0, 12, 1, 1)
	w.email, err = gtk.EntryNew()
	fatal(err)
	w.email.SetText(w.ircEmail)
	grid2.Attach(w.email, 0, 13, 1, 1)
	config.Add(grid2)
	w.start, err = gtk.ButtonNew()
	fatal(err)
	w.start.SetLabel("Start")
	w.start.SetHAlign(gtk.ALIGN_CENTER)
	w.start.Connect("clicked", w.startClicked)
	grid.Attach(w.start, 0, 1, 1, 2)
	w.window.ShowAll()
}

func (w *Window) setInactive() {
	w.window.SetVisible(false)
}

func (w *Window) setActive() {
	w.window.SetVisible(true)
}

func (w *Window) startClicked() {
	if err := w.validateInputs(); err != nil {
		msg := gtk.MessageDialogNew(w.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
			gtk.BUTTONS_CLOSE, "%s", err.Error())
		_, err := msg.Connect("response", func() {
			msg.Destroy()
		})
		fatal(err)
		msg.ShowAll()
	} else {
		var err error
		w.ircNick, err = w.nick.GetText()
		fatal(err)
		chans, err := w.chans.GetText()
		fatal(err)
		w.ircChannels = make([]string, 0)
		for _, ch := range strings.Split(chans, ", ") {
			w.ircChannels = append(w.ircChannels, ch)
		}
		w.ircServer, err = w.server.GetText()
		fatal(err)
		w.ircUseTLS = w.tls.GetActive()
		w.ircTrigger, err = w.trigger.GetText()
		fatal(err)
		w.ircEmail, err = w.email.GetText()
		fatal(err)
		w.ircNominatim, err = w.nominatim.GetText()
		fatal(err)
		w.onHide()
	}

}

func (w *Window) validateInputs() error {
	nickreg := regexp.MustCompile("^\\S+$")
	nick, err := w.nick.GetText()
	fatal(err)
	if nick == "" {
		return fmt.Errorf("Empty nick")
	}
	if !nickreg.MatchString(nick) {
		return fmt.Errorf("Nick contains whitespace characters")
	}
	chanreg := regexp.MustCompile("^#+\\S+$")
	chans, err := w.chans.GetText()
	fatal(err)
	for _, ch := range strings.Split(chans, ", ") {
		if !chanreg.MatchString(ch) || len(ch) <= 1 {
			return fmt.Errorf("Invalid channel name: %s", ch)
		}
	}
	serverreg := regexp.MustCompile("^\\S+:\\d+$")
	server, err := w.server.GetText()
	fatal(err)
	if !serverreg.MatchString(server) {
		return fmt.Errorf("Invalid irc server name")
	}
	triggerreg := regexp.MustCompile("^\\S+$")
	trigger, err := w.trigger.GetText()
	fatal(err)
	if len(trigger) <= 0 {
		return fmt.Errorf("Empty trigger")
	}
	if !triggerreg.MatchString(trigger) {
		return (fmt.Errorf("Trigger contains whitespace characters"))
	}
	nominatim, err := w.nominatim.GetText()
	fatal(err)
	if nominatim == "" {
		return fmt.Errorf("No nominatim server defined")
	}
	if !xurls.Strict().MatchString(nominatim) {
		return fmt.Errorf("Invalid nominatim server url")
	}
	email, err := w.email.GetText()
	fatal(err)
	if email == "" {
		return fmt.Errorf("You must enter valid email")
	}
	if err := checkmail.ValidateFormat(email); err != nil {
		return fmt.Errorf("Invalid email adress")
	}
	return nil
}

func (w *Window) windowDestroyed() {
	w.onClose()
}

func fatal(err error) {
	if err != nil {
		panic(err)
	}
}

func labelNew(s string) *gtk.Label {
	l, err := gtk.LabelNew(s)
	fatal(err)
	l.SetHAlign(gtk.ALIGN_START)
	return l
}
<|endoftext|>


<commit_before>// Package oauth implements a convenience function for doing the Oauth exchange.
package oauth

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const (
	// Oauth client ID.
	ClientId = "457311175792-n3hpckfadgri6opat70c8an1fmhmaev7.apps.googleusercontent.com"
	// Oauth client secret.
	Secret = "GOylH6-BUUQFm_lzrhXKpdac"
)

func GetOAuthClient(ctx context.Context, cfg *oauth2.Config) (*oauth2.Token, error) {
	tok := new(oauth2.Token)
	// Have to get a new token.
	browser := os.Getenv("OAUTH") != "NOBROWSER"
	code := ""
	var err error
	if browser {
		print("Launching browser for OAuth exchange. To skip, rerun with environment variable 'OAUTH' set to 'NOBROWSER'.\n")
		code, err = tokenFromWeb(ctx, cfg)
	}
	if err != nil || !browser {
		// Fall back to non-browser auth by rewriting the redirect URL and reading the auth code from stdin.
		cfg.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"
		authURL := cfg.AuthCodeURL("")
		fmt.Printf("Authorize this app at %s and paste the authorization code.\n> ", authURL)
		_, err = fmt.Scanf("%s", &code)
	}
	tok, err = cfg.Exchange(ctx, code)
	return tok, nil
}

func tokenFromWeb(ctx context.Context, config *oauth2.Config) (string, error) {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		http.Error(rw, "", 500)
	}))
	defer ts.Close()
	config.RedirectURL = ts.URL
	authURL := config.AuthCodeURL(randState)
	errs := make(chan error)
	go func() {
		err := openURL(authURL)
		errs <- err
	}()
	err := <-errs
	if err == nil {
		code := <-ch
		return code, nil
	} else {
		return "", err
	}
}

func openURL(url string) error {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return nil
		}
	}
	return fmt.Errorf("Error opening URL in browser.")
}
<commit_msg>Eliminate OOB Oauth flow, since it's no longer supported.<commit_after>// Package oauth implements a convenience function for doing the Oauth exchange.
package oauth

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"os/exec"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const (
	// Oauth client ID.
	ClientId = "457311175792-n3hpckfadgri6opat70c8an1fmhmaev7.apps.googleusercontent.com"
	// Oauth client secret.
	Secret = "GOylH6-BUUQFm_lzrhXKpdac"
)

func GetOAuthClient(ctx context.Context, cfg *oauth2.Config) (*oauth2.Token, error) {
	tok := new(oauth2.Token)
	// Have to get a new token.
	print("Launching browser for OAuth exchange. To skip, rerun with environment variable 'OAUTH' set to 'NOBROWSER'.\n")
	code, err := tokenFromWeb(ctx, cfg)
	if err == nil {
		tok, err = cfg.Exchange(ctx, code)
	}
	return tok, err
}

func tokenFromWeb(ctx context.Context, config *oauth2.Config) (string, error) {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		http.Error(rw, "", 500)
	}))
	defer ts.Close()
	config.RedirectURL = ts.URL
	authURL := config.AuthCodeURL(randState)
	errs := make(chan error)
	go func() {
		err := openURL(authURL)
		errs <- err
	}()
	err := <-errs
	if err == nil {
		code := <-ch
		return code, nil
	} else {
		return "", err
	}
}

func openURL(url string) error {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return nil
		}
	}
	fmt.Printf("Open %v in your browser.", url)
	return nil
}
<|endoftext|>


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

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")
	checkNodeCount = flag.Bool("check_node_count", true, ""+
		"By default, verify that the cluster has at least two nodes."+
		"You can explicitly set to false if you're, e.g., testing single-node clusters "+
		"for which the node count is supposed to be one.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	minNodeCount = 2
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	os.Setenv("KUBECTL", *root+`/cluster/kubectl.sh`+kubectlArgs())

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(*root, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}
	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	if os.Getenv("FEDERATION") == "true" {
		//TODO(colhom): federated equivalent of  ValidateClusterSize
		return
	}
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	if *checkNodeCount {
		ValidateClusterSize()
	}

	if os.Getenv("FEDERATION") == "true" {
		if *testArgs == "" {
			*testArgs = "--ginkgo.focus=\\[Feature:Federation\\]"
		}
		return finishRunning("Federated Ginkgo tests", exec.Command(filepath.Join(*root, "hack/federated-ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
	} else {
		return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
	}
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	args := []string{""}
	if *checkVersionSkew {
		args = append(args, "--match-server-version")
	}
	return strings.Join(args, " ")
}
<commit_msg>Move kubemark, skew, upgrade, leak logic into hack/e2e.go<commit_after>/*
Copyright 2014 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.
*/

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	build          = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	checkNodeCount = flag.Bool("check_node_count", true, ""+
		"By default, verify that the cluster has at least two nodes."+
		"You can explicitly set to false if you're, e.g., testing single-node clusters "+
		"for which the node count is supposed to be one.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")
	checkLeakedResources = flag.Bool("check_leaked_resources", false, "Ensure project ends with the same resources")
	ctlCmd               = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v.")
	down                 = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	dump                 = flag.String("dump", "", "If set, dump cluster logs to this location")
	kubemark             = flag.Bool("kubemark", false, "If true, run kubemark tests.")
	isup                 = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	push                 = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup               = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	skewTests            = flag.Bool("skew", false, "If true, run tests in another version at ../kubernetes/hack/e2e.go")
	testArgs             = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	test                 = flag.Bool("test", false, "Run Ginkgo tests.")
	up                   = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	upgradeArgs          = flag.String("upgrade_args", "", "If set, run upgrade tests before other tests")
	verbose              = flag.Bool("v", false, "If true, print all command output.")
)

const (
	minNodeCount = 2
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("Could not get pwd: %v", err)
	}
	acwd, err := filepath.Abs(cwd)
	if err != nil {
		log.Fatalf("Failed to convert to an absolute path: %v", err)
	}
	if !strings.Contains(filepath.Base(acwd), "kubernetes") {
		// TODO(fejta): cd up into  the kubernetes directory
		log.Fatalf("Must run from kubernetes directory: %v", cwd)
	}

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command("make", "quick-release")
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *up && !TearDown() {
		log.Fatal("Could not tear down previous cluster")
	}

	beforeResources := ""
	if *checkLeakedResources {
		beforeResources = ListResources()
	}

	os.Setenv("KUBECTL", strings.Join(append([]string{"./cluster/kubectl.sh"}, kubectlArgs()...), " "))

	if *upgradeArgs != "" { // Start the cluster using a previous version.
		if !UpgradeUp() {
			log.Fatal("Failed to start cluster to upgrade. Aborting.")
		}
	} else { // Start the cluster using this version.
		if *pushup {
			if IsUp() {
				log.Printf("e2e cluster is up, pushing.")
				*up = false
				*push = true
			} else {
				log.Printf("e2e cluster is down, creating.")
				*up = true
				*push = false
			}
		}
		if *up {
			if !Up() {
				log.Fatal("Error starting e2e cluster. Aborting.")
			}
		} else if *push {
			if !finishRunning("push", exec.Command("./hack/e2e-internal/e2e-push.sh")) {
				log.Fatal("Error pushing e2e cluster. Aborting.")
			}
		}
	}

	upResources := ""
	if *checkLeakedResources {
		upResources = ListResources()
	}

	success := true

	if *ctlCmd != "" {
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		ctlSuccess := finishRunning("'kubectl "+*ctlCmd+"'", exec.Command("./cluster/kubectl.sh", ctlArgs...))
		success = success && ctlSuccess
	}

	if *upgradeArgs != "" {
		upgradeSuccess := UpgradeTest(*upgradeArgs)
		success = success && upgradeSuccess
	}

	if *test {
		if *skewTests {
			skewSuccess := SkewTest()
			success = success && skewSuccess
		} else {
			testSuccess := Test()
			success = success && testSuccess
		}
	}

	if *kubemark {
		kubeSuccess := KubemarkTest()
		success = success && kubeSuccess
	}

	if *down {
		if !success && *dump != "" {
			DumpClusterLogs(*dump)
		}
		tearSuccess := TearDown()
		success = success && tearSuccess
	}

	if *checkLeakedResources {
		log.Print("Sleeping for 30 seconds...") // Wait for eventually consistent listing
		time.Sleep(30 * time.Second)
		DiffResources(beforeResources, upResources, ListResources(), *dump)
	}

	if !success {
		os.Exit(1)
	}
}

func writeOrDie(dir, name, data string) string {
	f, err := os.Create(filepath.Join(dir, name))
	if err != nil {
		log.Fatal(err)
	}
	if _, err := f.WriteString(data); err != nil {
		log.Fatal(err)
	}
	if err := f.Close(); err != nil {
		log.Fatal(err)
	}
	log.Printf("Created file: %s", f.Name())
	return f.Name()
}

func DiffResources(before, clusterUp, after, location string) {
	if location == "" {
		var err error
		location, err = ioutil.TempDir("", "e2e-check-resources")
		if err != nil {
			log.Fatal(err)
		}
	}
	bp := writeOrDie(location, "gcp-resources-before.txt", before)
	writeOrDie(location, "gcp-resources-cluster-up.txt", clusterUp)
	ap := writeOrDie(location, "gcp-resources-after.txt", after)

	cmd := exec.Command("diff", "-sw", "-U0", "-F^\\[.*\\]$", bp, ap)
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	o, err := cmd.Output()
	stdout := string(o)
	writeOrDie(location, "gcp-resources-diff.txt", stdout)
	if err == nil {
		return
	}
	lines := strings.Split(stdout, "\n")
	if len(lines) < 3 { // Ignore the +++ and --- header lines
		return
	}

	var added []string
	for _, l := range lines {
		if strings.HasPrefix(l, "+") {
			added = append(added, l)
		}
	}
	if len(added) > 0 {
		log.Printf("Error: %d leaked resources", len(added))
		log.Fatal(strings.Join(added, "\n"))
	}
}

func ListResources() string {
	log.Printf("Listing resources...")
	cmd := exec.Command("./cluster/gce/list-resources.sh")
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Failed to list resources (%s):\n%s", err, stdout)
	}
	return string(stdout)
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command("./hack/e2e-internal/e2e-down.sh"))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	return finishRunning("up", exec.Command("./hack/e2e-internal/e2e-up.sh"))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command("./hack/e2e-internal/e2e-cluster-size.sh")
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command("./hack/e2e-internal/e2e-status.sh"))
}

func DumpClusterLogs(location string) {
	log.Printf("Dumping cluster logs to: %v", location)
	finishRunning("dump cluster logs", exec.Command("./cluster/log-dump.sh", location))
}

func KubemarkTest() bool {
	// Stop previous run
	if !finishRunning("Stop kubemark", exec.Command("./test/kubemark/stop-kubemark.sh")) {
		log.Print("stop kubemark failed")
		return false
	}

	// Start new run
	backups := []string{"NUM_NODES", "MASTER_SIZE"}
	for _, item := range backups {
		old, present := os.LookupEnv(item)
		if present {
			defer os.Setenv(item, old)
		} else {
			defer os.Unsetenv(item)
		}
	}
	os.Setenv("NUM_NODES", os.Getenv("KUBEMARK_NUM_NODES"))
	os.Setenv("MASTER_SIZE", os.Getenv("KUBEMARK_MASTER_SIZE"))
	if !finishRunning("Start Kubemark", exec.Command("./test/kubemark/start-kubemark.sh")) {
		log.Print("Error: start kubemark failed")
		return false
	}

	// Run kubemark tests
	focus, present := os.LookupEnv("KUBEMARK_TESTS")
	if !present {
		focus = "starting\\s30\\pods"
	}
	test_args := os.Getenv("KUBEMARK_TEST_ARGS")

	if !finishRunning("Run kubemark tests", exec.Command("./test/kubemark/run-e2e-tests.sh", "--ginkgo.focus="+focus, test_args)) {
		log.Print("Error: run kubemark tests failed")
		return false
	}

	// Stop kubemark
	if !finishRunning("Stop kubemark", exec.Command("./test/kubemark/stop-kubemark.sh")) {
		log.Print("Error: stop kubemark failed")
		return false
	}
	return true
}

func UpgradeUp() bool {
	old, err := os.Getwd()
	if err != nil {
		log.Printf("Failed to os.Getwd(): %v", err)
		return false
	}
	defer os.Chdir(old)
	err = os.Chdir("../kubernetes_skew")
	if err != nil {
		log.Printf("Failed to cd ../kubernetes_skew: %v", err)
		return false
	}
	return finishRunning("UpgradeUp",
		exec.Command(
			"go", "run", "./hack/e2e.go",
			fmt.Sprintf("--check_version_skew=%t", *checkVersionSkew),
			fmt.Sprintf("--push=%t", *push),
			fmt.Sprintf("--pushup=%t", *pushup),
			fmt.Sprintf("--up=%t", *up),
			fmt.Sprintf("--v=%t", *verbose),
		))
}

func UpgradeTest(args string) bool {
	old, err := os.Getwd()
	if err != nil {
		log.Printf("Failed to os.Getwd(): %v", err)
		return false
	}
	defer os.Chdir(old)
	err = os.Chdir("../kubernetes_skew")
	if err != nil {
		log.Printf("Failed to cd ../kubernetes_skew: %v", err)
		return false
	}
	previous, present := os.LookupEnv("E2E_REPORT_PREFIX")
	if present {
		defer os.Setenv("E2E_REPORT_PREFIX", previous)
	} else {
		defer os.Unsetenv("E2E_REPORT_PREFIX")
	}
	os.Setenv("E2E_REPORT_PREFIX", "upgrade")
	return finishRunning("Upgrade Ginkgo tests",
		exec.Command(
			"go", "run", "./hack/e2e.go",
			"--test",
			"--test_args="+args,
			fmt.Sprintf("--v=%t", *verbose),
			fmt.Sprintf("--check_version_skew=%t", *checkVersionSkew)))
}

func SkewTest() bool {
	old, err := os.Getwd()
	if err != nil {
		log.Printf("Failed to Getwd: %v", err)
		return false
	}
	defer os.Chdir(old)
	err = os.Chdir("../kubernetes_skew")
	if err != nil {
		log.Printf("Failed to cd ../kubernetes_skew: %v", err)
		return false
	}
	return finishRunning("Skewed Ginkgo tests",
		exec.Command(
			"go", "run", "./hack/e2e.go",
			"--test",
			"--test_args="+*testArgs,
			fmt.Sprintf("--v=%t", *verbose),
			fmt.Sprintf("--check_version_skew=%t", *checkVersionSkew)))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	// TODO(fejta): add a --federated or something similar
	if os.Getenv("FEDERATION") == "" {
		if *checkNodeCount {
			ValidateClusterSize()
		}
		return finishRunning("Ginkgo tests", exec.Command("./hack/ginkgo-e2e.sh", strings.Fields(*testArgs)...))
	}

	if *testArgs == "" {
		*testArgs = "--ginkgo.focus=\\[Feature:Federation\\]"
	}
	return finishRunning("Federated Ginkgo tests", exec.Command("./hack/federated-ginkgo-e2e.sh", strings.Fields(*testArgs)...))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() []string {
	if !*checkVersionSkew {
		return []string{}
	}
	return []string{"--match-server-version"}
}
<|endoftext|>


<commit_before>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package web

import (
	"io"
	"net/http"
	"reflect"
)

// internal handler type. handler of slightly differing signatures are accepted
// but transformed (wrapped) early on to match this one.
type handlerf func(ctx *Context, arg ...string) error

// functions according to reflect
type valuefun func([]reflect.Value) []reflect.Value

var nilerr error
var nilerrv reflect.Value = reflect.ValueOf(&nilerr).Elem()
var errtype reflect.Type = reflect.TypeOf(&nilerr).Elem()

// Small optimization: cache the context type instead of repeteadly calling reflect.Typeof
var contextType reflect.Type = reflect.TypeOf(Context{})

//should the context be passed to the handler?
func requiresContext(handlerType reflect.Type) bool {
	//if the method doesn't take arguments, no
	if handlerType.NumIn() == 0 {
		return false
	}

	//if the first argument is not a pointer, no
	a0 := handlerType.In(0)
	if a0.Kind() != reflect.Ptr {
		return false
	}
	//if the first argument is a context, yes
	if a0.Elem() == contextType {
		return true
	}

	return false
}

// waiting for go1.1
func callableValue(fv reflect.Value) valuefun {
	if fv.Type().Kind() != reflect.Func {
		panic("not a function value")
	}
	return func(args []reflect.Value) []reflect.Value {
		return fv.Call(args)
	}
}

// Wrap f in a function that disregards its first arg
func disregardFirstArg(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		return f(args[1:])
	}
}

// Wrap f to return a nil error value in addition to current return values
func addNilErrorReturn(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		ret := f(args)
		return append(ret, nilerrv)
	}
}

// Wrap f to write its string return value to the first arg (being an io.Writer)
// requires the original function signature to be:
//
// func (io.Writer, ...) (string, error)
//
// signature of wrapped function:
//
// func (io.Writer, ...) error
//
// if the error value of the original call is not nil that value is passed back
// verbatim and no further action is taken. If it is nil the wrapper writes the
// string to the writer and returns whatever error ocurred there, if any.
//
// Note that wherever it says string []byte is also okay.
func writeStringToFirstArg(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		wv := args[0]
		w, ok := wv.Interface().(io.Writer)
		if !ok {
			panic("First argument must be an io.Writer")
		}
		ret := f(args)
		if len(ret) < 2 {
			panic("Two return values required for proper wrapping")
		}
		if i := ret[1].Interface(); i != nil {
			return ret[1:]
		}
		var ar []byte
		if i := ret[0].Interface(); i != nil {
			switch typed := i.(type) {
			case string:
				ar = []byte(typed)
				break
			case []byte:
				ar = typed
				break
			default:
				panic("First return value must be a byte array / string")
			}
		}
		_, err := w.Write(ar)
		if err != nil {
			return []reflect.Value{reflect.ValueOf(err)}
		}
		return []reflect.Value{nilerrv}
	}
}

func lastRetIsError(fv reflect.Value) bool {
	// type of fun
	t := fv.Type()
	if t.NumOut() == 0 {
		return false
	}
	// type of last return val
	t = t.Out(t.NumOut() - 1)
	return t.Implements(errtype)
}

func firstRetIsString(fv reflect.Value) bool {
	// type of fun
	t := fv.Type()
	if t.NumOut() == 0 {
		return false
	}
	// type of first return val
	t = t.Out(0)
	return t.AssignableTo(reflect.TypeOf("")) || t.AssignableTo(reflect.TypeOf([]byte{}))
}

// convert a value back to the original error interface. panics if value is not
// nil and also does not implement error.
func value2error(v reflect.Value) error {
	i := v.Interface()
	if i == nil {
		return nil
	}
	return i.(error)
}

// Beat the supplied handler into a uniform signature. panics if incompatible
// (may only happen when the wrapped fun is called)
func fixHandlerSignature(f interface{}) handlerf {
	// classic net/http.Hander implementors can easily be converted
	if httph, ok := f.(http.Handler); ok {
		return func(ctx *Context, args ...string) error {
			httph.ServeHTTP(ctx.ResponseWriter, ctx.Request)
			return nil
		}
	}
	fv := reflect.ValueOf(f)
	var callf valuefun = callableValue(fv)
	if !requiresContext(fv.Type()) {
		callf = disregardFirstArg(callf)
	}
	// now callf definitely accepts a *Context as its first arg
	if !lastRetIsError(fv) {
		callf = addNilErrorReturn(callf)
	}
	// now callf definitely returns an error as its last value
	if firstRetIsString(fv) {
		callf = writeStringToFirstArg(callf)
	}
	// now callf definitely does not return a string: just an error
	// wrap callf in a function with pretty signature
	return func(ctx *Context, args ...string) error {
		argvs := make([]reflect.Value, len(args)+1)
		argvs[0] = reflect.ValueOf(ctx)
		for i, arg := range args {
			argvs[i+1] = reflect.ValueOf(arg)
		}
		rets := callf(argvs)
		return value2error(rets[0])
	}
}

// this function must either be updated to new internal API or removed
func MethodHandler(val interface{}, name string) reflect.Value {
	v := reflect.ValueOf(val)
	typ := v.Type()
	n := typ.NumMethod()
	for i := 0; i < n; i++ {
		m := typ.Method(i)
		if m.Name == name {
			return v.Method(i)
		}
	}

	return reflect.ValueOf(nil)
}
<commit_msg>Remove MethodHandler (let user worry about that)<commit_after>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package web

import (
	"io"
	"net/http"
	"reflect"
)

// internal handler type. handler of slightly differing signatures are accepted
// but transformed (wrapped) early on to match this one.
type handlerf func(ctx *Context, arg ...string) error

// functions according to reflect
type valuefun func([]reflect.Value) []reflect.Value

var nilerr error
var nilerrv reflect.Value = reflect.ValueOf(&nilerr).Elem()
var errtype reflect.Type = reflect.TypeOf(&nilerr).Elem()

// Small optimization: cache the context type instead of repeteadly calling reflect.Typeof
var contextType reflect.Type = reflect.TypeOf(Context{})

//should the context be passed to the handler?
func requiresContext(handlerType reflect.Type) bool {
	//if the method doesn't take arguments, no
	if handlerType.NumIn() == 0 {
		return false
	}

	//if the first argument is not a pointer, no
	a0 := handlerType.In(0)
	if a0.Kind() != reflect.Ptr {
		return false
	}
	//if the first argument is a context, yes
	if a0.Elem() == contextType {
		return true
	}

	return false
}

// waiting for go1.1
func callableValue(fv reflect.Value) valuefun {
	if fv.Type().Kind() != reflect.Func {
		panic("not a function value")
	}
	return func(args []reflect.Value) []reflect.Value {
		return fv.Call(args)
	}
}

// Wrap f in a function that disregards its first arg
func disregardFirstArg(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		return f(args[1:])
	}
}

// Wrap f to return a nil error value in addition to current return values
func addNilErrorReturn(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		ret := f(args)
		return append(ret, nilerrv)
	}
}

// Wrap f to write its string return value to the first arg (being an io.Writer)
// requires the original function signature to be:
//
// func (io.Writer, ...) (string, error)
//
// signature of wrapped function:
//
// func (io.Writer, ...) error
//
// if the error value of the original call is not nil that value is passed back
// verbatim and no further action is taken. If it is nil the wrapper writes the
// string to the writer and returns whatever error ocurred there, if any.
//
// Note that wherever it says string []byte is also okay.
func writeStringToFirstArg(f valuefun) valuefun {
	return func(args []reflect.Value) []reflect.Value {
		wv := args[0]
		w, ok := wv.Interface().(io.Writer)
		if !ok {
			panic("First argument must be an io.Writer")
		}
		ret := f(args)
		if len(ret) < 2 {
			panic("Two return values required for proper wrapping")
		}
		if i := ret[1].Interface(); i != nil {
			return ret[1:]
		}
		var ar []byte
		if i := ret[0].Interface(); i != nil {
			switch typed := i.(type) {
			case string:
				ar = []byte(typed)
				break
			case []byte:
				ar = typed
				break
			default:
				panic("First return value must be a byte array / string")
			}
		}
		_, err := w.Write(ar)
		if err != nil {
			return []reflect.Value{reflect.ValueOf(err)}
		}
		return []reflect.Value{nilerrv}
	}
}

func lastRetIsError(fv reflect.Value) bool {
	// type of fun
	t := fv.Type()
	if t.NumOut() == 0 {
		return false
	}
	// type of last return val
	t = t.Out(t.NumOut() - 1)
	return t.Implements(errtype)
}

func firstRetIsString(fv reflect.Value) bool {
	// type of fun
	t := fv.Type()
	if t.NumOut() == 0 {
		return false
	}
	// type of first return val
	t = t.Out(0)
	return t.AssignableTo(reflect.TypeOf("")) || t.AssignableTo(reflect.TypeOf([]byte{}))
}

// convert a value back to the original error interface. panics if value is not
// nil and also does not implement error.
func value2error(v reflect.Value) error {
	i := v.Interface()
	if i == nil {
		return nil
	}
	return i.(error)
}

// Beat the supplied handler into a uniform signature. panics if incompatible
// (may only happen when the wrapped fun is called)
func fixHandlerSignature(f interface{}) handlerf {
	// classic net/http.Hander implementors can easily be converted
	if httph, ok := f.(http.Handler); ok {
		return func(ctx *Context, args ...string) error {
			httph.ServeHTTP(ctx.ResponseWriter, ctx.Request)
			return nil
		}
	}
	fv := reflect.ValueOf(f)
	var callf valuefun = callableValue(fv)
	if !requiresContext(fv.Type()) {
		callf = disregardFirstArg(callf)
	}
	// now callf definitely accepts a *Context as its first arg
	if !lastRetIsError(fv) {
		callf = addNilErrorReturn(callf)
	}
	// now callf definitely returns an error as its last value
	if firstRetIsString(fv) {
		callf = writeStringToFirstArg(callf)
	}
	// now callf definitely does not return a string: just an error
	// wrap callf in a function with pretty signature
	return func(ctx *Context, args ...string) error {
		argvs := make([]reflect.Value, len(args)+1)
		argvs[0] = reflect.ValueOf(ctx)
		for i, arg := range args {
			argvs[i+1] = reflect.ValueOf(arg)
		}
		rets := callf(argvs)
		return value2error(rets[0])
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/gorilla/mux"
	"github.com/rigingo/dlog"
)

type JournalVars struct {
	Title      string
	Desc       string
	PubStarred bool
	Count      string
	CssLookup  map[string]string
}

func Load(f string) JournalVars {
	jv := JournalVars{}

	jf, err := ioutil.ReadFile(filepath.Join(f, "conf", "dop.json"))
	if err != nil {
		dlog.Error.Fatalf("Unable to read the data file (%s): %s", f, err)
	}
	if err := json.Unmarshal(jf, &jv); err != nil {
		dlog.Error.Fatalf("Unable to Unmarshal DOP config from data file (%s): %s", jf, err)
	}
	dlog.Trace.Println(jv)
	return jv
}

func NotFound(id string, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusNotFound)
	if err := json.NewEncoder(w).Encode(jsonErr{Code: http.StatusNotFound, Text: "Not Found", Id: id}); err != nil {
		dlog.Error.Panic(err)
	}

}

func (jc *JournalConf) JsonAPI(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error
	var entry string
	var journals Journals
	//var jindex JIndex

	var jv JournalVars
	jv = Load(jc.DopRoot)

	entry = vars["entryId"]
	journals, _, err = Parse(entry, "", jc, &jv)
	if (err != nil) && (err.Error() == "NotFound") {
		NotFound(entry, w)
		return
	}

	b, err := json.MarshalIndent(journals, "", "    ")
	if err != nil {
		dlog.Error.Panicf("ERROR: encoding JSON: %s\n", err)
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func (jc *JournalConf) Index(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error
	var entry string
	var journals Journals
	var jindex JIndex
	var current Journals
	var list bool
	var search string
	var desc string

	var jv JournalVars
	jv = Load(jc.DopRoot)

	search = strings.Replace(vars["term"], "+", " ", -1)
	journals, jindex, err = Parse("*", search, jc, &jv)

	entry = vars["entryId"]
	dlog.Trace.Printf("entry_POST=%s", entry)
	if entry == "" {
		list = true
		desc = jv.Desc
		if len(journals) > 0 {
			entry = journals[0].Id
		} else {
			entry = ""
		}
	} else {
		list = false
		entry = jindex[entry]
	}

	dlog.Trace.Printf("entry_PARSE=%s", entry)
	dlog.Trace.Printf("len(jindex)=%s", len(jindex))

	current, _, err = Parse(entry, "", jc, &jv)
	if (err != nil) && (err.Error() == "NotFound") {
		NotFound(entry, w)
		return
	}

	if desc == "" {
		if desc = current[0].DopDesc; desc == "" {
			desc = jv.Desc
		}
	}

	var nextid string
	var previd string
	if currpos := journals.CurrPosition(entry); currpos != -1 {
		nextid = journals.NextId(currpos)
		previd = journals.PrevId(currpos)
	}

	page := Page{
		Title:     jv.Title,
		Desc:      desc,
		IsList:    list,
		PrevId:    previd,
		NextId:    nextid,
		HttpFQDN:  jc.HttpFQDN,
		EUrl:      jc.EUrl,
		TUrl:      jc.TUrl,
		Search:    search,
		CssLookup: jv.CssLookup,
		Navbar:    journals,
		Content:   current,
	}
	renderTemplate(w, "dop", &page)
}
<commit_msg>issue#1 - title changes for list/page<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/gorilla/mux"
	"github.com/rigingo/dlog"
)

type JournalVars struct {
	Title      string
	Desc       string
	PubStarred bool
	Count      string
	CssLookup  map[string]string
}

func Load(f string) JournalVars {
	jv := JournalVars{}

	jf, err := ioutil.ReadFile(filepath.Join(f, "conf", "dop.json"))
	if err != nil {
		dlog.Error.Fatalf("Unable to read the data file (%s): %s", f, err)
	}
	if err := json.Unmarshal(jf, &jv); err != nil {
		dlog.Error.Fatalf("Unable to Unmarshal DOP config from data file (%s): %s", jf, err)
	}
	dlog.Trace.Println(jv)
	return jv
}

func NotFound(id string, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusNotFound)
	if err := json.NewEncoder(w).Encode(jsonErr{Code: http.StatusNotFound, Text: "Not Found", Id: id}); err != nil {
		dlog.Error.Panic(err)
	}

}

func (jc *JournalConf) JsonAPI(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error
	var entry string
	var journals Journals
	//var jindex JIndex

	var jv JournalVars
	jv = Load(jc.DopRoot)

	entry = vars["entryId"]
	journals, _, err = Parse(entry, "", jc, &jv)
	if (err != nil) && (err.Error() == "NotFound") {
		NotFound(entry, w)
		return
	}

	b, err := json.MarshalIndent(journals, "", "    ")
	if err != nil {
		dlog.Error.Panicf("ERROR: encoding JSON: %s\n", err)
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func (jc *JournalConf) Index(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error
	var entry string
	var journals Journals
	var jindex JIndex
	var current Journals
	var list bool
	var search string
	var title, desc string

	var jv JournalVars
	jv = Load(jc.DopRoot)

	search = strings.Replace(vars["term"], "+", " ", -1)
	journals, jindex, err = Parse("*", search, jc, &jv)

	entry = vars["entryId"]
	dlog.Trace.Printf("entry_POST=%s", entry)
	if entry == "" {
		list = true
		desc = jv.Desc
		title = jv.Title
		if len(journals) > 0 {
			entry = journals[0].Id
		} else {
			entry = ""
		}
	} else {
		list = false
		entry = jindex[entry]
	}

	dlog.Trace.Printf("entry_PARSE=%s", entry)
	dlog.Trace.Printf("len(jindex)=%s", len(jindex))

	current, _, err = Parse(entry, "", jc, &jv)
	if (err != nil) && (err.Error() == "NotFound") {
		NotFound(entry, w)
		return
	}

	if desc == "" {
		if desc = current[0].DopDesc; desc == "" {
			desc = jv.Desc
		}
	}

	if title == "" {
		if title = current[0].Title; title == "" {
			title = jv.Title
		}
	}

	var nextid string
	var previd string
	if currpos := journals.CurrPosition(entry); currpos != -1 {
		nextid = journals.NextId(currpos)
		previd = journals.PrevId(currpos)
	}

	page := Page{
		Title:     title,
		Desc:      desc,
		IsList:    list,
		PrevId:    previd,
		NextId:    nextid,
		HttpFQDN:  jc.HttpFQDN,
		EUrl:      jc.EUrl,
		TUrl:      jc.TUrl,
		Search:    search,
		CssLookup: jv.CssLookup,
		Navbar:    journals,
		Content:   current,
	}
	renderTemplate(w, "dop", &page)
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/dustin/gojson"
	"github.com/dustin/seriesly/timelib"
	"github.com/mschoch/gouchstore"
)

func serverInfo(parts []string, w http.ResponseWriter, req *http.Request) {
	sinfo := map[string]string{
		"seriesly": "Why so series?", "version": "seriesly 0.0",
	}
	mustEncode(200, w, sinfo)
}

func listDatabases(parts []string, w http.ResponseWriter, req *http.Request) {
	mustEncode(200, w, dblist(*dbRoot))
}

func notImplemented(parts []string, w http.ResponseWriter, req *http.Request) {
	emitError(501, w, "not implemented", "TODO")
}

func createDB(parts []string, w http.ResponseWriter, req *http.Request) {
	path := dbPath(parts[0])
	err := dbcreate(path)
	if err == nil {
		w.WriteHeader(201)
	} else {
		emitError(500, w, "Server Error", err.Error())
	}
}

func checkDB(args []string, w http.ResponseWriter, req *http.Request) {
	dbname := args[0]
	if db, err := dbopen(dbname); err == nil {
		closeDBConn(db)
		w.WriteHeader(200)
	} else {
		w.WriteHeader(404)
	}
}

func newDocument(args []string, w http.ResponseWriter, req *http.Request) {
	var k, fk string
	form, err := url.ParseQuery(req.URL.RawQuery)
	if err == nil {
		fk = form.Get("ts")
	}

	if fk == "" {
		k = time.Now().UTC().Format(time.RFC3339Nano)
	} else {
		t, err := timelib.ParseTime(fk)
		if err != nil {
			emitError(400, w, "Bad time format", err.Error())
			return
		}
		k = t.UTC().Format(time.RFC3339Nano)
	}
	putDocument([]string{args[0], k}, w, req)
}

func putDocument(args []string, w http.ResponseWriter, req *http.Request) {
	dbname := args[0]
	k := args[1]
	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		emitError(400, w, "Bad Request",
			fmt.Sprintf("Error reading body: %v", err))
		return
	}

	err = json.Validate(body)
	if err != nil {
		emitError(400, w, "Error parsing JSON data", err.Error())
		return
	}

	err = dbstore(dbname, k, body)

	if err == nil {
		w.WriteHeader(201)
	} else {
		emitError(500, w, "Error storing data", err.Error())
	}
}

func cleanupRangeParam(in, def string) (string, error) {
	if in == "" {
		return def, nil
	}
	t, err := timelib.ParseTime(in)
	if err != nil {
		return in, err
	}
	return t.UTC().Format(time.RFC3339Nano), nil
}

func canGzip(req *http.Request) bool {
	acceptable := req.Header.Get("accept-encoding")
	return strings.Contains(acceptable, "gzip")
}

func query(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	group, err := strconv.Atoi(req.FormValue("group"))
	if err != nil {
		emitError(400, w, "Bad group value", err.Error())
		return
	}

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	ptrs := req.Form["ptr"]
	reds := make([]string, 0, len(ptrs))
	for _, r := range req.Form["reducer"] {
		_, ok := reducers[r]
		if !ok {
			emitError(400, w, "No such reducer", r)
			return
		}
		reds = append(reds, r)
	}

	if len(ptrs) < 1 {
		emitError(400, w, "Pointer required",
			"At least one ptr argument is required")
		return
	}

	if len(ptrs) != len(reds) {
		emitError(400, w, "Parameter mismatch",
			"Must supply the same number of pointers and reducers")
		return
	}

	filters := req.Form["f"]
	filtervals := req.Form["fv"]
	if len(filters) != len(filtervals) {
		emitError(400, w, "Parameter mismatch",
			"Must supply the same number of filters and filter values")
		return
	}

	q := executeQuery(args[0], from, to, group, ptrs, reds, filters, filtervals)
	defer close(q.out)
	defer close(q.cherr)

	output := newGzippingWriter(w, req)
	defer output.Close()

	going := true
	finished := int32(0)
	started := false
	walkComplete := false
	for going {
		select {
		case po := <-q.out:
			if !started {
				started = true
				w.WriteHeader(200)
				output.Write([]byte{'{'})
			}
			if finished != 0 {
				output.Write([]byte{',', '\n'})
			}
			finished++

			_, err := fmt.Fprintf(output, `"%d": `, po.key/1e6)
			if err == nil {
				var d []byte
				d, err = json.Marshal(po.value)
				if err == nil {
					_, err = output.Write(d)
				}
			}
			if err != nil {
				log.Printf("Error sending item: %v", err)
				output = ioutil.Discard
				q.before = time.Time{}
			}
		case err = <-q.cherr:
			if err != nil {
				if !started {
					w.WriteHeader(500)
					w.Header().Set("Content-Type", "text/plain")
					fmt.Fprintf(output, "Error beginning traversal: %v", err)
				}
				log.Printf("Walk completed with err: %v", err)
				going = false
			}
			walkComplete = true
		}
		going = (q.started-finished > 0) || !walkComplete
	}

	if started {
		output.Write([]byte{'}'})
	}

	duration := time.Since(q.start)
	if duration > *minQueryLogDuration {
		log.Printf("Completed query processing in %v, %v keys, %v chunks",
			duration, humanize.Comma(int64(q.totalKeys)),
			humanize.Comma(int64(q.started)))
	}
}

func deleteBulk(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	compactAfter := strings.ToLower(req.FormValue("compact"))

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	db, err := dbopen(args[0])
	if err != nil {
		emitError(400, w, "Unable to open Database", err.Error())
		return
	}

	bulk := db.Bulk()
	deleteCount := 0
	commitThreshold := 10000

	err = dbwalkKeys(args[0], from, to, func(k string) error {

		bulk.Delete(gouchstore.NewDocumentInfo(k))
		deleteCount++
		if deleteCount >= commitThreshold {
			bulk.Commit()
			deleteCount = 0
		}
		return err
	})

	if deleteCount > 0 {
		bulk.Commit()
	}

	bulk.Close()

	if compactAfter == "true" {
		err = dbcompact(args[0])
	}

	w.WriteHeader(201)

}
func deleteDB(parts []string, w http.ResponseWriter, req *http.Request) {
	err := dbdelete(parts[0])
	if err == nil {
		mustEncode(200, w, map[string]interface{}{"ok": true})
	} else {
		emitError(500, w, "Error deleting DB", err.Error())
	}
}

func compact(parts []string, w http.ResponseWriter, req *http.Request) {
	err := dbcompact(parts[0])
	if err == nil {
		mustEncode(200, w, map[string]interface{}{"ok": true})
	} else {
		emitError(500, w, "Error compacting DB", err.Error())
	}
}

type gzippingWriter struct {
	underlying http.ResponseWriter
	gz         *gzip.Writer
	output     io.Writer
}

func newGzippingWriter(w http.ResponseWriter, req *http.Request) *gzippingWriter {
	rv := &gzippingWriter{underlying: w, output: w}
	if canGzip(req) {
		w.Header().Set("Content-Encoding", "gzip")
		rv.gz = gzip.NewWriter(rv.underlying)
		rv.output = rv.gz
	}
	return rv
}

func (g *gzippingWriter) Write(b []byte) (int, error) {
	return g.output.Write(b)
}

func (g *gzippingWriter) Close() error {
	if g.gz != nil {
		return g.gz.Close()
	}
	return nil
}

func allDocs(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	limit, err := strconv.Atoi(req.FormValue("limit"))
	if err != nil {
		limit = 2000000000
	}

	output := newGzippingWriter(w, req)
	defer output.Close()
	w.WriteHeader(200)

	output.Write([]byte{'{'})
	defer output.Write([]byte{'}'})

	seenOne := false

	walked := 0
	err = dbwalk(args[0], from, to, func(k string, v []byte) error {
		if walked > limit {
			return io.EOF
		}
		walked++
		if seenOne {
			output.Write([]byte(",\n"))
		} else {
			seenOne = true
		}
		_, err := fmt.Fprintf(output, `"%s": `, k)
		if err != nil {
			return err
		}
		_, err = output.Write(v)
		return err
	})
}

func dumpDocs(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	limit, err := strconv.Atoi(req.FormValue("limit"))
	if err != nil {
		limit = 2000000000
	}

	output := newGzippingWriter(w, req)
	defer output.Close()
	w.WriteHeader(200)

	walked := 0
	err = dbwalk(args[0], from, to, func(k string, v []byte) error {
		if walked > limit {
			return io.EOF
		}
		walked++
		_, err := fmt.Fprintf(output, `{"%s": `, k)
		if err != nil {
			return err
		}
		_, err = output.Write(v)
		output.Write([]byte{'}', '\n'})
		return err
	})
}

func getDocument(parts []string, w http.ResponseWriter, req *http.Request) {
	d, err := dbGetDoc(parts[0], parts[1])
	if err == nil {
		w.Write(d)
	} else {
		emitError(404, w, "Error retrieving value", err.Error())
	}
}

func dbInfo(args []string, w http.ResponseWriter, req *http.Request) {
	db, err := dbopen(args[0])
	if err != nil {
		emitError(500, w, "Error opening DB", err.Error())
		return
	}
	defer closeDBConn(db)

	inf, err := db.DatabaseInfo()
	if err == nil {
		mustEncode(200, w, map[string]interface{}{
			"last_seq":      inf.LastSeq,
			"doc_count":     inf.DocumentCount,
			"deleted_count": inf.DeletedCount,
			"space_used":    inf.SpaceUsed,
			"header_pos":    inf.HeaderPosition,
		})
	} else {
		emitError(500, w, "Error getting db info", err.Error())
	}
}

// TODO:

func dbChanges(parts []string, w http.ResponseWriter, req *http.Request) {
	notImplemented(parts, w, req)
}

func rmDocument(parts []string, w http.ResponseWriter, req *http.Request) {
	notImplemented(parts, w, req)
}
<commit_msg>build fix<commit_after>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/dustin/gojson"
	"github.com/dustin/seriesly/timelib"
	"github.com/mschoch/gouchstore"
)

func serverInfo(parts []string, w http.ResponseWriter, req *http.Request) {
	sinfo := map[string]string{
		"seriesly": "Why so series?", "version": "seriesly 0.0",
	}
	mustEncode(200, w, sinfo)
}

func listDatabases(parts []string, w http.ResponseWriter, req *http.Request) {
	mustEncode(200, w, dblist(*dbRoot))
}

func notImplemented(parts []string, w http.ResponseWriter, req *http.Request) {
	emitError(501, w, "not implemented", "TODO")
}

func createDB(parts []string, w http.ResponseWriter, req *http.Request) {
	path := dbPath(parts[0])
	err := dbcreate(path)
	if err == nil {
		w.WriteHeader(201)
	} else {
		emitError(500, w, "Server Error", err.Error())
	}
}

func checkDB(args []string, w http.ResponseWriter, req *http.Request) {
	dbname := args[0]
	if db, err := dbopen(dbname); err == nil {
		closeDBConn(db)
		w.WriteHeader(200)
	} else {
		w.WriteHeader(404)
	}
}

func newDocument(args []string, w http.ResponseWriter, req *http.Request) {
	var k, fk string
	form, err := url.ParseQuery(req.URL.RawQuery)
	if err == nil {
		fk = form.Get("ts")
	}

	if fk == "" {
		k = time.Now().UTC().Format(time.RFC3339Nano)
	} else {
		t, err := timelib.ParseTime(fk)
		if err != nil {
			emitError(400, w, "Bad time format", err.Error())
			return
		}
		k = t.UTC().Format(time.RFC3339Nano)
	}
	putDocument([]string{args[0], k}, w, req)
}

func putDocument(args []string, w http.ResponseWriter, req *http.Request) {
	dbname := args[0]
	k := args[1]
	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		emitError(400, w, "Bad Request",
			fmt.Sprintf("Error reading body: %v", err))
		return
	}

	err = json.Validate(body)
	if err != nil {
		emitError(400, w, "Error parsing JSON data", err.Error())
		return
	}

	err = dbstore(dbname, k, body)

	if err == nil {
		w.WriteHeader(201)
	} else {
		emitError(500, w, "Error storing data", err.Error())
	}
}

func cleanupRangeParam(in, def string) (string, error) {
	if in == "" {
		return def, nil
	}
	t, err := timelib.ParseTime(in)
	if err != nil {
		return in, err
	}
	return t.UTC().Format(time.RFC3339Nano), nil
}

func canGzip(req *http.Request) bool {
	acceptable := req.Header.Get("accept-encoding")
	return strings.Contains(acceptable, "gzip")
}

func query(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	group, err := strconv.Atoi(req.FormValue("group"))
	if err != nil {
		emitError(400, w, "Bad group value", err.Error())
		return
	}

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	ptrs := req.Form["ptr"]
	reds := make([]string, 0, len(ptrs))
	for _, r := range req.Form["reducer"] {
		_, ok := reducers[r]
		if !ok {
			emitError(400, w, "No such reducer", r)
			return
		}
		reds = append(reds, r)
	}

	if len(ptrs) < 1 {
		emitError(400, w, "Pointer required",
			"At least one ptr argument is required")
		return
	}

	if len(ptrs) != len(reds) {
		emitError(400, w, "Parameter mismatch",
			"Must supply the same number of pointers and reducers")
		return
	}

	filters := req.Form["f"]
	filtervals := req.Form["fv"]
	if len(filters) != len(filtervals) {
		emitError(400, w, "Parameter mismatch",
			"Must supply the same number of filters and filter values")
		return
	}

	q := executeQuery(args[0], from, to, group, ptrs, reds, filters, filtervals)
	defer close(q.out)
	defer close(q.cherr)

	output := io.Writer(newGzippingWriter(w, req))
	defer output.(io.Closer).Close()

	going := true
	finished := int32(0)
	started := false
	walkComplete := false
	for going {
		select {
		case po := <-q.out:
			if !started {
				started = true
				w.WriteHeader(200)
				output.Write([]byte{'{'})
			}
			if finished != 0 {
				output.Write([]byte{',', '\n'})
			}
			finished++

			_, err := fmt.Fprintf(output, `"%d": `, po.key/1e6)
			if err == nil {
				var d []byte
				d, err = json.Marshal(po.value)
				if err == nil {
					_, err = output.Write(d)
				}
			}
			if err != nil {
				log.Printf("Error sending item: %v", err)
				output = ioutil.Discard
				q.before = time.Time{}
			}
		case err = <-q.cherr:
			if err != nil {
				if !started {
					w.WriteHeader(500)
					w.Header().Set("Content-Type", "text/plain")
					fmt.Fprintf(output, "Error beginning traversal: %v", err)
				}
				log.Printf("Walk completed with err: %v", err)
				going = false
			}
			walkComplete = true
		}
		going = (q.started-finished > 0) || !walkComplete
	}

	if started {
		output.Write([]byte{'}'})
	}

	duration := time.Since(q.start)
	if duration > *minQueryLogDuration {
		log.Printf("Completed query processing in %v, %v keys, %v chunks",
			duration, humanize.Comma(int64(q.totalKeys)),
			humanize.Comma(int64(q.started)))
	}
}

func deleteBulk(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	compactAfter := strings.ToLower(req.FormValue("compact"))

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	db, err := dbopen(args[0])
	if err != nil {
		emitError(400, w, "Unable to open Database", err.Error())
		return
	}

	bulk := db.Bulk()
	deleteCount := 0
	commitThreshold := 10000

	err = dbwalkKeys(args[0], from, to, func(k string) error {

		bulk.Delete(gouchstore.NewDocumentInfo(k))
		deleteCount++
		if deleteCount >= commitThreshold {
			bulk.Commit()
			deleteCount = 0
		}
		return err
	})

	if deleteCount > 0 {
		bulk.Commit()
	}

	bulk.Close()

	if compactAfter == "true" {
		err = dbcompact(args[0])
	}

	w.WriteHeader(201)

}
func deleteDB(parts []string, w http.ResponseWriter, req *http.Request) {
	err := dbdelete(parts[0])
	if err == nil {
		mustEncode(200, w, map[string]interface{}{"ok": true})
	} else {
		emitError(500, w, "Error deleting DB", err.Error())
	}
}

func compact(parts []string, w http.ResponseWriter, req *http.Request) {
	err := dbcompact(parts[0])
	if err == nil {
		mustEncode(200, w, map[string]interface{}{"ok": true})
	} else {
		emitError(500, w, "Error compacting DB", err.Error())
	}
}

type gzippingWriter struct {
	underlying http.ResponseWriter
	gz         *gzip.Writer
	output     io.Writer
}

func newGzippingWriter(w http.ResponseWriter, req *http.Request) *gzippingWriter {
	rv := &gzippingWriter{underlying: w, output: w}
	if canGzip(req) {
		w.Header().Set("Content-Encoding", "gzip")
		rv.gz = gzip.NewWriter(rv.underlying)
		rv.output = rv.gz
	}
	return rv
}

func (g *gzippingWriter) Write(b []byte) (int, error) {
	return g.output.Write(b)
}

func (g *gzippingWriter) Close() error {
	if g.gz != nil {
		return g.gz.Close()
	}
	return nil
}

func allDocs(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	limit, err := strconv.Atoi(req.FormValue("limit"))
	if err != nil {
		limit = 2000000000
	}

	output := newGzippingWriter(w, req)
	defer output.Close()
	w.WriteHeader(200)

	output.Write([]byte{'{'})
	defer output.Write([]byte{'}'})

	seenOne := false

	walked := 0
	err = dbwalk(args[0], from, to, func(k string, v []byte) error {
		if walked > limit {
			return io.EOF
		}
		walked++
		if seenOne {
			output.Write([]byte(",\n"))
		} else {
			seenOne = true
		}
		_, err := fmt.Fprintf(output, `"%s": `, k)
		if err != nil {
			return err
		}
		_, err = output.Write(v)
		return err
	})
}

func dumpDocs(args []string, w http.ResponseWriter, req *http.Request) {
	// Parse the params

	req.ParseForm()

	from, err := cleanupRangeParam(req.FormValue("from"), "")
	if err != nil {
		emitError(400, w, "Bad from value", err.Error())
		return
	}
	to, err := cleanupRangeParam(req.FormValue("to"), "")
	if err != nil {
		emitError(400, w, "Bad to value", err.Error())
		return
	}

	limit, err := strconv.Atoi(req.FormValue("limit"))
	if err != nil {
		limit = 2000000000
	}

	output := newGzippingWriter(w, req)
	defer output.Close()
	w.WriteHeader(200)

	walked := 0
	err = dbwalk(args[0], from, to, func(k string, v []byte) error {
		if walked > limit {
			return io.EOF
		}
		walked++
		_, err := fmt.Fprintf(output, `{"%s": `, k)
		if err != nil {
			return err
		}
		_, err = output.Write(v)
		output.Write([]byte{'}', '\n'})
		return err
	})
}

func getDocument(parts []string, w http.ResponseWriter, req *http.Request) {
	d, err := dbGetDoc(parts[0], parts[1])
	if err == nil {
		w.Write(d)
	} else {
		emitError(404, w, "Error retrieving value", err.Error())
	}
}

func dbInfo(args []string, w http.ResponseWriter, req *http.Request) {
	db, err := dbopen(args[0])
	if err != nil {
		emitError(500, w, "Error opening DB", err.Error())
		return
	}
	defer closeDBConn(db)

	inf, err := db.DatabaseInfo()
	if err == nil {
		mustEncode(200, w, map[string]interface{}{
			"last_seq":      inf.LastSeq,
			"doc_count":     inf.DocumentCount,
			"deleted_count": inf.DeletedCount,
			"space_used":    inf.SpaceUsed,
			"header_pos":    inf.HeaderPosition,
		})
	} else {
		emitError(500, w, "Error getting db info", err.Error())
	}
}

// TODO:

func dbChanges(parts []string, w http.ResponseWriter, req *http.Request) {
	notImplemented(parts, w, req)
}

func rmDocument(parts []string, w http.ResponseWriter, req *http.Request) {
	notImplemented(parts, w, req)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/mux"
)

func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Welcome!")
}

func PostIndex(w http.ResponseWriter, r *http.Request) {
	var (
		posts   Posts
		id      int
		title   string
		content string
		posted  time.Time
	)

	db := dbConnection()
	rows, err := db.Query("SELECT * FROM posts")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		err := rows.Scan(&id, &title, &content, &posted)
		if err != nil {
			log.Fatal(err)
		}
		p := Post{id, title, content, posted}
		posts = append(posts, p)
	}
	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	JSONHandler(w, r)
	if err := json.NewEncoder(w).Encode(posts); err != nil {
		log.Fatal(err)
	}
}

func PostShow(w http.ResponseWriter, r *http.Request) {
	var (
		p       Post
		id      int
		title   string
		content string
		posted  time.Time
	)

	vars := mux.Vars(r)
	postId := vars["postId"]

	db := dbConnection()
	rows, err := db.Query("SELECT * FROM posts WHERE ID = ?", postId)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		err := rows.Scan(&id, &title, &content, &posted)
		if err != nil {
			log.Fatal(err)
		}
		p = Post{id, title, content, posted}
	}
	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	JSONHandler(w, r)
	if err := json.NewEncoder(w).Encode(p); err != nil {
		log.Fatal(err)
	}
}

func PostCreate(w http.ResponseWriter, r *http.Request) {
	var post Post

	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
	log.Print(body)
	if err != nil {
		log.Fatal(err)
	}
	if err := r.Body.Close(); err != nil {
		log.Fatal(err)
	}

	if err := json.Unmarshal(body, &post); err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422)
	}
	log.Print(post)
}
<commit_msg>remove unused packages<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Welcome!")
}

func PostIndex(w http.ResponseWriter, r *http.Request) {
	var (
		posts   Posts
		id      int
		title   string
		content string
		posted  string
	)

	db := dbConnection()
	rows, err := db.Query("SELECT * FROM posts")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		err := rows.Scan(&id, &title, &content, &posted)
		if err != nil {
			log.Fatal(err)
		}
		p := Post{id, title, content, posted}
		posts = append(posts, p)
	}
	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	JSONHandler(w, r)
	if err := json.NewEncoder(w).Encode(posts); err != nil {
		log.Fatal(err)
	}
}

func PostShow(w http.ResponseWriter, r *http.Request) {
	var (
		p       Post
		id      int
		title   string
		content string
		posted  string
	)

	vars := mux.Vars(r)
	postId := vars["postId"]

	db := dbConnection()
	rows, err := db.Query("SELECT * FROM posts WHERE ID = ?", postId)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		err := rows.Scan(&id, &title, &content, &posted)
		if err != nil {
			log.Fatal(err)
		}
		p = Post{id, title, content, posted}
	}
	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	JSONHandler(w, r)
	if err := json.NewEncoder(w).Encode(p); err != nil {
		log.Fatal(err)
	}
}

func PostCreate(w http.ResponseWriter, r *http.Request) {
	var post Post

	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
	if err != nil {
		log.Fatal(err)
	}
	if err := r.Body.Close(); err != nil {
		log.Fatal(err)
	}

	if err := json.Unmarshal(body, &post); err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422)
	}
	log.Print(post)
}
<|endoftext|>


<commit_before>package acme

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/miekg/dns"
)

type preCheckDNSFunc func(domain, fqdn string) bool

var preCheckDNS preCheckDNSFunc = checkDNS

var preCheckDNSFallbackCount = 5

// DNSProvider represents a service for creating dns records.
type DNSProvider interface {
	// CreateTXT creates a TXT record
	CreateTXTRecord(fqdn, value string, ttl int) error
	RemoveTXTRecord(fqdn, value string, ttl int) error
}

// dnsChallenge implements the dns-01 challenge according to ACME 7.5
type dnsChallenge struct {
	jws      *jws
	provider DNSProvider
}

func (s *dnsChallenge) Solve(chlng challenge, domain string) error {

	logf("[INFO] acme: Trying to solve DNS-01")

	// Generate the Key Authorization for the challenge
	keyAuth, err := getKeyAuthorization(chlng.Token, &s.jws.privKey.PublicKey)
	if err != nil {
		return err
	}

	keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
	// base64URL encoding without padding
	keyAuthSha := base64.URLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
	keyAuthSha = strings.TrimRight(keyAuthSha, "=")

	fqdn := fmt.Sprintf("_acme-challenge.%s.", domain)
	if err = s.provider.CreateTXTRecord(fqdn, keyAuthSha, 120); err != nil {
		return err
	}

	preCheckDNS(domain, fqdn)

	jsonBytes, err := json.Marshal(challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
	if err != nil {
		return errors.New("Failed to marshal network message...")
	}

	// Tell the server we handle DNS-01
	resp, err := s.jws.post(chlng.URI, jsonBytes)
	if err != nil {
		return fmt.Errorf("Failed to post JWS message. -> %v", err)
	}

	// Repeatedly check the server for an updated status on our request.
	var challengeResponse challenge
Loop:
	for {
		if resp.StatusCode >= http.StatusBadRequest {
			return handleHTTPError(resp)
		}

		err = json.NewDecoder(resp.Body).Decode(&challengeResponse)
		resp.Body.Close()
		if err != nil {
			return err
		}

		switch challengeResponse.Status {
		case "valid":
			logf("The server validated our request")
			break Loop
		case "pending":
			break
		case "invalid":
			return errors.New("The server could not validate our request.")
		default:
			return errors.New("The server returned an unexpected state.")
		}

		time.Sleep(1 * time.Second)
		resp, err = http.Get(chlng.URI)
	}

	if err = s.provider.RemoveTXTRecord(fqdn, keyAuthSha, 120); err != nil {
		logf("[WARN] acme: Failed to cleanup DNS record. -> %v ", err)
	}

	return nil
}

func checkDNS(domain, fqdn string) bool {
	// check if the expected DNS entry was created. If not wait for some time and try again.
	m := new(dns.Msg)
	m.SetQuestion(domain+".", dns.TypeSOA)
	c := new(dns.Client)
	in, _, err := c.Exchange(m, "8.8.8.8:53")
	if err != nil {
		return false
	}

	var authorativeNS string
	for _, answ := range in.Answer {
		soa := answ.(*dns.SOA)
		authorativeNS = soa.Ns
	}

	fallbackCnt := 0
	for fallbackCnt < preCheckDNSFallbackCount {
		m.SetQuestion(fqdn, dns.TypeTXT)
		in, _, err = c.Exchange(m, authorativeNS+":53")
		if err != nil {
			return false
		}

		if len(in.Answer) > 0 {
			return true
		}

		fallbackCnt++
		if fallbackCnt >= preCheckDNSFallbackCount {
			return false
		}

		time.Sleep(time.Second * time.Duration(fallbackCnt))
	}

	return false
}
<commit_msg>Tweak comment<commit_after>package acme

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/miekg/dns"
)

type preCheckDNSFunc func(domain, fqdn string) bool

var preCheckDNS preCheckDNSFunc = checkDNS

var preCheckDNSFallbackCount = 5

// DNSProvider represents a service for managing DNS records.
type DNSProvider interface {
	CreateTXTRecord(fqdn, value string, ttl int) error
	RemoveTXTRecord(fqdn, value string, ttl int) error
}

// dnsChallenge implements the dns-01 challenge according to ACME 7.5
type dnsChallenge struct {
	jws      *jws
	provider DNSProvider
}

func (s *dnsChallenge) Solve(chlng challenge, domain string) error {

	logf("[INFO] acme: Trying to solve DNS-01")

	// Generate the Key Authorization for the challenge
	keyAuth, err := getKeyAuthorization(chlng.Token, &s.jws.privKey.PublicKey)
	if err != nil {
		return err
	}

	keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
	// base64URL encoding without padding
	keyAuthSha := base64.URLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
	keyAuthSha = strings.TrimRight(keyAuthSha, "=")

	fqdn := fmt.Sprintf("_acme-challenge.%s.", domain)
	if err = s.provider.CreateTXTRecord(fqdn, keyAuthSha, 120); err != nil {
		return err
	}

	preCheckDNS(domain, fqdn)

	jsonBytes, err := json.Marshal(challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
	if err != nil {
		return errors.New("Failed to marshal network message...")
	}

	// Tell the server we handle DNS-01
	resp, err := s.jws.post(chlng.URI, jsonBytes)
	if err != nil {
		return fmt.Errorf("Failed to post JWS message. -> %v", err)
	}

	// Repeatedly check the server for an updated status on our request.
	var challengeResponse challenge
Loop:
	for {
		if resp.StatusCode >= http.StatusBadRequest {
			return handleHTTPError(resp)
		}

		err = json.NewDecoder(resp.Body).Decode(&challengeResponse)
		resp.Body.Close()
		if err != nil {
			return err
		}

		switch challengeResponse.Status {
		case "valid":
			logf("The server validated our request")
			break Loop
		case "pending":
			break
		case "invalid":
			return errors.New("The server could not validate our request.")
		default:
			return errors.New("The server returned an unexpected state.")
		}

		time.Sleep(1 * time.Second)
		resp, err = http.Get(chlng.URI)
	}

	if err = s.provider.RemoveTXTRecord(fqdn, keyAuthSha, 120); err != nil {
		logf("[WARN] acme: Failed to cleanup DNS record. -> %v ", err)
	}

	return nil
}

func checkDNS(domain, fqdn string) bool {
	// check if the expected DNS entry was created. If not wait for some time and try again.
	m := new(dns.Msg)
	m.SetQuestion(domain+".", dns.TypeSOA)
	c := new(dns.Client)
	in, _, err := c.Exchange(m, "8.8.8.8:53")
	if err != nil {
		return false
	}

	var authorativeNS string
	for _, answ := range in.Answer {
		soa := answ.(*dns.SOA)
		authorativeNS = soa.Ns
	}

	fallbackCnt := 0
	for fallbackCnt < preCheckDNSFallbackCount {
		m.SetQuestion(fqdn, dns.TypeTXT)
		in, _, err = c.Exchange(m, authorativeNS+":53")
		if err != nil {
			return false
		}

		if len(in.Answer) > 0 {
			return true
		}

		fallbackCnt++
		if fallbackCnt >= preCheckDNSFallbackCount {
			return false
		}

		time.Sleep(time.Second * time.Duration(fallbackCnt))
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.FilterQuotes(func(quote *arn.Quote) bool {
			return !quote.IsDraft && quote.IsValid()
		})

		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}
}
<commit_msg>Updated animelist command<commit_after>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist/watching")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.FilterQuotes(func(quote *arn.Quote) bool {
			return !quote.IsDraft && quote.IsValid()
		})

		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"net/url"
	"os"
	"strconv"
	"sync"

	"github.com/chrislusf/glow/driver/cmd"
	"github.com/chrislusf/glow/io/store"
	"github.com/chrislusf/glow/resource"
	"github.com/chrislusf/glow/resource/service_discovery/client"
	"github.com/chrislusf/glow/util"
	"github.com/golang/protobuf/proto"
)

type LiveDataStore struct {
	store           store.DataStore
	killHeartBeater chan bool
}

func NewLiveDataStore(s store.DataStore) *LiveDataStore {
	return &LiveDataStore{
		store:           s,
		killHeartBeater: make(chan bool, 1),
	}
}

func (ds *LiveDataStore) Destroy() {
	ds.killHeartBeater <- true
	ds.store.Destroy()
}

type AgentServerOption struct {
	Leader      *string
	Port        *int
	Dir         *string
	DataCenter  *string
	Rack        *string
	MaxExecutor *int
	MemoryMB    *int64
	CPULevel    *int
}

type AgentServer struct {
	Option                *AgentServerOption
	leader                string
	Port                  int
	name2Store            map[string]*LiveDataStore
	dir                   string
	name2StoreLock        sync.Mutex
	wg                    sync.WaitGroup
	l                     net.Listener
	computeResource       *resource.ComputeResource
	allocatedResource     *resource.ComputeResource
	allocatedResourceLock sync.Mutex
}

func NewAgentServer(option *AgentServerOption) *AgentServer {
	as := &AgentServer{
		Option:     option,
		leader:     *option.Leader,
		Port:       *option.Port,
		dir:        *option.Dir,
		name2Store: make(map[string]*LiveDataStore),
		computeResource: &resource.ComputeResource{
			CPUCount: *option.MaxExecutor,
			CPULevel: *option.CPULevel,
			MemoryMB: *option.MemoryMB,
		},
		allocatedResource: &resource.ComputeResource{},
	}

	err := as.Init()
	if err != nil {
		panic(err)
	}

	return as
}

// Start starts to listen on a port, returning the listening port
// r.Port can be pre-set or leave it as zero
// The actual port set to r.Port
func (r *AgentServer) Init() (err error) {
	r.l, err = net.Listen("tcp", ":"+strconv.Itoa(r.Port))
	if err != nil {
		log.Fatal(err)
	}

	r.Port = r.l.Addr().(*net.TCPAddr).Port
	fmt.Println("AgentServer starts on:", r.Port)
	return
}

func (as *AgentServer) Run() {
	//register agent
	killHeartBeaterChan := make(chan bool, 1)
	go client.NewHeartBeater(as.Port, as.leader).StartAgentHeartBeat(killHeartBeaterChan, func(values url.Values) {
		resource.AddToValues(values, as.computeResource, as.allocatedResource)
		values.Add("dataCenter", *as.Option.DataCenter)
		values.Add("rack", *as.Option.Rack)
	})

	for {
		// Listen for an incoming connection.
		conn, err := as.l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}
		// Handle connections in a new goroutine.
		as.wg.Add(1)
		go func() {
			defer as.wg.Done()
			defer conn.Close()
			as.handleRequest(conn)
		}()
	}
}

func (r *AgentServer) Stop() {
	r.l.Close()
	r.wg.Wait()
}

// Handles incoming requests.
func (r *AgentServer) handleRequest(conn net.Conn) {

	buf := make([]byte, 4)

	f, message, err := util.ReadBytes(conn, buf)
	if f != util.Data {
		//strange if this happens
		println("read", len(message.Bytes()), "request flag:", f, "data", string(message.Data()))
		return
	}

	if err != nil {
		log.Printf("Failed to read command %s:%v", string(message.Data()), err)
	}
	if bytes.HasPrefix(message.Data(), []byte("PUT ")) {
		name := string(message.Data()[4:])
		r.handleWriteConnection(conn, name)
	} else if bytes.HasPrefix(message.Data(), []byte("GET ")) {
		name := string(message.Data()[4:])
		offset := util.ReadUint64(conn)
		r.handleLocalReadConnection(conn, name, int64(offset))
	} else if bytes.HasPrefix(message.Data(), []byte("CMD ")) {
		newCmd := &cmd.ControlMessage{}
		err := proto.Unmarshal(message.Data()[4:], newCmd)
		if err != nil {
			log.Fatal("unmarshaling error: ", err)
		}
		reply := r.handleCommandConnection(conn, newCmd)
		if reply != nil {
			data, err := proto.Marshal(reply)
			if err != nil {
				log.Fatal("marshaling error: ", err)
			}
			conn.Write(data)
		}
	}

}
<commit_msg>add option to delete old .dat file on start<commit_after>package agent

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/chrislusf/glow/driver/cmd"
	"github.com/chrislusf/glow/io/store"
	"github.com/chrislusf/glow/resource"
	"github.com/chrislusf/glow/resource/service_discovery/client"
	"github.com/chrislusf/glow/util"
	"github.com/golang/protobuf/proto"
)

type LiveDataStore struct {
	store           store.DataStore
	killHeartBeater chan bool
}

func NewLiveDataStore(s store.DataStore) *LiveDataStore {
	return &LiveDataStore{
		store:           s,
		killHeartBeater: make(chan bool, 1),
	}
}

func (ds *LiveDataStore) Destroy() {
	ds.killHeartBeater <- true
	ds.store.Destroy()
}

type AgentServerOption struct {
	Leader       *string
	Port         *int
	Dir          *string
	DataCenter   *string
	Rack         *string
	MaxExecutor  *int
	MemoryMB     *int64
	CPULevel     *int
	CleanRestart *bool
}

type AgentServer struct {
	Option                *AgentServerOption
	leader                string
	Port                  int
	name2Store            map[string]*LiveDataStore
	dir                   string
	name2StoreLock        sync.Mutex
	wg                    sync.WaitGroup
	l                     net.Listener
	computeResource       *resource.ComputeResource
	allocatedResource     *resource.ComputeResource
	allocatedResourceLock sync.Mutex
}

func NewAgentServer(option *AgentServerOption) *AgentServer {
	as := &AgentServer{
		Option:     option,
		leader:     *option.Leader,
		Port:       *option.Port,
		dir:        *option.Dir,
		name2Store: make(map[string]*LiveDataStore),
		computeResource: &resource.ComputeResource{
			CPUCount: *option.MaxExecutor,
			CPULevel: *option.CPULevel,
			MemoryMB: *option.MemoryMB,
		},
		allocatedResource: &resource.ComputeResource{},
	}

	err := as.Init()
	if err != nil {
		panic(err)
	}

	return as
}

// Start starts to listen on a port, returning the listening port
// r.Port can be pre-set or leave it as zero
// The actual port set to r.Port
func (r *AgentServer) Init() (err error) {
	r.l, err = net.Listen("tcp", ":"+strconv.Itoa(r.Port))
	if err != nil {
		log.Fatal(err)
	}

	r.Port = r.l.Addr().(*net.TCPAddr).Port
	fmt.Println("AgentServer starts on:", r.Port)

	if *r.Option.CleanRestart {
		if fileInfos, err := ioutil.ReadDir(r.dir); err == nil {
			for _, fi := range fileInfos {
				name := fi.Name()
				if !fi.IsDir() && strings.HasSuffix(name, ".dat") {
					println("removing old dat file:", name)
					os.Remove(name)
				}
			}
		}
	}

	return
}

func (as *AgentServer) Run() {
	//register agent
	killHeartBeaterChan := make(chan bool, 1)
	go client.NewHeartBeater(as.Port, as.leader).StartAgentHeartBeat(killHeartBeaterChan, func(values url.Values) {
		resource.AddToValues(values, as.computeResource, as.allocatedResource)
		values.Add("dataCenter", *as.Option.DataCenter)
		values.Add("rack", *as.Option.Rack)
	})

	for {
		// Listen for an incoming connection.
		conn, err := as.l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}
		// Handle connections in a new goroutine.
		as.wg.Add(1)
		go func() {
			defer as.wg.Done()
			defer conn.Close()
			as.handleRequest(conn)
		}()
	}
}

func (r *AgentServer) Stop() {
	r.l.Close()
	r.wg.Wait()
}

// Handles incoming requests.
func (r *AgentServer) handleRequest(conn net.Conn) {

	buf := make([]byte, 4)

	f, message, err := util.ReadBytes(conn, buf)
	if f != util.Data {
		//strange if this happens
		println("read", len(message.Bytes()), "request flag:", f, "data", string(message.Data()))
		return
	}

	if err != nil {
		log.Printf("Failed to read command %s:%v", string(message.Data()), err)
	}
	if bytes.HasPrefix(message.Data(), []byte("PUT ")) {
		name := string(message.Data()[4:])
		r.handleWriteConnection(conn, name)
	} else if bytes.HasPrefix(message.Data(), []byte("GET ")) {
		name := string(message.Data()[4:])
		offset := util.ReadUint64(conn)
		r.handleLocalReadConnection(conn, name, int64(offset))
	} else if bytes.HasPrefix(message.Data(), []byte("CMD ")) {
		newCmd := &cmd.ControlMessage{}
		err := proto.Unmarshal(message.Data()[4:], newCmd)
		if err != nil {
			log.Fatal("unmarshaling error: ", err)
		}
		reply := r.handleCommandConnection(conn, newCmd)
		if reply != nil {
			data, err := proto.Marshal(reply)
			if err != nil {
				log.Fatal("marshaling error: ", err)
			}
			conn.Write(data)
		}
	}

}
<|endoftext|>