<commit_before>package prisclient

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw      net.Conn
	decoder  *json.Decoder
	encoder  *json.Encoder
	SourceId string
	Type     string
	Logger   *prislog.PrisLog
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message       string   `json:"message,omitempty"`
	From          string   `json:"from,omitempty"`
	Room          string   `json:"room,omitempty"`
	Mentioned     bool     `json:"mentioned,omitempty"`
	Stripped      string   `json:"stripped,omitempty"`
	MentionNotify []string `json:"mentionnotify,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port, clientType, sourceid string,
	logger *prislog.PrisLog) (*Client, error) {

	if clientType != "adapter" && clientType != "responder" {
		return nil, errors.New("client type has to be adapter or responder")
	}

	pris := new(Client)

	pris.Logger = logger
	pris.SourceId = sourceid
	pris.Type = clientType

	conn, err := net.Dial("tcp", host+":"+port)

	if err != nil {
		return pris, err
	}

	pris.raw = conn
	pris.decoder = json.NewDecoder(pris.raw)
	pris.encoder = json.NewEncoder(pris.raw)

	return pris, nil
}

func (pris *Client) Engage() error {
	q := Query{
		Type:   "command",
		Source: pris.SourceId,
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   pris.Type,
			Time:   time.Now().Unix(),
		},
	}

	return pris.encoder.Encode(&q)
}

func (pris *Client) listen(out chan<- *Query) {
	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				out <- &Query{
					Type:   "command",
					Source: "pris",
					Command: &CommandBlock{
						Action: "disengage",
					},
				}
				break
			}
		}

		if pris.ValidateQuery(q) {
			pris.Logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.Logger.Debug.Println("Message:", q.Message.Message)
				pris.Logger.Debug.Println("From:", q.Message.From)
				pris.Logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.Logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			pris.encoder.Encode(q)
		} else {
			pris.Logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<commit_msg>auto reconnect option<commit_after>package prisclient

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw        net.Conn
	decoder    *json.Decoder
	encoder    *json.Encoder
	SourceId   string
	clientType string
	logger     *prislog.PrisLog
	autoRetry  bool
	host       string
	port       string
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message       string   `json:"message,omitempty"`
	From          string   `json:"from,omitempty"`
	Room          string   `json:"room,omitempty"`
	Mentioned     bool     `json:"mentioned,omitempty"`
	Stripped      string   `json:"stripped,omitempty"`
	MentionNotify []string `json:"mentionnotify,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port, clientType, sourceid string, autoretry bool,
	logger *prislog.PrisLog) (*Client, error) {

	if clientType != "adapter" && clientType != "responder" {
		return nil, errors.New("client type has to be adapter or responder")
	}

	pris := new(Client)

	pris.logger = logger
	pris.SourceId = sourceid
	pris.clientType = clientType
	pris.autoRetry = autoretry
	pris.host = host
	pris.port = port

	pris.connect()

	return pris, nil
}

func (pris *Client) connect() error {
	conn, err := net.Dial("tcp", pris.host+":"+pris.port)

	if err != nil {
		return err
	}

	pris.raw = conn
	pris.decoder = json.NewDecoder(pris.raw)
	pris.encoder = json.NewEncoder(pris.raw)

	err = pris.Engage()

	if err != nil {
		pris.logger.Error.Println("Failed to engage:", err)
		return err
	}

	pris.logger.Info.Println("Priscilla engaged")

	return nil
}

func (pris *Client) Engage() error {
	q := Query{
		Type:   "command",
		Source: pris.SourceId,
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   pris.clientType,
			Time:   time.Now().Unix(),
		},
	}

	return pris.encoder.Encode(&q)
}

func (pris *Client) listen(out chan<- *Query) {
	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				pris.logger.Error.Println("Priscilla disconnected")
				if pris.autoRetry {
					pris.logger.Error.Println("Auto reconnect in 5 seconds...")
					time.Sleep(5 * time.Second)
					err := pris.connect()
					if err != nil {
						pris.logger.Error.Println("Connect error:", err)
					}
				} else {
					out <- &Query{
						Type:   "command",
						Source: "pris",
						Command: &CommandBlock{
							Action: "disengage",
						},
					}
					break
				}
			}
		}

		if pris.ValidateQuery(q) {
			pris.logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.logger.Debug.Println("Message:", q.Message.Message)
				pris.logger.Debug.Println("From:", q.Message.From)
				pris.logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			pris.encoder.Encode(q)
		} else {
			pris.logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package gapi

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

type Client struct {
	key     string
	baseURL url.URL
	*http.Client
}

//New creates a new grafana client
//auth can be in user:pass format, or it can be an api key
func New(auth, baseURL string) (*Client, error) {
	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}
	key := ""
	if strings.Contains(auth, ":") {
		split := strings.Split(auth, ":")
		u.User = url.UserPassword(split[0], split[1])
	} else {
		key = fmt.Sprintf("Bearer %s", auth)
	}
	return &Client{
		key,
		*u,
		&http.Client{},
	}, nil
}

func (c *Client) newRequest(method, requestPath string, query url.Values, body io.Reader) (*http.Request, error) {
	url := c.baseURL
	url.Path = path.Join(url.Path, requestPath)
	url.RawQuery = query.Encode()
	req, err := http.NewRequest(method, url.String(), body)
	if err != nil {
		return req, err
	}
	if c.key != "" {
		req.Header.Add("Authorization", c.key)
	}

	if os.Getenv("GF_LOG") != "" {
		if body == nil {
			log.Println("request to ", url.String(), "with no body data")
		} else {
			log.Println("request to ", url.String(), "with body data", body.(*bytes.Buffer).String())
		}
	}

	req.Header.Add("Content-Type", "application/json")
	return req, err
}
<commit_msg>include method in debug output<commit_after>package gapi

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

type Client struct {
	key     string
	baseURL url.URL
	*http.Client
}

//New creates a new grafana client
//auth can be in user:pass format, or it can be an api key
func New(auth, baseURL string) (*Client, error) {
	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}
	key := ""
	if strings.Contains(auth, ":") {
		split := strings.Split(auth, ":")
		u.User = url.UserPassword(split[0], split[1])
	} else {
		key = fmt.Sprintf("Bearer %s", auth)
	}
	return &Client{
		key,
		*u,
		&http.Client{},
	}, nil
}

func (c *Client) newRequest(method, requestPath string, query url.Values, body io.Reader) (*http.Request, error) {
	url := c.baseURL
	url.Path = path.Join(url.Path, requestPath)
	url.RawQuery = query.Encode()
	req, err := http.NewRequest(method, url.String(), body)
	if err != nil {
		return req, err
	}
	if c.key != "" {
		req.Header.Add("Authorization", c.key)
	}

	if os.Getenv("GF_LOG") != "" {
		if body == nil {
			log.Printf("request (%s) to %s with no body data", method, url.String())
		} else {
			log.Printf("request (%s) to %s with body data: %s", method, url.String(), body.(*bytes.Buffer).String())
		}
	}

	req.Header.Add("Content-Type", "application/json")
	return req, err
}
<|endoftext|>


<commit_before>package fs

import (
	"errors"
	"io"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var (
	//ErrSrcNotExist src file doesnt exist
	ErrSrcNotExist = errors.New("Source file does not exist")

	//ErrSrcNotRegularFile src file is not a regular file
	ErrSrcNotRegularFile = errors.New("Source file is not a regular file")

	//ErrDstNotRegularFile dst file is not a regular file
	ErrDstNotRegularFile = errors.New("Destination file is not a regular file")
)

//Fs is a static class that provides Filesystem type functions
type Fs struct{}

//NewFs generates a Fs object
func NewFs() *Fs {
	myFs := &Fs{}
	return myFs
}

//DoesFileExist just like it sounds
func (fs *Fs) DoesFileExist(fullpath string) bool {
	log.Debugln("DoesFileExist ENTER")
	log.Debugln("fullpath:", fullpath)

	sfi, err := os.Stat(fullpath)
	if !os.IsNotExist(err) {
		log.Debugln("Src Stat Failed:", err)
		log.Debugln("FileCopy LEAVE")
		return false
	}
	if !sfi.Mode().IsRegular() {
		//cannot use non-regular files (e.g., directories, symlinks, devices, etc.)
		log.Debugln("Src file is not regular")
		log.Debugln("DoesFileExist LEAVE")
		return false
	}

	log.Debugln("File exists")
	log.Debugln("DoesFileExist LEAVE")
	return true
}

//GetFullExePath returns the fullpath of the executable including the executable
//name itself
func (fs *Fs) GetFullExePath() (string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		log.Errorln("Readlink failed:", err)
		return "", nil
	}
	log.Debugln("EXE path:", path)
	return path, nil
}

//GetPathFileFullFilename returns the parent folder name
func (fs *Fs) GetPathFileFullFilename(path string) string {
	log.Debugln("GetPathFileFullFilename ENTER")
	log.Debugln("path:", path)
	last := strings.LastIndex(path, string(filepath.Separator))
	if last == -1 {
		log.Debugln("No slash. Return Path:", path)
		log.Debugln("GetPathFileFullFilename LEAVE")
		return path
	}
	tmp := path[0:last]
	log.Debugln("Final Path:", tmp)
	log.Debugln("GetPathFileFullFilename LEAVE")
	return tmp
}

//GetFullPath returns the fullpath of the executable without the executable name
func (fs *Fs) GetFullPath() (string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		log.Errorln("Readlink failed:", err)
		return "", nil
	}
	log.Debugln("EXE path:", path)

	tmp := fs.GetPathFileFullFilename(path)
	return tmp, nil
}

//GetFilenameFromURIOrFullPath retrieves the filename from an URI
func (fs *Fs) GetFilenameFromURIOrFullPath(path string) string {
	log.Debugln("GetFilenameFromURI ENTER")
	log.Debugln("path:", path)

	last := strings.LastIndex(path, string(filepath.Separator))
	if last == -1 {
		log.Debugln("No slash. Return Path:", path)
		log.Debugln("GetFilenameFromURI LEAVE")
		return path
	}
	pathTmp := path[last+1:]
	log.Debugln("Return Path:", pathTmp)
	log.Debugln("GetFilenameFromURI LEAVE")

	return pathTmp
}

//AppendSlash appends a slash to a path if one is needed
func (fs *Fs) AppendSlash(path string) string {
	log.Debugln("AppendSlash ENTER")
	log.Debugln("path:", path)
	if path[len(path)-1] != filepath.Separator {
		path += string(filepath.Separator)
	}
	log.Debugln("Return Path:", path)
	log.Debugln("GetFilenameFromURI LEAVE")
	return path
}

//FileCopy copies the contents of the src file to the dst file
func (fs *Fs) FileCopy(src string, dst string) error {
	log.Debugln("FileCopy ENTER")
	log.Debugln("SRC:", src)
	log.Debugln("DST:", dst)

	sfi, err := os.Stat(src)
	if err != nil {
		log.Debugln("Src Stat Failed:", err)
		log.Debugln("FileCopy LEAVE")
		return ErrSrcNotExist
	}
	if !sfi.Mode().IsRegular() {
		//cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
		log.Debugln("Src file is not regular")
		log.Debugln("FileCopy LEAVE")
		return ErrSrcNotRegularFile
	}
	dfi, err := os.Stat(dst)
	if err == nil {
		if !(dfi.Mode().IsRegular()) {
			log.Debugln("Dst file is not regular")
			log.Debugln("FileCopy LEAVE")
			return ErrDstNotRegularFile
		}
		if os.SameFile(sfi, dfi) {
			log.Debugln("Src and Dst files are the same")
			log.Debugln("FileCopy LEAVE")
			return nil
		}
	}

	//Copy the file
	in, err := os.OpenFile(src, os.O_RDONLY, 0666)
	if err != nil {
		log.Debugln("Failed to open SRC file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}
	defer in.Close()
	out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Debugln("Failed to open DST file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}
	defer out.Close()
	if _, err = io.Copy(out, in); err != nil {
		log.Debugln("Failed to copy file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}

	err = out.Sync()
	if err != nil {
		log.Debugln("Failed to flush file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}

	log.Debugln("File copy succeeded")
	log.Debugln("FileCopy LEAVE")
	return nil
}
<commit_msg>File exists<commit_after>package fs

import (
	"errors"
	"io"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var (
	//ErrSrcNotExist src file doesnt exist
	ErrSrcNotExist = errors.New("Source file does not exist")

	//ErrSrcNotRegularFile src file is not a regular file
	ErrSrcNotRegularFile = errors.New("Source file is not a regular file")

	//ErrDstNotRegularFile dst file is not a regular file
	ErrDstNotRegularFile = errors.New("Destination file is not a regular file")
)

//Fs is a static class that provides Filesystem type functions
type Fs struct{}

//NewFs generates a Fs object
func NewFs() *Fs {
	myFs := &Fs{}
	return myFs
}

//DoesFileExist just like it sounds
func (fs *Fs) DoesFileExist(fullpath string) bool {
	log.Debugln("DoesFileExist ENTER")
	log.Debugln("fullpath:", fullpath)

	if _, err := os.Stat(fullpath); !os.IsNotExist(err) {
		log.Debugln(fullpath, "does exists")
		log.Debugln("DoesFileExist LEAVE")
		return true
	}

	log.Debugln(fullpath, "does not exists")
	log.Debugln("DoesFileExist LEAVE")
	return false
}

//GetFullExePath returns the fullpath of the executable including the executable
//name itself
func (fs *Fs) GetFullExePath() (string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		log.Errorln("Readlink failed:", err)
		return "", nil
	}
	log.Debugln("EXE path:", path)
	return path, nil
}

//GetPathFileFullFilename returns the parent folder name
func (fs *Fs) GetPathFileFullFilename(path string) string {
	log.Debugln("GetPathFileFullFilename ENTER")
	log.Debugln("path:", path)
	last := strings.LastIndex(path, string(filepath.Separator))
	if last == -1 {
		log.Debugln("No slash. Return Path:", path)
		log.Debugln("GetPathFileFullFilename LEAVE")
		return path
	}
	tmp := path[0:last]
	log.Debugln("Final Path:", tmp)
	log.Debugln("GetPathFileFullFilename LEAVE")
	return tmp
}

//GetFullPath returns the fullpath of the executable without the executable name
func (fs *Fs) GetFullPath() (string, error) {
	path, err := os.Readlink("/proc/self/exe")
	if err != nil {
		log.Errorln("Readlink failed:", err)
		return "", nil
	}
	log.Debugln("EXE path:", path)

	tmp := fs.GetPathFileFullFilename(path)
	return tmp, nil
}

//GetFilenameFromURIOrFullPath retrieves the filename from an URI
func (fs *Fs) GetFilenameFromURIOrFullPath(path string) string {
	log.Debugln("GetFilenameFromURI ENTER")
	log.Debugln("path:", path)

	last := strings.LastIndex(path, string(filepath.Separator))
	if last == -1 {
		log.Debugln("No slash. Return Path:", path)
		log.Debugln("GetFilenameFromURI LEAVE")
		return path
	}
	pathTmp := path[last+1:]
	log.Debugln("Return Path:", pathTmp)
	log.Debugln("GetFilenameFromURI LEAVE")

	return pathTmp
}

//AppendSlash appends a slash to a path if one is needed
func (fs *Fs) AppendSlash(path string) string {
	log.Debugln("AppendSlash ENTER")
	log.Debugln("path:", path)
	if path[len(path)-1] != filepath.Separator {
		path += string(filepath.Separator)
	}
	log.Debugln("Return Path:", path)
	log.Debugln("GetFilenameFromURI LEAVE")
	return path
}

//FileCopy copies the contents of the src file to the dst file
func (fs *Fs) FileCopy(src string, dst string) error {
	log.Debugln("FileCopy ENTER")
	log.Debugln("SRC:", src)
	log.Debugln("DST:", dst)

	sfi, err := os.Stat(src)
	if err != nil {
		log.Debugln("Src Stat Failed:", err)
		log.Debugln("FileCopy LEAVE")
		return ErrSrcNotExist
	}
	if !sfi.Mode().IsRegular() {
		//cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
		log.Debugln("Src file is not regular")
		log.Debugln("FileCopy LEAVE")
		return ErrSrcNotRegularFile
	}
	dfi, err := os.Stat(dst)
	if err == nil {
		if !(dfi.Mode().IsRegular()) {
			log.Debugln("Dst file is not regular")
			log.Debugln("FileCopy LEAVE")
			return ErrDstNotRegularFile
		}
		if os.SameFile(sfi, dfi) {
			log.Debugln("Src and Dst files are the same")
			log.Debugln("FileCopy LEAVE")
			return nil
		}
	}

	//Copy the file
	in, err := os.OpenFile(src, os.O_RDONLY, 0666)
	if err != nil {
		log.Debugln("Failed to open SRC file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}
	defer in.Close()
	out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Debugln("Failed to open DST file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}
	defer out.Close()
	if _, err = io.Copy(out, in); err != nil {
		log.Debugln("Failed to copy file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}

	err = out.Sync()
	if err != nil {
		log.Debugln("Failed to flush file:", err)
		log.Debugln("FileCopy LEAVE")
		return err
	}

	log.Debugln("File copy succeeded")
	log.Debugln("FileCopy LEAVE")
	return nil
}
<|endoftext|>


<commit_before>/*
Package ezk is an enhanced Zookeeper client. It uses the
connection and underlying operations from
https://github.com/samuel/go-zookeeper/zk
in conjunction with automatic retry of operations via the
https://github.com/betable/retry library.
*/
package ezk

import (
	"github.com/betable/retry"
	"github.com/samuel/go-zookeeper/zk"
	"time"
)

// Client is a wrapper over github.com/samuel/go-zookeeper/zk that retries all but one of its operations according to the ClientConfig.Retry function. The one exception is for CreateProtectedEphemeralSequential(); it is not retried automatically.
type Client struct {

	// The configuration for the client.
	Cfg ClientConfig

	// The underlying github.com/samuel/go-zookeeper/zk connection.
	Conn *zk.Conn

	// WatchCh will be nil until Connect returns without error.
	// Watches that fire over the Zookeeper connection will be
	// received on WatchCh.
	WatchCh <-chan zk.Event
}

// ClientConfig is used to configure a Client; pass
// it to NewClient().
type ClientConfig struct {

	// The Chroot directory will be prepended to all paths
	Chroot string

	// The set of ACLs used by defeault when
	// calling Client.Create(), if the formal
	// parameter acl in Create() is length 0.
	Acl []zk.ACL

	// The URLs of the zookeepers to attempt to connect to.
	Servers []string

	// SessionTimeout defaults to 10 seconds if not
	// otherwise set.
	SessionTimeout time.Duration

	// The Retry function determines how many times
	// and how often we retry our Zookeeper operations
	// before failing. See DefaultRetry() which is
	// used if this is not otherwise set.
	Retry Retry
}

// NewClient creates a new ezk.Client.
// If the cfg.SessionTimout is set to 0
// a default value of 10 seconds will be used.
// If cfg.Retry is nil then the DefaultRetry
// function will be used.
func NewClient(cfg ClientConfig) *Client {
	if cfg.Retry == nil {
		cfg.Retry = DefaultRetry
	}
	if cfg.SessionTimeout == 0 {
		cfg.SessionTimeout = 10 * time.Second
	}
	cli := &Client{
		Cfg: cfg,
	}
	return cli
}

// Retry defines the type of the retry method to use.
type Retry func(op, path string, f func() error)

// Connect connects to a Zookeeper server.
// Upon success it sets the z.WatchCh and
// z.Conn and returns nil.
// If an error is returned then z.WatchCh and
// z.Conn will not be set. Connect() will
// typically only be needed once, but can be
// called again if need be.
func (z *Client) Connect() error {
	z.WatchCh = nil
	z.Conn = nil
	conn, ch, err := zk.Connect(z.Cfg.Servers, z.Cfg.SessionTimeout)
	if err != nil {
		return err
	}
	z.Conn = conn
	z.WatchCh = ch
	return nil
}

// Close closes the connection to the Zookeeper server.
func (z *Client) Close() {
	z.Conn.Close()
}

// Exists checks if a znode exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Exists(path string) (ok bool, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("exists", path, func() error {
		ok, s, err = z.Conn.Exists(path)
		return err
	})
	return ok, s, err
}

// ExistsW returns if a znode exists and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) ExistsW(path string) (ok bool, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("existsw", path, func() error {
		ok, s, ch, err = z.Conn.ExistsW(path)
		return err
	})
	return ok, s, ch, err
}

// Create creates a znode with a content. If
// acl is nil then the z.Cfg.Acl set will be
// applied to the new znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Create(path string, data []byte, flags int32, acl []zk.ACL) (s string, err error) {
	path = z.fullpath(path)
	if len(acl) == 0 && len(z.Cfg.Acl) != 0 {
		acl = z.Cfg.Acl
	}
	z.Cfg.Retry("create", path, func() error {
		s, err = z.Conn.Create(path, data, flags, acl)
		return err
	})
	return s, err
}

// Delete deletes a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Delete(path string, version int32) (err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("delete", path, func() error {
		err = z.Conn.Delete(path, version)
		return err
	})
	return err
}

// Get returns the contents of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Get(path string) (d []byte, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("get", path, func() error {
		d, s, err = z.Conn.Get(path)
		return err
	})
	return d, s, err
}

// GetW returns the contents of a znode and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) GetW(path string) (d []byte, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("getw", path, func() error {
		d, s, ch, err = z.Conn.GetW(path)
		return err
	})
	return d, s, ch, err
}

// Set writes content in an existent znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Set(path string, data []byte, version int32) (s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("set", path, func() error {
		s, err = z.Conn.Set(path, data, version)
		return err
	})
	return s, err
}

// Children returns the children of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Children(path string) (c []string, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("children", path, func() error {
		c, s, err = z.Conn.Children(path)
		return err
	})
	return c, s, err
}

// ChildrenW returns the children of a znode and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) ChildrenW(path string) (c []string, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("childrenw", path, func() error {
		c, s, ch, err = z.Conn.ChildrenW(path)
		return err
	})
	return c, s, ch, err
}

// Sync performs a sync from the master in the Zookeeper server.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Sync(path string) (s string, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("sync", path, func() error {
		s, err = z.Conn.Sync(path)
		return err
	})
	return s, err
}

// GetACL returns the ACL for a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) GetACL(path string) (a []zk.ACL, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("getacl", path, func() error {
		a, s, err = z.Conn.GetACL(path)
		return err
	})
	return a, s, err
}

// SetACL sets a ACL to a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SetACL(path string, acl []zk.ACL, version int32) (s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("setacl", path, func() error {
		s, err = z.Conn.SetACL(path, acl, version)
		return err
	})
	return s, err
}

// CreateProtectedEphemeralSequential creates a sequential ephemeral znode.
// z.Cfg.Chroot will be prepended to path. The call will be NOT be retried.
func (z *Client) CreateProtectedEphemeralSequential(path string, data []byte, acl []zk.ACL) (string, error) {
	path = z.fullpath(path)
	return z.Conn.CreateProtectedEphemeralSequential(path, data, acl)
}

// CreateDir is a helper method that creates and empty znode if it does not exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) CreateDir(path string, acl []zk.ACL) error {
	path = z.fullpath(path)
	ok, _, err := z.Exists(path)
	if err != nil {
		return err
	}

	if !ok {
		_, err = z.Create(path, []byte{}, 0, acl)
		if err == zk.ErrNodeExists {
			return nil
		}
	}

	return err
}

// SafeSet is a helper method that writes a znode creating it first if it does not exists. It will sync the Zookeeper before checking if the node exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SafeSet(path string, data []byte, version int32, acl []zk.ACL) (*zk.Stat, error) {
	path = z.fullpath(path)
	_, err := z.Sync(path)
	if err != nil {
		return nil, err
	}

	ok, _, err := z.Exists(path)
	if err != nil {
		return nil, err
	}

	if !ok {
		_, err := z.Create(path, data, 0, acl)
		if err != nil {
			return nil, err
		}
		_, s, err := z.Exists(path)
		return s, err
	}

	return z.Set(path, data, version)
}

// SafeGet is a helper method that syncs Zookeeper and return the content of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SafeGet(path string) ([]byte, *zk.Stat, error) {
	path = z.fullpath(path)
	_, err := z.Sync(path)
	if err != nil {
		return nil, nil, err
	}

	return z.Get(path)
}

// fullpath returns the path with the chroot prepended.
func (z *Client) fullpath(path string) string {
	//	if z.Cfg.Chroot != "" && !strings.HasPrefix(path, z.Cfg.Chroot) {
	//    return z.Cfg.Chroot + path
	//	}
	//	return path

	return z.Cfg.Chroot + path
}

// The DefaultRetry function will retry four times if the
// first Zookeeper call fails, after sleeping in turn: 0ms, 100ms, 500ms and 1500ms.
func DefaultRetry(op, path string, f func() error) {
	retry.NewExecutor().
		WithRetries(4).
		WithBackoff(retry.ExponentialDelayBackoff(100*time.Millisecond, 5)).
		WithErrorComparator(func(err error) bool {
		return err == zk.ErrConnectionClosed || err == zk.ErrSessionExpired || err == zk.ErrSessionMoved
	}).Execute(f)
}

// A convenience version of Delete, DeleteNode deletes a znode,
// using version -1 to delete any version.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) DeleteNode(path string) error {
	return z.Delete(path, -1)
}

// A convenience version of Create, CreateNode supplies
// empty data, 0 flags, and the default z.Cfg.Acl list
// to a z.Create() call.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) CreateNode(path string) error {
	_, err := z.Create(path, []byte{}, 0, z.Cfg.Acl)
	return err
}
<commit_msg>atg. fix dup fullpath issue.<commit_after>/*
Package ezk is an enhanced Zookeeper client. It uses the
connection and underlying operations from
https://github.com/samuel/go-zookeeper/zk
in conjunction with automatic retry of operations via the
https://github.com/betable/retry library.
*/
package ezk

import (
	"fmt"
	"github.com/betable/retry"
	"github.com/samuel/go-zookeeper/zk"
	"time"
)

// Client is a wrapper over github.com/samuel/go-zookeeper/zk that retries all but one of its operations according to the ClientConfig.Retry function. The one exception is for CreateProtectedEphemeralSequential(); it is not retried automatically.
type Client struct {

	// The configuration for the client.
	Cfg ClientConfig

	// The underlying github.com/samuel/go-zookeeper/zk connection.
	Conn *zk.Conn

	// WatchCh will be nil until Connect returns without error.
	// Watches that fire over the Zookeeper connection will be
	// received on WatchCh.
	WatchCh <-chan zk.Event
}

// ClientConfig is used to configure a Client; pass
// it to NewClient().
type ClientConfig struct {

	// The Chroot directory will be prepended to all paths
	Chroot string

	// The set of ACLs used by defeault when
	// calling Client.Create(), if the formal
	// parameter acl in Create() is length 0.
	Acl []zk.ACL

	// The URLs of the zookeepers to attempt to connect to.
	Servers []string

	// SessionTimeout defaults to 10 seconds if not
	// otherwise set.
	SessionTimeout time.Duration

	// The Retry function determines how many times
	// and how often we retry our Zookeeper operations
	// before failing. See DefaultRetry() which is
	// used if this is not otherwise set.
	Retry Retry
}

// NewClient creates a new ezk.Client.
// If the cfg.SessionTimout is set to 0
// a default value of 10 seconds will be used.
// If cfg.Retry is nil then the DefaultRetry
// function will be used.
// If cfg.Acl is length 0 then zk.WorldACL(zk.PermAll)
// will be used.
func NewClient(cfg ClientConfig) *Client {
	if cfg.Retry == nil {
		cfg.Retry = DefaultRetry
	}
	if cfg.SessionTimeout == 0 {
		cfg.SessionTimeout = 10 * time.Second
	}
	if len(cfg.Acl) == 0 {
		cfg.Acl = zk.WorldACL(zk.PermAll)
	}
	cli := &Client{
		Cfg: cfg,
	}
	return cli
}

// Retry defines the type of the retry method to use.
type Retry func(op, path string, f func() error)

// Connect connects to a Zookeeper server.
// Upon success it sets the z.WatchCh and
// z.Conn and returns nil.
// If an error is returned then z.WatchCh and
// z.Conn will not be set. Connect() will
// typically only be needed once, but can be
// called again if need be.
func (z *Client) Connect() error {
	z.WatchCh = nil
	z.Conn = nil
	conn, ch, err := zk.Connect(z.Cfg.Servers, z.Cfg.SessionTimeout)
	if err != nil {
		return err
	}
	z.Conn = conn
	z.WatchCh = ch

	// make the Chroot dir; deliberately ignore err as
	// the Chroot node may already exist.
	z.Create("", []byte{}, 0, z.Cfg.Acl)
	return nil
}

// Close closes the connection to the Zookeeper server.
func (z *Client) Close() {
	z.Conn.Close()
}

// Exists checks if a znode exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Exists(path string) (ok bool, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("exists", path, func() error {
		ok, s, err = z.Conn.Exists(path)
		return err
	})
	return ok, s, err
}

// ExistsW returns if a znode exists and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) ExistsW(path string) (ok bool, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("existsw", path, func() error {
		ok, s, ch, err = z.Conn.ExistsW(path)
		return err
	})
	return ok, s, ch, err
}

// Create creates a znode with a content. If
// acl is nil then the z.Cfg.Acl set will be
// applied to the new znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Create(path string, data []byte, flags int32, acl []zk.ACL) (s string, err error) {
	path = z.fullpath(path)
	if len(acl) == 0 && len(z.Cfg.Acl) != 0 {
		acl = z.Cfg.Acl
	}
	z.Cfg.Retry("create", path, func() error {
		s, err = z.Conn.Create(path, data, flags, acl)
		return err
	})
	return s, err
}

// Delete deletes a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Delete(path string, version int32) (err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("delete", path, func() error {
		err = z.Conn.Delete(path, version)
		return err
	})
	return err
}

// Get returns the contents of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Get(path string) (d []byte, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("get", path, func() error {
		d, s, err = z.Conn.Get(path)
		return err
	})
	return d, s, err
}

// GetW returns the contents of a znode and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) GetW(path string) (d []byte, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("getw", path, func() error {
		d, s, ch, err = z.Conn.GetW(path)
		return err
	})
	return d, s, ch, err
}

// Set writes content in an existent znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Set(path string, data []byte, version int32) (s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("set", path, func() error {
		fmt.Printf("Set on path='%s'\n", path)
		s, err = z.Conn.Set(path, data, version)
		return err
	})
	return s, err
}

// Children returns the children of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Children(path string) (c []string, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("children", path, func() error {
		c, s, err = z.Conn.Children(path)
		return err
	})
	return c, s, err
}

// ChildrenW returns the children of a znode and sets a watch.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) ChildrenW(path string) (c []string, s *zk.Stat, ch <-chan zk.Event, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("childrenw", path, func() error {
		c, s, ch, err = z.Conn.ChildrenW(path)
		return err
	})
	return c, s, ch, err
}

// Sync performs a sync from the master in the Zookeeper server.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) Sync(path string) (s string, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("sync", path, func() error {
		s, err = z.Conn.Sync(path)
		return err
	})
	return s, err
}

// GetACL returns the ACL for a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) GetACL(path string) (a []zk.ACL, s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("getacl", path, func() error {
		a, s, err = z.Conn.GetACL(path)
		return err
	})
	return a, s, err
}

// SetACL sets a ACL to a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SetACL(path string, acl []zk.ACL, version int32) (s *zk.Stat, err error) {
	path = z.fullpath(path)
	z.Cfg.Retry("setacl", path, func() error {
		s, err = z.Conn.SetACL(path, acl, version)
		return err
	})
	return s, err
}

// CreateProtectedEphemeralSequential creates a sequential ephemeral znode.
// z.Cfg.Chroot will be prepended to path. The call will be NOT be retried.
func (z *Client) CreateProtectedEphemeralSequential(path string, data []byte, acl []zk.ACL) (string, error) {
	path = z.fullpath(path)
	return z.Conn.CreateProtectedEphemeralSequential(path, data, acl)
}

// CreateDir is a helper method that creates and empty znode if it does not exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) CreateDir(path string, acl []zk.ACL) error {
	path = z.fullpath(path)
	ok, _, err := z.Exists(path)
	if err != nil {
		return err
	}

	if !ok {
		_, err = z.Create(path, []byte{}, 0, acl)
		if err == zk.ErrNodeExists {
			return nil
		}
	}

	return err
}

// SafeSet is a helper method that writes a znode creating it first if it does not exists. It will sync the Zookeeper before checking if the node exists.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SafeSet(path string, data []byte, version int32, acl []zk.ACL) (*zk.Stat, error) {
	_, err := z.Sync(path)
	if err != nil {
		return nil, err
	}

	ok, _, err := z.Exists(path)
	if err != nil {
		return nil, err
	}

	if !ok {
		_, err := z.Create(path, data, 0, acl)
		if err != nil {
			return nil, err
		}
		_, s, err := z.Exists(path)
		return s, err
	}

	return z.Set(path, data, version)
}

// SafeGet is a helper method that syncs Zookeeper and return the content of a znode.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) SafeGet(path string) ([]byte, *zk.Stat, error) {
	_, err := z.Sync(path)
	if err != nil {
		return nil, nil, err
	}

	return z.Get(path)
}

// fullpath returns the path with the chroot prepended.
func (z *Client) fullpath(path string) string {
	//	if z.Cfg.Chroot != "" && !strings.HasPrefix(path, z.Cfg.Chroot) {
	//    return z.Cfg.Chroot + path
	//	}
	//	return path

	return z.Cfg.Chroot + path
}

// The DefaultRetry function will retry four times if the
// first Zookeeper call fails, after sleeping in turn: 0ms, 100ms, 500ms and 1500ms.
func DefaultRetry(op, path string, f func() error) {
	retry.NewExecutor().
		WithRetries(4).
		WithBackoff(retry.ExponentialDelayBackoff(100*time.Millisecond, 5)).
		WithErrorComparator(func(err error) bool {
		return err == zk.ErrConnectionClosed || err == zk.ErrSessionExpired || err == zk.ErrSessionMoved
	}).Execute(f)
}

// A convenience version of Delete, DeleteNode deletes a znode,
// using version -1 to delete any version.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) DeleteNode(path string) error {
	return z.Delete(path, -1)
}

// A convenience version of Create, CreateNode supplies
// empty data, 0 flags, and the default z.Cfg.Acl list
// to a z.Create() call.
// z.Cfg.Chroot will be prepended to path. The call will be retried.
func (z *Client) CreateNode(path string) error {
	_, err := z.Create(path, []byte{}, 0, z.Cfg.Acl)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Client struct {
	conn       net.Conn
	reader     *bufio.Reader
	writer     *bufio.Writer
	responseCh chan []byte
	Outgoing   chan []byte
}

func MakeClient(conn net.Conn, respCh chan []byte) *Client {
	client := &Client{
		conn:       conn,
		reader:     bufio.NewReader(conn),
		writer:     bufio.NewWriter(conn),
		responseCh: respCh,
		Outgoing:   make(chan []byte),
	}

	go client.Read()
	go client.write()

	ohai := MakeWelcomeMsg()
	data, _ := ohai.Pack()
	client.Outgoing <- data

	return client
}

func MakeWelcomeMsg() *baps3.Message {
	return baps3.NewMessage(baps3.RsOhai).AddArg("listd").AddArg("0.0")
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) Read() {
	for {
		data, _ := c.reader.ReadBytes('\n')
		c.responseCh <- data // Each client doesn't care what it got, that's for the server to handle
	}
}

func (c *Client) write() {
	for data := range c.Outgoing {
		_, err := c.writer.Write(data)
		if err != nil {
			continue // TODO Handle
		}
		c.writer.Flush()
	}
}
<commit_msg>Remove unnecessary bufio.Writer<commit_after>package main

import (
	"bufio"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Client struct {
	conn       net.Conn
	reader     *bufio.Reader
	responseCh chan []byte
	Outgoing   chan []byte
}

func MakeClient(conn net.Conn, respCh chan []byte) *Client {
	client := &Client{
		conn:       conn,
		reader:     bufio.NewReader(conn),
		responseCh: respCh,
		Outgoing:   make(chan []byte),
	}

	go client.Read()
	go client.write()

	ohai := MakeWelcomeMsg()
	data, _ := ohai.Pack()
	client.Outgoing <- data

	return client
}

func MakeWelcomeMsg() *baps3.Message {
	return baps3.NewMessage(baps3.RsOhai).AddArg("listd").AddArg("0.0")
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) Read() {
	for {
		data, _ := c.reader.ReadBytes('\n')
		c.responseCh <- data // Each client doesn't care what it got, that's for the server to handle
	}
}

func (c *Client) write() {
	for data := range c.Outgoing {
		_, err := c.conn.Write(data)
		if err != nil {
			continue // TODO Handle
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Prometheus Team
// 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.

// Prometheus HTTP API client functionality.
//
// TODO(julius): This functionality should be moved to a separate
// library/repository once we have a good name for it (client_* is already used
// for the interface between metrics-exposing servers and Prometheus).

package main

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

const (
	queryPath      = "/api/query"
	queryRangePath = "/api/query_range"
	metricsPath    = "/api/metrics"

	scalarType = "scalar"
	vectorType = "vector"
	matrixType = "matrix"
	errorType  = "error"
)

// Client is a client for executing queries against the Prometheus API.
type Client struct {
	Endpoint   string
	httpClient http.Client
}

// transport builds a new transport with the provided timeout.
func transport(netw, addr string, timeout time.Duration) (connection net.Conn, err error) {
	deadline := time.Now().Add(timeout)
	connection, err = net.DialTimeout(netw, addr, timeout)
	if err == nil {
		connection.SetDeadline(deadline)
	}
	return
}

// NewClient creates a new Client, given a server URL and timeout.
func NewClient(url string, timeout time.Duration) *Client {
	return &Client{
		Endpoint: url,
		httpClient: http.Client{
			Transport: &http.Transport{
				Dial: func(netw, addr string) (net.Conn, error) { return transport(netw, addr, timeout) },
			},
		},
	}
}

// Query performs an instant expression query via the Prometheus API.
func (c *Client) Query(expr string) (QueryResponse, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = queryPath
	q := u.Query()

	q.Set("expr", expr)
	u.RawQuery = q.Encode()

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r StubQueryResponse
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	if r.Version != 1 {
		return nil, fmt.Errorf("unsupported JSON format version %d", r.Version)
	}

	var typedResp QueryResponse
	switch r.Type {
	case errorType:
		return nil, fmt.Errorf("query error: %s", r.Value.(string))
	case scalarType:
		typedResp = &ScalarQueryResponse{}
	case vectorType:
		typedResp = &VectorQueryResponse{}
	case matrixType:
		typedResp = &MatrixQueryResponse{}
	default:
		return nil, fmt.Errorf("invalid response type %s", r.Type)
	}

	if err := json.Unmarshal(buf, typedResp); err != nil {
		return nil, err
	}
	return typedResp, err
}

// QueryRange performs an range expression query via the Prometheus API.
func (c *Client) QueryRange(expr string, end float64, rangeSec uint64, step uint64) (*MatrixQueryResponse, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = queryRangePath
	q := u.Query()

	q.Set("expr", expr)
	q.Set("end", fmt.Sprintf("%f", end))
	q.Set("range", fmt.Sprintf("%d", rangeSec))
	q.Set("step", fmt.Sprintf("%d", step))
	u.RawQuery = q.Encode()

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r StubQueryResponse
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	if r.Version != 1 {
		return nil, fmt.Errorf("unsupported JSON format version %d", r.Version)
	}

	switch r.Type {
	case errorType:
		return nil, fmt.Errorf("query error: %s", r.Value.(string))
	case matrixType:
		var typedResp MatrixQueryResponse
		if err := json.Unmarshal(buf, &typedResp); err != nil {
			return nil, err
		}
		return &typedResp, nil
	default:
		return nil, fmt.Errorf("invalid response type %s", r.Type)
	}
}

// Metrics retrieves the list of available metric names via the Prometheus API.
func (c *Client) Metrics() ([]string, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = metricsPath

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r []string
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	return r, nil
}
<commit_msg>don't overwrite URL.Path<commit_after>// Copyright 2013 Prometheus Team
// 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.

// Prometheus HTTP API client functionality.
//
// TODO(julius): This functionality should be moved to a separate
// library/repository once we have a good name for it (client_* is already used
// for the interface between metrics-exposing servers and Prometheus).

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	queryPath      = "/api/query"
	queryRangePath = "/api/query_range"
	metricsPath    = "/api/metrics"

	scalarType = "scalar"
	vectorType = "vector"
	matrixType = "matrix"
	errorType  = "error"
)

// Client is a client for executing queries against the Prometheus API.
type Client struct {
	Endpoint   string
	httpClient http.Client
}

// transport builds a new transport with the provided timeout.
func transport(netw, addr string, timeout time.Duration) (connection net.Conn, err error) {
	deadline := time.Now().Add(timeout)
	connection, err = net.DialTimeout(netw, addr, timeout)
	if err == nil {
		connection.SetDeadline(deadline)
	}
	return
}

// NewClient creates a new Client, given a server URL and timeout.
func NewClient(url string, timeout time.Duration) *Client {
	return &Client{
		Endpoint: url,
		httpClient: http.Client{
			Transport: &http.Transport{
				Dial: func(netw, addr string) (net.Conn, error) { return transport(netw, addr, timeout) },
			},
		},
	}
}

// Query performs an instant expression query via the Prometheus API.
func (c *Client) Query(expr string) (QueryResponse, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = strings.TrimRight(u.Path, "/") + queryPath
	q := u.Query()

	q.Set("expr", expr)
	u.RawQuery = q.Encode()

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r StubQueryResponse
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	if r.Version != 1 {
		return nil, fmt.Errorf("unsupported JSON format version %d", r.Version)
	}

	var typedResp QueryResponse
	switch r.Type {
	case errorType:
		return nil, fmt.Errorf("query error: %s", r.Value.(string))
	case scalarType:
		typedResp = &ScalarQueryResponse{}
	case vectorType:
		typedResp = &VectorQueryResponse{}
	case matrixType:
		typedResp = &MatrixQueryResponse{}
	default:
		return nil, fmt.Errorf("invalid response type %s", r.Type)
	}

	if err := json.Unmarshal(buf, typedResp); err != nil {
		return nil, err
	}
	return typedResp, err
}

// QueryRange performs an range expression query via the Prometheus API.
func (c *Client) QueryRange(expr string, end float64, rangeSec uint64, step uint64) (*MatrixQueryResponse, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = strings.TrimRight(u.Path, "/") + queryRangePath
	q := u.Query()

	q.Set("expr", expr)
	q.Set("end", fmt.Sprintf("%f", end))
	q.Set("range", fmt.Sprintf("%d", rangeSec))
	q.Set("step", fmt.Sprintf("%d", step))
	u.RawQuery = q.Encode()

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r StubQueryResponse
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	if r.Version != 1 {
		return nil, fmt.Errorf("unsupported JSON format version %d", r.Version)
	}

	switch r.Type {
	case errorType:
		return nil, fmt.Errorf("query error: %s", r.Value.(string))
	case matrixType:
		var typedResp MatrixQueryResponse
		if err := json.Unmarshal(buf, &typedResp); err != nil {
			return nil, err
		}
		return &typedResp, nil
	default:
		return nil, fmt.Errorf("invalid response type %s", r.Type)
	}
}

// Metrics retrieves the list of available metric names via the Prometheus API.
func (c *Client) Metrics() ([]string, error) {
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return nil, err
	}

	u.Path = strings.TrimRight(u.Path, "/") + metricsPath

	resp, err := c.httpClient.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var r []string
	if err := json.Unmarshal(buf, &r); err != nil {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>// S3 interface
package main

// FIXME need to prevent anything but ListDir working for s3://

import (
	"errors"
	"flag"
	"fmt"
	"github.com/ncw/swift"
	"io"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
	"log"
	"mime"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Constants
const (
	metaMtime = "X-Amz-Meta-Mtime" // the meta key to store mtime in
)

// FsS3 represents a remote s3 server
type FsS3 struct {
	c      *s3.S3     // the connection to the s3 server
	b      *s3.Bucket // the connection to the bucket
	bucket string     // the bucket we are working on
	perm   s3.ACL     // permissions for new buckets / objects
}

// FsObjectS3 describes a s3 object
type FsObjectS3 struct {
	// Will definitely have everything but meta which may be nil
	//
	// List will read everything but meta - to fill that in need to call
	// readMetaData
	s3           *FsS3      // what this object is part of
	remote       string     // The remote path
	etag         string     // md5sum of the object
	bytes        int64      // size of the object
	lastModified time.Time  // Last modified
	meta         s3.Headers // The object metadata if known - may be nil
}

// ------------------------------------------------------------

// Globals
var (
	// Flags
	awsAccessKeyId     = flag.String("aws-access-key-id", os.Getenv("AWS_ACCESS_KEY_ID"), "AWS Access Key ID. Defaults to environment var AWS_ACCESS_KEY_ID.")
	awsSecretAccessKey = flag.String("aws-secret-access-key", os.Getenv("AWS_SECRET_ACCESS_KEY"), "AWS Secret Access Key (password). Defaults to environment var AWS_SECRET_ACCESS_KEY.")
	// AWS endpoints: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
	s3Endpoint           = flag.String("s3-endpoint", os.Getenv("S3_ENDPOINT"), "S3 Endpoint. Defaults to environment var S3_ENDPOINT then https://s3.amazonaws.com/.")
	s3LocationConstraint = flag.String("s3-location-constraint", os.Getenv("S3_LOCATION_CONSTRAINT"), "Location constraint for creating buckets only. Defaults to environment var S3_LOCATION_CONSTRAINT.")
)

// String converts this FsS3 to a string
func (f *FsS3) String() string {
	return fmt.Sprintf("S3 bucket %s", f.bucket)
}

// Pattern to match a s3 url
var s3Match = regexp.MustCompile(`^s3://([^/]*)(.*)$`)

// parseParse parses a s3 'url'
func s3ParsePath(path string) (bucket, directory string, err error) {
	parts := s3Match.FindAllStringSubmatch(path, -1)
	if len(parts) != 1 || len(parts[0]) != 3 {
		err = fmt.Errorf("Couldn't parse s3 url %q", path)
	} else {
		bucket, directory = parts[0][1], parts[0][2]
		directory = strings.Trim(directory, "/")
	}
	return
}

// s3Connection makes a connection to s3
func s3Connection() (*s3.S3, error) {
	// Make the auth
	if *awsAccessKeyId == "" {
		return nil, errors.New("Need -aws-access-key-id or environmental variable AWS_ACCESS_KEY_ID")
	}
	if *awsSecretAccessKey == "" {
		return nil, errors.New("Need -aws-secret-access-key or environmental variable AWS_SECRET_ACCESS_KEY")
	}
	auth := aws.Auth{AccessKey: *awsAccessKeyId, SecretKey: *awsSecretAccessKey}

	// FIXME look through all the regions by name and use one of them if found

	// Synthesize the region
	if *s3Endpoint == "" {
		*s3Endpoint = "https://s3.amazonaws.com/"
	}
	region := aws.Region{
		Name:                 "s3",
		S3Endpoint:           *s3Endpoint,
		S3LocationConstraint: false,
	}
	if *s3LocationConstraint != "" {
		region.Name = *s3LocationConstraint
		region.S3LocationConstraint = true
	}

	c := s3.New(auth, region)
	return c, nil
}

// NewFsS3 contstructs an FsS3 from the path, bucket:path
func NewFsS3(path string) (*FsS3, error) {
	bucket, directory, err := s3ParsePath(path)
	if err != nil {
		return nil, err
	}
	if directory != "" {
		return nil, fmt.Errorf("Directories not supported yet in %q: %q", path, directory)
	}
	c, err := s3Connection()
	if err != nil {
		return nil, err
	}
	f := &FsS3{
		c:      c,
		bucket: bucket,
		b:      c.Bucket(bucket),
		perm:   s3.Private, // FIXME need user to specify
	}
	return f, nil
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsS3) NewFsObjectWithInfo(remote string, info *s3.Key) FsObject {
	fs := &FsObjectS3{
		s3:     f,
		remote: remote,
	}
	if info != nil {
		// Set info but not meta
		var err error
		fs.lastModified, err = time.Parse(time.RFC3339, info.LastModified)
		if err != nil {
			FsLog(fs, "Failed to read last modified: %s", err)
			fs.lastModified = time.Now()
		}
		fs.etag = info.ETag
		fs.bytes = info.Size
	} else {
		err := fs.readMetaData() // reads info and meta, returning an error
		if err != nil {
			// logged already FsDebug("Failed to read info: %s", err)
			return nil
		}
	}
	return fs
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsS3) NewFsObject(remote string) FsObject {
	return f.NewFsObjectWithInfo(remote, nil)
}

// Walk the path returning a channel of FsObjects
func (f *FsS3) List() FsObjectsChan {
	out := make(FsObjectsChan, *checkers)
	go func() {
		// FIXME need to implement ALL loop
		objects, err := f.b.List("", "", "", 10000)
		if err != nil {
			stats.Error()
			log.Printf("Couldn't read bucket %q: %s", f.bucket, err)
		} else {
			for i := range objects.Contents {
				object := &objects.Contents[i]
				if fs := f.NewFsObjectWithInfo(object.Key, object); fs != nil {
					out <- fs
				}
			}
		}
		close(out)
	}()
	return out
}

// Lists the buckets
func (f *FsS3) ListDir() FsDirChan {
	out := make(FsDirChan, *checkers)
	go func() {
		defer close(out)
		buckets, err := f.c.List()
		if err != nil {
			stats.Error()
			log.Printf("Couldn't list buckets: %s", err)
		} else {
			for _, bucket := range buckets.Buckets {
				when, _ := time.Parse(time.RFC3339, bucket.CreationDate)
				out <- &FsDir{
					Name:  bucket.Name,
					When:  when,
					Bytes: -1,
					Count: -1,
				}
			}
		}
	}()
	return out
}

// Put the FsObject into the bucket
func (f *FsS3) Put(in io.Reader, remote string, modTime time.Time, size int64) (FsObject, error) {
	// Temporary FsObject under construction
	fs := &FsObjectS3{s3: f, remote: remote}

	// Set the mtime in the headers
	headers := s3.Headers{
		metaMtime: swift.TimeToFloatString(modTime),
	}

	// Guess the content type
	contentType := mime.TypeByExtension(path.Ext(remote))
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	_, err := fs.s3.b.PutReaderHeaders(remote, in, size, contentType, f.perm, headers)
	return fs, err
}

// Mkdir creates the bucket if it doesn't exist
func (f *FsS3) Mkdir() error {
	err := f.b.PutBucket(f.perm)
	if err, ok := err.(*s3.Error); ok {
		if err.Code == "BucketAlreadyOwnedByYou" {
			return nil
		}
	}
	return err
}

// Rmdir deletes the bucket
//
// Returns an error if it isn't empty
func (f *FsS3) Rmdir() error {
	return f.b.DelBucket()
}

// Return the precision
func (fs *FsS3) Precision() time.Duration {
	return time.Nanosecond
}

// ------------------------------------------------------------

// Return the remote path
func (fs *FsObjectS3) Remote() string {
	return fs.remote
}

// Md5sum returns the Md5sum of an object returning a lowercase hex string
func (fs *FsObjectS3) Md5sum() (string, error) {
	return strings.Trim(strings.ToLower(fs.etag), `"`), nil
}

// Size returns the size of an object in bytes
func (fs *FsObjectS3) Size() int64 {
	return fs.bytes
}

// readMetaData gets the metadata if it hasn't already been fetched
//
// it also sets the info
func (fs *FsObjectS3) readMetaData() (err error) {
	if fs.meta != nil {
		return nil
	}

	headers, err := fs.s3.b.Head(fs.remote, nil)
	if err != nil {
		FsDebug(fs, "Failed to read info: %s", err)
		return err
	}
	size, err := strconv.ParseInt(headers["Content-Length"], 10, 64)
	if err != nil {
		FsDebug(fs, "Failed to read size from: %q", headers)
		return err
	}
	fs.etag = headers["Etag"]
	fs.bytes = size
	fs.meta = headers
	if fs.lastModified, err = time.Parse(http.TimeFormat, headers["Last-Modified"]); err != nil {
		FsLog(fs, "Failed to read last modified from HEAD: %s", err)
		fs.lastModified = time.Now()
	}
	return nil
}

// ModTime returns the modification time of the object
//
// It attempts to read the objects mtime and if that isn't present the
// LastModified returned in the http headers
func (fs *FsObjectS3) ModTime() time.Time {
	err := fs.readMetaData()
	if err != nil {
		FsLog(fs, "Failed to read metadata: %s", err)
		return time.Now()
	}
	// read mtime out of metadata if available
	d, ok := fs.meta[metaMtime]
	if !ok {
		// FsDebug(fs, "No metadata")
		return fs.lastModified
	}
	modTime, err := swift.FloatStringToTime(d)
	if err != nil {
		FsLog(fs, "Failed to read mtime from object: %s", err)
		return fs.lastModified
	}
	return modTime
}

// Sets the modification time of the local fs object
func (fs *FsObjectS3) SetModTime(modTime time.Time) {
	err := fs.readMetaData()
	if err != nil {
		stats.Error()
		FsLog(fs, "Failed to read metadata: %s", err)
		return
	}
	fs.meta[metaMtime] = swift.TimeToFloatString(modTime)
	_, err = fs.s3.b.Update(fs.remote, fs.s3.perm, fs.meta)
	if err != nil {
		stats.Error()
		FsLog(fs, "Failed to update remote mtime: %s", err)
	}
}

// Is this object storable
func (fs *FsObjectS3) Storable() bool {
	return true
}

// Open an object for read
func (fs *FsObjectS3) Open() (in io.ReadCloser, err error) {
	in, err = fs.s3.b.GetReader(fs.remote)
	return
}

// Remove an object
func (fs *FsObjectS3) Remove() error {
	return fs.s3.b.Del(fs.remote)
}

// Check the interfaces are satisfied
var _ Fs = &FsS3{}
var _ FsObject = &FsObjectS3{}
<commit_msg>Update after API changes in goamz/s3<commit_after>// S3 interface
package main

// FIXME need to prevent anything but ListDir working for s3://

import (
	"errors"
	"flag"
	"fmt"
	"github.com/ncw/swift"
	"io"
	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
	"log"
	"mime"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Constants
const (
	metaMtime = "X-Amz-Meta-Mtime" // the meta key to store mtime in
)

// FsS3 represents a remote s3 server
type FsS3 struct {
	c      *s3.S3     // the connection to the s3 server
	b      *s3.Bucket // the connection to the bucket
	bucket string     // the bucket we are working on
	perm   s3.ACL     // permissions for new buckets / objects
}

// FsObjectS3 describes a s3 object
type FsObjectS3 struct {
	// Will definitely have everything but meta which may be nil
	//
	// List will read everything but meta - to fill that in need to call
	// readMetaData
	s3           *FsS3      // what this object is part of
	remote       string     // The remote path
	etag         string     // md5sum of the object
	bytes        int64      // size of the object
	lastModified time.Time  // Last modified
	meta         s3.Headers // The object metadata if known - may be nil
}

// ------------------------------------------------------------

// Globals
var (
	// Flags
	awsAccessKeyId     = flag.String("aws-access-key-id", os.Getenv("AWS_ACCESS_KEY_ID"), "AWS Access Key ID. Defaults to environment var AWS_ACCESS_KEY_ID.")
	awsSecretAccessKey = flag.String("aws-secret-access-key", os.Getenv("AWS_SECRET_ACCESS_KEY"), "AWS Secret Access Key (password). Defaults to environment var AWS_SECRET_ACCESS_KEY.")
	// AWS endpoints: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
	s3Endpoint           = flag.String("s3-endpoint", os.Getenv("S3_ENDPOINT"), "S3 Endpoint. Defaults to environment var S3_ENDPOINT then https://s3.amazonaws.com/.")
	s3LocationConstraint = flag.String("s3-location-constraint", os.Getenv("S3_LOCATION_CONSTRAINT"), "Location constraint for creating buckets only. Defaults to environment var S3_LOCATION_CONSTRAINT.")
)

// String converts this FsS3 to a string
func (f *FsS3) String() string {
	return fmt.Sprintf("S3 bucket %s", f.bucket)
}

// Pattern to match a s3 url
var s3Match = regexp.MustCompile(`^s3://([^/]*)(.*)$`)

// parseParse parses a s3 'url'
func s3ParsePath(path string) (bucket, directory string, err error) {
	parts := s3Match.FindAllStringSubmatch(path, -1)
	if len(parts) != 1 || len(parts[0]) != 3 {
		err = fmt.Errorf("Couldn't parse s3 url %q", path)
	} else {
		bucket, directory = parts[0][1], parts[0][2]
		directory = strings.Trim(directory, "/")
	}
	return
}

// s3Connection makes a connection to s3
func s3Connection() (*s3.S3, error) {
	// Make the auth
	if *awsAccessKeyId == "" {
		return nil, errors.New("Need -aws-access-key-id or environmental variable AWS_ACCESS_KEY_ID")
	}
	if *awsSecretAccessKey == "" {
		return nil, errors.New("Need -aws-secret-access-key or environmental variable AWS_SECRET_ACCESS_KEY")
	}
	auth := aws.Auth{AccessKey: *awsAccessKeyId, SecretKey: *awsSecretAccessKey}

	// FIXME look through all the regions by name and use one of them if found

	// Synthesize the region
	if *s3Endpoint == "" {
		*s3Endpoint = "https://s3.amazonaws.com/"
	}
	region := aws.Region{
		Name:                 "s3",
		S3Endpoint:           *s3Endpoint,
		S3LocationConstraint: false,
	}
	if *s3LocationConstraint != "" {
		region.Name = *s3LocationConstraint
		region.S3LocationConstraint = true
	}

	c := s3.New(auth, region)
	return c, nil
}

// NewFsS3 contstructs an FsS3 from the path, bucket:path
func NewFsS3(path string) (*FsS3, error) {
	bucket, directory, err := s3ParsePath(path)
	if err != nil {
		return nil, err
	}
	if directory != "" {
		return nil, fmt.Errorf("Directories not supported yet in %q: %q", path, directory)
	}
	c, err := s3Connection()
	if err != nil {
		return nil, err
	}
	f := &FsS3{
		c:      c,
		bucket: bucket,
		b:      c.Bucket(bucket),
		perm:   s3.Private, // FIXME need user to specify
	}
	return f, nil
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsS3) NewFsObjectWithInfo(remote string, info *s3.Key) FsObject {
	fs := &FsObjectS3{
		s3:     f,
		remote: remote,
	}
	if info != nil {
		// Set info but not meta
		var err error
		fs.lastModified, err = time.Parse(time.RFC3339, info.LastModified)
		if err != nil {
			FsLog(fs, "Failed to read last modified: %s", err)
			fs.lastModified = time.Now()
		}
		fs.etag = info.ETag
		fs.bytes = info.Size
	} else {
		err := fs.readMetaData() // reads info and meta, returning an error
		if err != nil {
			// logged already FsDebug("Failed to read info: %s", err)
			return nil
		}
	}
	return fs
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsS3) NewFsObject(remote string) FsObject {
	return f.NewFsObjectWithInfo(remote, nil)
}

// Walk the path returning a channel of FsObjects
func (f *FsS3) List() FsObjectsChan {
	out := make(FsObjectsChan, *checkers)
	go func() {
		// FIXME need to implement ALL loop
		objects, err := f.b.List("", "", "", 10000)
		if err != nil {
			stats.Error()
			log.Printf("Couldn't read bucket %q: %s", f.bucket, err)
		} else {
			for i := range objects.Contents {
				object := &objects.Contents[i]
				if fs := f.NewFsObjectWithInfo(object.Key, object); fs != nil {
					out <- fs
				}
			}
		}
		close(out)
	}()
	return out
}

// Lists the buckets
func (f *FsS3) ListDir() FsDirChan {
	out := make(FsDirChan, *checkers)
	go func() {
		defer close(out)
		buckets, err := f.c.ListBuckets()
		if err != nil {
			stats.Error()
			log.Printf("Couldn't list buckets: %s", err)
		} else {
			for _, bucket := range buckets {
				when, _ := time.Parse(time.RFC3339, bucket.CreationDate)
				out <- &FsDir{
					Name:  bucket.Name,
					When:  when,
					Bytes: -1,
					Count: -1,
				}
			}
		}
	}()
	return out
}

// Put the FsObject into the bucket
func (f *FsS3) Put(in io.Reader, remote string, modTime time.Time, size int64) (FsObject, error) {
	// Temporary FsObject under construction
	fs := &FsObjectS3{s3: f, remote: remote}

	// Set the mtime in the headers
	headers := s3.Headers{
		metaMtime: swift.TimeToFloatString(modTime),
	}

	// Guess the content type
	contentType := mime.TypeByExtension(path.Ext(remote))
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	_, err := fs.s3.b.PutReaderHeaders(remote, in, size, contentType, f.perm, headers)
	return fs, err
}

// Mkdir creates the bucket if it doesn't exist
func (f *FsS3) Mkdir() error {
	err := f.b.PutBucket(f.perm)
	if err, ok := err.(*s3.Error); ok {
		if err.Code == "BucketAlreadyOwnedByYou" {
			return nil
		}
	}
	return err
}

// Rmdir deletes the bucket
//
// Returns an error if it isn't empty
func (f *FsS3) Rmdir() error {
	return f.b.DelBucket()
}

// Return the precision
func (fs *FsS3) Precision() time.Duration {
	return time.Nanosecond
}

// ------------------------------------------------------------

// Return the remote path
func (fs *FsObjectS3) Remote() string {
	return fs.remote
}

// Md5sum returns the Md5sum of an object returning a lowercase hex string
func (fs *FsObjectS3) Md5sum() (string, error) {
	return strings.Trim(strings.ToLower(fs.etag), `"`), nil
}

// Size returns the size of an object in bytes
func (fs *FsObjectS3) Size() int64 {
	return fs.bytes
}

// readMetaData gets the metadata if it hasn't already been fetched
//
// it also sets the info
func (fs *FsObjectS3) readMetaData() (err error) {
	if fs.meta != nil {
		return nil
	}

	headers, err := fs.s3.b.Head(fs.remote, nil)
	if err != nil {
		FsDebug(fs, "Failed to read info: %s", err)
		return err
	}
	size, err := strconv.ParseInt(headers["Content-Length"], 10, 64)
	if err != nil {
		FsDebug(fs, "Failed to read size from: %q", headers)
		return err
	}
	fs.etag = headers["Etag"]
	fs.bytes = size
	fs.meta = headers
	if fs.lastModified, err = time.Parse(http.TimeFormat, headers["Last-Modified"]); err != nil {
		FsLog(fs, "Failed to read last modified from HEAD: %s", err)
		fs.lastModified = time.Now()
	}
	return nil
}

// ModTime returns the modification time of the object
//
// It attempts to read the objects mtime and if that isn't present the
// LastModified returned in the http headers
func (fs *FsObjectS3) ModTime() time.Time {
	err := fs.readMetaData()
	if err != nil {
		FsLog(fs, "Failed to read metadata: %s", err)
		return time.Now()
	}
	// read mtime out of metadata if available
	d, ok := fs.meta[metaMtime]
	if !ok {
		// FsDebug(fs, "No metadata")
		return fs.lastModified
	}
	modTime, err := swift.FloatStringToTime(d)
	if err != nil {
		FsLog(fs, "Failed to read mtime from object: %s", err)
		return fs.lastModified
	}
	return modTime
}

// Sets the modification time of the local fs object
func (fs *FsObjectS3) SetModTime(modTime time.Time) {
	err := fs.readMetaData()
	if err != nil {
		stats.Error()
		FsLog(fs, "Failed to read metadata: %s", err)
		return
	}
	fs.meta[metaMtime] = swift.TimeToFloatString(modTime)
	_, err = fs.s3.b.Update(fs.remote, fs.s3.perm, fs.meta)
	if err != nil {
		stats.Error()
		FsLog(fs, "Failed to update remote mtime: %s", err)
	}
}

// Is this object storable
func (fs *FsObjectS3) Storable() bool {
	return true
}

// Open an object for read
func (fs *FsObjectS3) Open() (in io.ReadCloser, err error) {
	in, err = fs.s3.b.GetReader(fs.remote)
	return
}

// Remove an object
func (fs *FsObjectS3) Remove() error {
	return fs.s3.b.Del(fs.remote)
}

// Check the interfaces are satisfied
var _ Fs = &FsS3{}
var _ FsObject = &FsObjectS3{}
<|endoftext|>


<commit_before>package beanstalk

import (
	"errors"
	"io"
	"net"
	"net/textproto"
	"strconv"
	"strings"
	"time"
)

// Errors that can be returned by the beanstalk client functions.
var (
	ErrBuried         = errors.New("Job is buried")
	ErrDraining       = errors.New("Server in draining mode")
	ErrExpectedCRLF   = errors.New("Expected CRLF after job body")
	ErrJobTooBig      = errors.New("Job body too big")
	ErrNotConnected   = errors.New("Not connected")
	ErrNotFound       = errors.New("Job not found")
	ErrNotIgnored     = errors.New("Tube cannot be ignored")
	ErrOutOfMemory    = errors.New("Server is out of memory")
	ErrUnexpectedResp = errors.New("Unexpected response from server")
)

// Client implements a simple beanstalk API.
type Client struct {
	options     Options
	conn        net.Conn
	textConn    *textproto.Conn
	isConnected bool
}

// NewClient returns a new beanstalk Client object.
func NewClient(conn net.Conn, options Options) *Client {
	return &Client{
		options:     SanitizeOptions(options),
		conn:        conn,
		textConn:    textproto.NewConn(conn),
		isConnected: true}
}

// Close the connection to the beanstalk server.
func (client *Client) Close() {
	if client.conn == nil {
		return
	}

	client.options.LogInfo("Closing connection to beanstalk server %s (local=%s)", client.conn.RemoteAddr().String(), client.conn.LocalAddr().String())
	client.conn.Close()
	client.conn = nil
}

// Bury a reserved job. This is done after being unable to process the job and
// it is likely that other consumers won't either.
func (client *Client) Bury(job *Job, priority uint32) error {
	_, _, err := client.requestResponse("bury %d %d", job.ID, priority)
	if err == ErrBuried {
		return nil
	}

	return err
}

// Delete a reserved job. This is done after successful processing.
func (client *Client) Delete(job *Job) error {
	_, _, err := client.requestResponse("delete %d", job.ID)
	return err
}

// Ignore removes an active tube from the watch list.
func (client *Client) Ignore(tube string) error {
	_, _, err := client.requestResponse("ignore %s", tube)
	return err
}

// Put a new job into beanstalk.
func (client *Client) Put(putRequest *PutRequest) (uint64, error) {
	id, _, err := client.requestResponse("put %d %d %d %d\r\n%s",
		putRequest.Params.Priority,
		putRequest.Params.Delay/time.Second,
		putRequest.Params.TTR/time.Second,
		len(putRequest.Body),
		putRequest.Body)

	return id, err
}

// Release a reserved job. This is done after being unable to process the job,
// but another consumer might be successful.
func (client *Client) Release(job *Job, priority uint32, delay time.Duration) error {
	_, _, err := client.requestResponse("release %d %d %d", job.ID, priority, delay/time.Second)
	return err
}

// Reserve retrieves a new job.
func (client *Client) Reserve() (*Job, error) {
	err := client.request("reserve-with-timeout %d", client.options.ReserveTimeout/time.Second)
	if err != nil {
		return nil, err
	}

	// Set a read deadline that is slightly longer than the reserve timeout.
	if client.options.ReserveTimeout != 0 {
		client.conn.SetReadDeadline(time.Now().Add(client.options.ReserveTimeout + time.Second))
		defer client.conn.SetReadDeadline(time.Time{})
	}

	job := &Job{TTR: time.Second}
	job.ID, job.Body, err = client.response()
	if err != nil {
		return nil, err
	}
	if job.ID == 0 {
		return nil, nil
	}

	// Fetch the TTR value for this job via stats-job. If this fails, ignore it.
	if _, yaml, err := client.requestResponse("stats-job %d", job.ID); err == nil {
		if val, err := yamlValue(yaml, "pri"); err == nil {
			if prio, err := strconv.ParseUint(val, 10, 32); err == nil {
				job.Priority = uint32(prio)
			}
		}

		if val, err := yamlValue(yaml, "ttr"); err == nil {
			if ttr, err := strconv.Atoi(val); err == nil {
				job.TTR = time.Duration(ttr) * time.Second
			}
		}
	}

	// Shave off 100 milliseconds to have some room to play with between touches.
	job.TTR -= 100 * time.Millisecond

	return job, nil
}

// Touch a job to extend the TTR of the reserved job.
func (client *Client) Touch(job *Job) error {
	_, _, err := client.requestResponse("touch %d", job.ID)
	return err
}

// Use the specified tube for the upcoming put requests.
func (client *Client) Use(tube string) error {
	_, _, err := client.requestResponse("use %s", tube)
	return err
}

// Watch adds a tube to the watch list.
func (client *Client) Watch(tube string) error {
	_, _, err := client.requestResponse("watch %s", tube)
	return err
}

// request sends a request to the beanstalk server.
func (client *Client) request(format string, args ...interface{}) error {
	if client.options.ReadWriteTimeout != 0 {
		client.conn.SetWriteDeadline(time.Now().Add(client.options.ReadWriteTimeout))
		defer client.conn.SetWriteDeadline(time.Time{})
	}

	if err := client.textConn.PrintfLine(format, args...); err != nil {
		return err
	}

	return nil
}

// response reads and parses a response from the beanstalk server.
func (client *Client) response() (uint64, []byte, error) {
	line, err := client.textConn.ReadLine()
	if err != nil {
		return 0, nil, err
	}

	items := strings.SplitN(line, " ", 2)
	if len(items[0]) == 0 {
		return 0, nil, ErrUnexpectedResp
	}

	var response, rest = items[0], ""
	if len(items) == 2 {
		rest = items[1]
	}

	switch response {
	// Simple successful responses.
	case "DELETED", "RELEASED", "TIMED_OUT", "TOUCHED", "USING", "WATCHING":
		return 0, nil, nil

	// BURIED can either be a successful response to a _bury_ command or an
	// unsuccessful response to the _put_ and _release_ commands.
	case "BURIED":
		if rest != "" {
			// The response to the _put_ command provides an id of the job.
			if id, err := strconv.ParseUint(rest, 10, 64); err == nil {
				return id, nil, ErrBuried
			}
		}

		return 0, nil, ErrBuried

	// INSERTED is a successful response to a _put_ command.
	case "INSERTED":
		if id, err := strconv.ParseUint(rest, 10, 64); err == nil {
			return id, nil, nil
		}

	// OK is a successful response to a request that responds with YAML data.
	case "OK":
		if size, err := strconv.Atoi(rest); err == nil {
			body := make([]byte, size+2)
			if _, err := io.ReadFull(client.textConn.R, body); err != nil {
				break
			}

			return 0, body[:size], nil
		}

	// A RESERVED response is a successful response to a _reserve_ command.
	case "RESERVED":
		resInfo := strings.SplitN(rest, " ", 2)
		if len(resInfo) != 2 {
			break
		}

		id, err := strconv.ParseUint(resInfo[0], 10, 64)
		if err != nil {
			break
		}
		size, err := strconv.Atoi(resInfo[1])
		if err != nil {
			break
		}

		body := make([]byte, size+2)
		if _, err := io.ReadFull(client.textConn.R, body); err != nil {
			break
		}

		return id, body[:size], nil

	// NOT_FOUND is a response to an unsuccessful _bury_, _delete_, _touch_ or
	// _release_ command.
	case "NOT_FOUND":
		return 0, nil, ErrNotFound

	// NOT_IGNORED is a response to an unsuccessful _ignore_ command.
	case "NOT_IGNORED":
		return 0, nil, ErrNotIgnored

	// The following responses can occur after an unsuccessful _put_ command.
	case "DRAINING":
		return 0, nil, ErrDraining
	case "EXPECTED_CRLF":
		return 0, nil, ErrExpectedCRLF
	case "JOB_TOO_BIG":
		return 0, nil, ErrJobTooBig
	case "OUT_OF_MEMORY":
		return 0, nil, ErrOutOfMemory
	}

	return 0, nil, ErrUnexpectedResp
}

// requestResponse sends a request to the beanstalk server and then parses
// and returns its response.
func (client *Client) requestResponse(format string, args ...interface{}) (uint64, []byte, error) {
	if err := client.request(format, args...); err != nil {
		return 0, nil, err
	}

	if client.options.ReadWriteTimeout != 0 {
		client.conn.SetReadDeadline(time.Now().Add(client.options.ReadWriteTimeout))
		defer client.conn.SetReadDeadline(time.Time{})
	}

	return client.response()
}
<commit_msg>Instead of returning error io.EOF, return ErrConnectionClosed which prints a nicer error message<commit_after>package beanstalk

import (
	"errors"
	"io"
	"net"
	"net/textproto"
	"strconv"
	"strings"
	"time"
)

// Errors that can be returned by the beanstalk client functions.
var (
	ErrBuried           = errors.New("Job is buried")
	ErrConnectionClosed = errors.New("Remote end closed connection")
	ErrDraining         = errors.New("Server in draining mode")
	ErrExpectedCRLF     = errors.New("Expected CRLF after job body")
	ErrJobTooBig        = errors.New("Job body too big")
	ErrNotConnected     = errors.New("Not connected")
	ErrNotFound         = errors.New("Job not found")
	ErrNotIgnored       = errors.New("Tube cannot be ignored")
	ErrOutOfMemory      = errors.New("Server is out of memory")
	ErrUnexpectedResp   = errors.New("Unexpected response from server")
)

// Client implements a simple beanstalk API.
type Client struct {
	options     Options
	conn        net.Conn
	textConn    *textproto.Conn
	isConnected bool
}

// NewClient returns a new beanstalk Client object.
func NewClient(conn net.Conn, options Options) *Client {
	return &Client{
		options:     SanitizeOptions(options),
		conn:        conn,
		textConn:    textproto.NewConn(conn),
		isConnected: true}
}

// Close the connection to the beanstalk server.
func (client *Client) Close() {
	if client.conn == nil {
		return
	}

	client.options.LogInfo("Closing connection to beanstalk server %s (local=%s)", client.conn.RemoteAddr().String(), client.conn.LocalAddr().String())
	client.conn.Close()
	client.conn = nil
}

// Bury a reserved job. This is done after being unable to process the job and
// it is likely that other consumers won't either.
func (client *Client) Bury(job *Job, priority uint32) error {
	_, _, err := client.requestResponse("bury %d %d", job.ID, priority)
	if err == ErrBuried {
		return nil
	}

	return err
}

// Delete a reserved job. This is done after successful processing.
func (client *Client) Delete(job *Job) error {
	_, _, err := client.requestResponse("delete %d", job.ID)
	return err
}

// Ignore removes an active tube from the watch list.
func (client *Client) Ignore(tube string) error {
	_, _, err := client.requestResponse("ignore %s", tube)
	return err
}

// Put a new job into beanstalk.
func (client *Client) Put(putRequest *PutRequest) (uint64, error) {
	id, _, err := client.requestResponse("put %d %d %d %d\r\n%s",
		putRequest.Params.Priority,
		putRequest.Params.Delay/time.Second,
		putRequest.Params.TTR/time.Second,
		len(putRequest.Body),
		putRequest.Body)

	return id, err
}

// Release a reserved job. This is done after being unable to process the job,
// but another consumer might be successful.
func (client *Client) Release(job *Job, priority uint32, delay time.Duration) error {
	_, _, err := client.requestResponse("release %d %d %d", job.ID, priority, delay/time.Second)
	return err
}

// Reserve retrieves a new job.
func (client *Client) Reserve() (*Job, error) {
	err := client.request("reserve-with-timeout %d", client.options.ReserveTimeout/time.Second)
	if err != nil {
		return nil, err
	}

	// Set a read deadline that is slightly longer than the reserve timeout.
	if client.options.ReserveTimeout != 0 {
		client.conn.SetReadDeadline(time.Now().Add(client.options.ReserveTimeout + time.Second))
		defer client.conn.SetReadDeadline(time.Time{})
	}

	job := &Job{TTR: time.Second}
	job.ID, job.Body, err = client.response()
	if err != nil {
		return nil, err
	}
	if job.ID == 0 {
		return nil, nil
	}

	// Fetch the TTR value for this job via stats-job. If this fails, ignore it.
	if _, yaml, err := client.requestResponse("stats-job %d", job.ID); err == nil {
		if val, err := yamlValue(yaml, "pri"); err == nil {
			if prio, err := strconv.ParseUint(val, 10, 32); err == nil {
				job.Priority = uint32(prio)
			}
		}

		if val, err := yamlValue(yaml, "ttr"); err == nil {
			if ttr, err := strconv.Atoi(val); err == nil {
				job.TTR = time.Duration(ttr) * time.Second
			}
		}
	}

	// Shave off 100 milliseconds to have some room to play with between touches.
	job.TTR -= 100 * time.Millisecond

	return job, nil
}

// Touch a job to extend the TTR of the reserved job.
func (client *Client) Touch(job *Job) error {
	_, _, err := client.requestResponse("touch %d", job.ID)
	return err
}

// Use the specified tube for the upcoming put requests.
func (client *Client) Use(tube string) error {
	_, _, err := client.requestResponse("use %s", tube)
	return err
}

// Watch adds a tube to the watch list.
func (client *Client) Watch(tube string) error {
	_, _, err := client.requestResponse("watch %s", tube)
	return err
}

// request sends a request to the beanstalk server.
func (client *Client) request(format string, args ...interface{}) error {
	if client.options.ReadWriteTimeout != 0 {
		client.conn.SetWriteDeadline(time.Now().Add(client.options.ReadWriteTimeout))
		defer client.conn.SetWriteDeadline(time.Time{})
	}

	if err := client.textConn.PrintfLine(format, args...); err != nil {
		if err == io.EOF {
			return ErrConnectionClosed
		}
		return err
	}

	return nil
}

// response reads and parses a response from the beanstalk server.
func (client *Client) response() (uint64, []byte, error) {
	line, err := client.textConn.ReadLine()
	if err != nil {
		if err == io.EOF {
			return 0, nil, ErrConnectionClosed
		}
		return 0, nil, err
	}

	items := strings.SplitN(line, " ", 2)
	if len(items[0]) == 0 {
		return 0, nil, ErrUnexpectedResp
	}

	var response, rest = items[0], ""
	if len(items) == 2 {
		rest = items[1]
	}

	switch response {
	// Simple successful responses.
	case "DELETED", "RELEASED", "TIMED_OUT", "TOUCHED", "USING", "WATCHING":
		return 0, nil, nil

	// BURIED can either be a successful response to a _bury_ command or an
	// unsuccessful response to the _put_ and _release_ commands.
	case "BURIED":
		if rest != "" {
			// The response to the _put_ command provides an id of the job.
			if id, err := strconv.ParseUint(rest, 10, 64); err == nil {
				return id, nil, ErrBuried
			}
		}

		return 0, nil, ErrBuried

	// INSERTED is a successful response to a _put_ command.
	case "INSERTED":
		if id, err := strconv.ParseUint(rest, 10, 64); err == nil {
			return id, nil, nil
		}

	// OK is a successful response to a request that responds with YAML data.
	case "OK":
		if size, err := strconv.Atoi(rest); err == nil {
			body := make([]byte, size+2)
			if _, err := io.ReadFull(client.textConn.R, body); err != nil {
				break
			}

			return 0, body[:size], nil
		}

	// A RESERVED response is a successful response to a _reserve_ command.
	case "RESERVED":
		resInfo := strings.SplitN(rest, " ", 2)
		if len(resInfo) != 2 {
			break
		}

		id, err := strconv.ParseUint(resInfo[0], 10, 64)
		if err != nil {
			break
		}
		size, err := strconv.Atoi(resInfo[1])
		if err != nil {
			break
		}

		body := make([]byte, size+2)
		if _, err := io.ReadFull(client.textConn.R, body); err != nil {
			break
		}

		return id, body[:size], nil

	// NOT_FOUND is a response to an unsuccessful _bury_, _delete_, _touch_ or
	// _release_ command.
	case "NOT_FOUND":
		return 0, nil, ErrNotFound

	// NOT_IGNORED is a response to an unsuccessful _ignore_ command.
	case "NOT_IGNORED":
		return 0, nil, ErrNotIgnored

	// The following responses can occur after an unsuccessful _put_ command.
	case "DRAINING":
		return 0, nil, ErrDraining
	case "EXPECTED_CRLF":
		return 0, nil, ErrExpectedCRLF
	case "JOB_TOO_BIG":
		return 0, nil, ErrJobTooBig
	case "OUT_OF_MEMORY":
		return 0, nil, ErrOutOfMemory
	}

	return 0, nil, ErrUnexpectedResp
}

// requestResponse sends a request to the beanstalk server and then parses
// and returns its response.
func (client *Client) requestResponse(format string, args ...interface{}) (uint64, []byte, error) {
	if err := client.request(format, args...); err != nil {
		return 0, nil, err
	}

	if client.options.ReadWriteTimeout != 0 {
		client.conn.SetReadDeadline(time.Now().Add(client.options.ReadWriteTimeout))
		defer client.conn.SetReadDeadline(time.Time{})
	}

	return client.response()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/xyproto/browserspeak"
	"github.com/xyproto/genericsite"
	"github.com/xyproto/siteengines"
	"github.com/xyproto/web"
)

// TODO: Norwegian everywhere
// TODO: Different Redis database than the other sites

const JQUERY_VERSION = "2.0.0"

func notFound2(ctx *web.Context, val string) {
	ctx.ResponseWriter.WriteHeader(404)
	ctx.ResponseWriter.Write([]byte(browserspeak.NotFound(ctx, val)))
}

func ServeEngines(userState *genericsite.UserState, mainMenuEntries genericsite.MenuEntries) {
	// The user engine
	userEngine := siteengines.NewUserEngine(userState)
	userEngine.ServePages()

	// The admin engine
	adminEngine := siteengines.NewAdminEngine(userState)
	adminEngine.ServePages(FTLSBaseCP, mainMenuEntries)

	// Wiki engine
	wikiEngine := siteengines.NewWikiEngine(userState)
	wikiEngine.ServePages(FTLSBaseCP, mainMenuEntries)
}

// TODO: Separate database for each site
func main() {

	// UserState with a Redis Connection Pool
	userState := genericsite.NewUserState()
	defer userState.Close()

	// The archlinux.no webpage,
	mainMenuEntries := ServeFTLS(userState, "/js/jquery-"+JQUERY_VERSION+".min.js")

	ServeEngines(userState, mainMenuEntries)

	// Compilation errors, vim-compatible filename
	web.Get("/error", browserspeak.GenerateErrorHandle("errors.err"))
	web.Get("/errors", browserspeak.GenerateErrorHandle("errors.err"))

	// Various .php and .asp urls that showed up in the log
	genericsite.ServeForFun()

	// TODO: Incorporate this check into web.go, to only return
	// stuff in the header when the HEAD method is requested:
	// if ctx.Request.Method == "HEAD" { return }
	// See also: curl -I

	// Serve on port 3002 for the Nginx instance to use
	web.Run("0.0.0.0:3002")
}
<commit_msg>Minor changes<commit_after>package main

import (
	"github.com/xyproto/browserspeak"
	"github.com/xyproto/genericsite"
	"github.com/xyproto/siteengines"
	"github.com/xyproto/web"
)

// TODO: Norwegian everywhere
// TODO: Different Redis database than the other sites

const JQUERY_VERSION = "2.0.0"

func notFound2(ctx *web.Context, val string) {
	ctx.ResponseWriter.WriteHeader(404)
	ctx.ResponseWriter.Write([]byte(browserspeak.NotFound(ctx, val)))
}

func ServeEngines(userState *genericsite.UserState, mainMenuEntries genericsite.MenuEntries) {
	// The user engine
	userEngine := siteengines.NewUserEngine(userState)
	userEngine.ServePages("ftls2.roboticoverlords.org")

	// The admin engine
	adminEngine := siteengines.NewAdminEngine(userState)
	adminEngine.ServePages(FTLSBaseCP, mainMenuEntries)

	// Wiki engine
	wikiEngine := siteengines.NewWikiEngine(userState)
	wikiEngine.ServePages(FTLSBaseCP, mainMenuEntries)
}

// TODO: Separate database for each site
func main() {

	// UserState with a Redis Connection Pool
	userState := genericsite.NewUserState()
	defer userState.Close()

	// The archlinux.no webpage,
	mainMenuEntries := ServeFTLS(userState, "/js/jquery-"+JQUERY_VERSION+".min.js")

	ServeEngines(userState, mainMenuEntries)

	// Compilation errors, vim-compatible filename
	web.Get("/error", browserspeak.GenerateErrorHandle("errors.err"))
	web.Get("/errors", browserspeak.GenerateErrorHandle("errors.err"))

	// Various .php and .asp urls that showed up in the log
	genericsite.ServeForFun()

	// TODO: Incorporate this check into web.go, to only return
	// stuff in the header when the HEAD method is requested:
	// if ctx.Request.Method == "HEAD" { return }
	// See also: curl -I

	// Serve on port 3002 for the Nginx instance to use
	web.Run("0.0.0.0:3002")
}
<|endoftext|>


<commit_before>package prompter

import (
	"regexp"
	"strings"
)

// Prompt simple prompting
func Prompt(message, defaultAnswer string) string {
	return (&Prompter{
		Message: message,
		Default: defaultAnswer,
	}).Prompt()
}

// YN y/n choice
func YN(message string, yes bool) bool {
	defaultChoice := "n"
	if yes {
		defaultChoice = "y"
	}
	input := (&Prompter{
		Message:    message,
		Choices:    []string{"y", "n"},
		IgnoreCase: true,
		Default:    defaultChoice,
	}).Prompt()

	return strings.ToLower(input) == "y"
}

// YesNo yes/no choice
func YesNo(message string, yes bool) bool {
	defaultChoice := "no"
	if yes {
		defaultChoice = "yes"
	}
	input := (&Prompter{
		Message:    message,
		Choices:    []string{"yes", "no"},
		IgnoreCase: true,
		Default:    defaultChoice,
	}).Prompt()

	return strings.ToLower(input) == "yes"
}

// Password asks password
func Password(message string) string {
	return (&Prompter{
		Message: message,
		NoEcho:  true,
	}).Prompt()
}

// Choose make a choice
func Choose(message string, choices []string, defaultChoice string) string {
	return (&Prompter{
		Message: message,
		Choices: choices,
		Default: defaultChoice,
	}).Prompt()
}

// Regexp checks the answer by regexp
func Regexp(message string, reg *regexp.Regexp, defaultAnswer string) string {
	return (&Prompter{
		Message: message,
		Regexp:  reg,
		Default: defaultAnswer,
	}).Prompt()
}
<commit_msg>Make yes no prompts clearer<commit_after>package prompter

import (
	"regexp"
	"strings"
)

// Prompt simple prompting
func Prompt(message, defaultAnswer string) string {
	return (&Prompter{
		Message: message,
		Default: defaultAnswer,
	}).Prompt()
}

// YN y/n choice
func YN(message string, defaultToYes bool) bool {
	defaultChoice := "n"
	if defaultToYes {
		defaultChoice = "y"
	}
	input := (&Prompter{
		Message:    message,
		Choices:    []string{"y", "n"},
		IgnoreCase: true,
		Default:    defaultChoice,
	}).Prompt()

	return strings.ToLower(input) == "y"
}

// YesNo yes/no choice
func YesNo(message string, defaultToYes bool) bool {
	defaultChoice := "no"
	if defaultToYes {
		defaultChoice = "yes"
	}
	input := (&Prompter{
		Message:    message,
		Choices:    []string{"yes", "no"},
		IgnoreCase: true,
		Default:    defaultChoice,
	}).Prompt()

	return strings.ToLower(input) == "yes"
}

// Password asks password
func Password(message string) string {
	return (&Prompter{
		Message: message,
		NoEcho:  true,
	}).Prompt()
}

// Choose make a choice
func Choose(message string, choices []string, defaultChoice string) string {
	return (&Prompter{
		Message: message,
		Choices: choices,
		Default: defaultChoice,
	}).Prompt()
}

// Regexp checks the answer by regexp
func Regexp(message string, reg *regexp.Regexp, defaultAnswer string) string {
	return (&Prompter{
		Message: message,
		Regexp:  reg,
		Default: defaultAnswer,
	}).Prompt()
}
<|endoftext|>