<commit_before>/**
 * Generates a date in YYYY-MM-DD format based on a relative time
 * description (e.g. -1 week, +3 years)
 *
 * @author R. S. Doiel, <rsdoiel@gmail.com>
 * copyright (c) 2014 all rights reserved.
 * Released under the Simplified BSD License
 * See: http://opensource.org/licenses/bsd-license.php
 */
package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	help       bool
	relativeTo string
	relativeT  time.Time
)

var usage = func(exit_code int, msg string) {
	var fh = os.Stderr
	if exit_code == 0 {
		fh = os.Stdout
	}
	fmt.Fprintf(fh, `%s
 USAGE %s [TIME_INCREMENT TIME_UNIT|WEEKDAY_NAME]

 EXAMPLES

 Two days from today: %s 2 days
 Three weeks ago: %s -- -3 weeks
 Three weeks from 2014-01-01: %s --from=2014-01-01 3 weeks
 Three days before 2014-01-01: %s --from=2014-01-01 -- -3 days
 The Friday of this week: %s Friday
 The Monday in week containing 2015-02-06: %s --from=2015-02-06 Monday

 Time increments are a positive or negative integer. Time unit can be
 either day(s), week(s), month(s), or year(s). Weekday names and are
 case insentive (e.g. Monday and monday). They can be abbreviated
 to the first three letters of the name, e.g. Sunday can be Sun, Monday
 can be Mon, Tuesday can be Tue, Wednesday can be Wed, Thursday can
 be Thu, Friday can be Fri or Saturday can be Sat.

 OPTIONS

`, msg, os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0])

	flag.VisitAll(func(f *flag.Flag) {
		fmt.Fprintf(fh, "\t-%s\t(defaults to %s) %s\n", f.Name, f.Value, f.Usage)
	})

	fmt.Fprintf(fh, `

 copyright (c) 2014 all rights reserved.
 Released under the Simplified BSD License
 See: http://opensource.org/licenses/bsd-license.php

`)
	os.Exit(exit_code)
}

func init() {
	const (
		relativeToUsage = "Date the relative time is calculated from."
		helpUsage       = "Display this help document."
	)

	flag.StringVar(&relativeTo, "from", relativeTo, relativeToUsage)
	flag.StringVar(&relativeTo, "f", relativeTo, relativeToUsage)
	flag.BoolVar(&help, "help", help, helpUsage)
	flag.BoolVar(&help, "h", help, helpUsage)
}

func assertOk(e error, failMsg string) {
	if e != nil {
		usage(1, fmt.Sprintf(" %s\n %s\n", failMsg, e))
	}
}

func weekdayOffset(weekday time.Weekday) int {
	switch {
	case weekday == time.Sunday:
		return 0
	case weekday == time.Monday:
		return 1
	case weekday == time.Tuesday:
		return 2
	case weekday == time.Wednesday:
		return 3
	case weekday == time.Thursday:
		return 4
	case weekday == time.Friday:
		return 5
	case weekday == time.Saturday:
		return 6
	}
	return 0
}

func relativeWeekday(t time.Time, weekday time.Weekday) (time.Time, error) {
	// Normalize to Sunday then add weekday constant
	switch {
	case t.Weekday() == time.Sunday:
		return t.AddDate(0, 0, weekdayOffset(weekday)), nil
	case t.Weekday() == time.Monday:
		return t.AddDate(0, 0, (-1 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Tuesday:
		return t.AddDate(0, 0, (-2 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Wednesday:
		return t.AddDate(0, 0, (-3 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Thursday:
		return t.AddDate(0, 0, (-4 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Friday:
		return t.AddDate(0, 0, (-5 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Saturday:
		return t.AddDate(0, 0, (-6 + weekdayOffset(weekday))), nil
	}
	return t, errors.New("Expecting Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, or Saturday.")
}

func relativeTime(t time.Time, i int, u string) (time.Time, error) {
	switch {
	case strings.HasPrefix(u, "sun"):
		return relativeWeekday(t, time.Sunday)
	case strings.HasPrefix(u, "mon"):
		return relativeWeekday(t, time.Monday)
	case strings.HasPrefix(u, "tue"):
		return relativeWeekday(t, time.Tuesday)
	case strings.HasPrefix(u, "wed"):
		return relativeWeekday(t, time.Wednesday)
	case strings.HasPrefix(u, "thu"):
		return relativeWeekday(t, time.Thursday)
	case strings.HasPrefix(u, "fri"):
		return relativeWeekday(t, time.Friday)
	case strings.HasPrefix(u, "sat"):
		return relativeWeekday(t, time.Saturday)
	case strings.HasPrefix(u, "day"):
		return t.AddDate(0, 0, i), nil
	case strings.HasPrefix(u, "week"):
		return t.AddDate(0, 0, 7*i), nil
	case strings.HasPrefix(u, "month"):
		return t.AddDate(0, i, 0), nil
	case strings.HasPrefix(u, "year"):
		return t.AddDate(i, 0, 0), nil
	}
	return t, errors.New("Time unit must be day(s), week(s), month(s) or year(s) or weekday name.")
}

func main() {
	const yyyymmdd = "2006-01-02"
	var (
		err        error
		unitString string
	)

	flag.Parse()
	if help == true {
		usage(0, "")
	}

	argc := flag.NArg()
	argv := flag.Args()

	if argc < 1 {
		usage(1, "Missing time increment and units (e.g. +2 days) or weekday name (e.g. Monday, Mon).\n")
	} else if argc > 2 {
		usage(1, "Too many command line arguments.\n")
	}

	relativeT = time.Now()
	if relativeTo != "" {
		relativeT, err = time.Parse(yyyymmdd, relativeTo)
		assertOk(err, "Cannot parse the from date.\n")
	}

	timeInc := 0
	if argc == 2 {
		unitString = strings.ToLower(argv[0])
		timeInc, err = strconv.Atoi(argv[0])
		assertOk(err, "Time increment should be a positive or negative integer.\n")
	} else {
		// We may have a weekday string
		unitString = strings.ToLower(argv[0])
	}
	t, err := relativeTime(relativeT, timeInc, unitString)
	assertOk(err, err.Error())
	fmt.Println(t.Format(yyyymmdd))
}
<commit_msg>fixed error handling for weekdays<commit_after>/**
 * Generates a date in YYYY-MM-DD format based on a relative time
 * description (e.g. -1 week, +3 years)
 *
 * @author R. S. Doiel, <rsdoiel@gmail.com>
 * copyright (c) 2014 all rights reserved.
 * Released under the Simplified BSD License
 * See: http://opensource.org/licenses/bsd-license.php
 */
package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	help       bool
	relativeTo string
	relativeT  time.Time
)

var usage = func(exit_code int, msg string) {
	var fh = os.Stderr
	if exit_code == 0 {
		fh = os.Stdout
	}
	fmt.Fprintf(fh, `%s
 USAGE %s [TIME_INCREMENT TIME_UNIT|WEEKDAY_NAME]

 EXAMPLES

 Two days from today: %s 2 days
 Three weeks ago: %s -- -3 weeks
 Three weeks from 2014-01-01: %s --from=2014-01-01 3 weeks
 Three days before 2014-01-01: %s --from=2014-01-01 -- -3 days
 The Friday of this week: %s Friday
 The Monday in week containing 2015-02-06: %s --from=2015-02-06 Monday

 Time increments are a positive or negative integer. Time unit can be
 either day(s), week(s), month(s), or year(s). Weekday names and are
 case insentive (e.g. Monday and monday). They can be abbreviated
 to the first three letters of the name, e.g. Sunday can be Sun, Monday
 can be Mon, Tuesday can be Tue, Wednesday can be Wed, Thursday can
 be Thu, Friday can be Fri or Saturday can be Sat.

 OPTIONS

`, msg, os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0], os.Args[0])

	flag.VisitAll(func(f *flag.Flag) {
		fmt.Fprintf(fh, "\t-%s\t(defaults to %s) %s\n", f.Name, f.Value, f.Usage)
	})

	fmt.Fprintf(fh, `

 copyright (c) 2014 all rights reserved.
 Released under the Simplified BSD License
 See: http://opensource.org/licenses/bsd-license.php

`)
	os.Exit(exit_code)
}

func init() {
	const (
		relativeToUsage = "Date the relative time is calculated from."
		helpUsage       = "Display this help document."
	)

	flag.StringVar(&relativeTo, "from", relativeTo, relativeToUsage)
	flag.StringVar(&relativeTo, "f", relativeTo, relativeToUsage)
	flag.BoolVar(&help, "help", help, helpUsage)
	flag.BoolVar(&help, "h", help, helpUsage)
}

func assertOk(e error, failMsg string) {
	if e != nil {
		usage(1, fmt.Sprintf(" %s\n %s\n", failMsg, e))
	}
}

func weekdayOffset(weekday time.Weekday) int {
	switch {
	case weekday == time.Sunday:
		return 0
	case weekday == time.Monday:
		return 1
	case weekday == time.Tuesday:
		return 2
	case weekday == time.Wednesday:
		return 3
	case weekday == time.Thursday:
		return 4
	case weekday == time.Friday:
		return 5
	case weekday == time.Saturday:
		return 6
	}
	return 0
}

func relativeWeekday(t time.Time, weekday time.Weekday) (time.Time, error) {
	// Normalize to Sunday then add weekday constant
	switch {
	case t.Weekday() == time.Sunday:
		return t.AddDate(0, 0, weekdayOffset(weekday)), nil
	case t.Weekday() == time.Monday:
		return t.AddDate(0, 0, (-1 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Tuesday:
		return t.AddDate(0, 0, (-2 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Wednesday:
		return t.AddDate(0, 0, (-3 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Thursday:
		return t.AddDate(0, 0, (-4 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Friday:
		return t.AddDate(0, 0, (-5 + weekdayOffset(weekday))), nil
	case t.Weekday() == time.Saturday:
		return t.AddDate(0, 0, (-6 + weekdayOffset(weekday))), nil
	}
	return t, errors.New("Expecting Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, or Saturday.")
}

func relativeTime(t time.Time, i int, u string) (time.Time, error) {
	switch {
	case strings.HasPrefix(u, "sun"):
		return relativeWeekday(t, time.Sunday)
	case strings.HasPrefix(u, "mon"):
		return relativeWeekday(t, time.Monday)
	case strings.HasPrefix(u, "tue"):
		return relativeWeekday(t, time.Tuesday)
	case strings.HasPrefix(u, "wed"):
		return relativeWeekday(t, time.Wednesday)
	case strings.HasPrefix(u, "thu"):
		return relativeWeekday(t, time.Thursday)
	case strings.HasPrefix(u, "fri"):
		return relativeWeekday(t, time.Friday)
	case strings.HasPrefix(u, "sat"):
		return relativeWeekday(t, time.Saturday)
	case strings.HasPrefix(u, "day"):
		return t.AddDate(0, 0, i), nil
	case strings.HasPrefix(u, "week"):
		return t.AddDate(0, 0, 7*i), nil
	case strings.HasPrefix(u, "month"):
		return t.AddDate(0, i, 0), nil
	case strings.HasPrefix(u, "year"):
		return t.AddDate(i, 0, 0), nil
	}
	return t, errors.New("Time unit must be day(s), week(s), month(s) or year(s) or weekday name.")
}

func main() {
	const yyyymmdd = "2006-01-02"
	var (
		err        error
		unitString string
	)

	flag.Parse()
	if help == true {
		usage(0, "")
	}

	argc := flag.NArg()
	argv := flag.Args()

	if argc < 1 {
		usage(1, "Missing time increment and units (e.g. +2 days) or weekday name (e.g. Monday, Mon).\n")
	} else if argc > 2 {
		usage(1, "Too many command line arguments.\n")
	}

	relativeT = time.Now()
	if relativeTo != "" {
		relativeT, err = time.Parse(yyyymmdd, relativeTo)
		assertOk(err, "Cannot parse the from date.\n")
	}

	timeInc := 0
	if argc == 2 {
		unitString = strings.ToLower(argv[0])
		timeInc, err = strconv.Atoi(argv[0])
		assertOk(err, "Time increment should be a positive or negative integer.\n")
	} else {
		// We may have a weekday string
		unitString = strings.ToLower(argv[0])
	}
	t, err := relativeTime(relativeT, timeInc, unitString)
	assertOk(err, "Did not understand command.")
	fmt.Println(t.Format(yyyymmdd))
}
<|endoftext|>


<commit_before>// Package rocket implements a connector for Rocket.Chat
package rocket

import (
	"sync"

	"github.com/lnxjedi/gopherbot/bot"
	models "github.com/lnxjedi/gopherbot/connectors/rocket/models"
	api "github.com/lnxjedi/gopherbot/connectors/rocket/realtime"
)

type config struct {
	Server   string // Rocket.Chat server to connect to
	Email    string // Rocket.Chat user email
	Password string // the initial userid
}

type rocketConnector struct {
	rt      *api.Client
	user    *models.User
	running bool
	bot.Handler
	sync.Mutex
	joinChannels map[string]struct{}
	subChannels  map[string]struct{}
}

var incoming chan models.Message = make(chan models.Message, 100)

func (rc *rocketConnector) Run(stop <-chan struct{}) {
	rc.Lock()
	// This should never happen, just a bit of defensive coding
	if rc.running {
		rc.Unlock()
		return
	}
	rc.running = true
	rc.Unlock()
	rc.subscribeChannels()
loop:
	for {
		select {
		case pmsg := <-incoming:
			rc.processMessage(&pmsg)

		case <-stop:
			rc.Log(bot.Debug, "Received stop in connector")
			break loop
		}
	}
}

// processMessage creates a bot.ConnectorMessage and calls
// bot.IncomingMessage
func (rc *rocketConnector) processMessage(msg *models.Message) {
	botMsg := &bot.ConnectorMessage{
		Protocol:      "Rocket",
		UserID:        msg.User.ID,
		ChannelID:     msg.RoomID,
		MessageText:   msg.Text,
		MessageObject: msg,
		Client:        rc.rt,
	}
	rc.IncomingMessage(botMsg)
}

func (rc *rocketConnector) subscribeChannels() {
	rc.Lock()
	defer rc.Unlock()
	for want := range rc.joinChannels {
		if _, ok := rc.subChannels[want]; !ok {
			rc.subChannels[want] = struct{}{}
			if rid, err := rc.rt.GetChannelId(want); err == nil {
				if err := rc.rt.JoinChannel(rid); err != nil {
					rc.Log(bot.Error, "joining channel %s/%s: %v", want, rid, err)
				} else {
					schan := &models.Channel{ID: rid}
					if err := rc.rt.SubscribeToMessageStream(schan, incoming); err != nil {
						rc.Log(bot.Error, "subscribing to %s/%s: %v", want, rid, err)
					}
				}
			} else {
				rc.Log(bot.Error, "getting channel ID for %s: %v", want, err)
			}
		}
	}
	return
}

func (rc *rocketConnector) sendMessage(ch, msg string, f bot.MessageFormat) (ret bot.RetVal) {
	return bot.Ok
}
<commit_msg>Debugging, get msg text<commit_after>// Package rocket implements a connector for Rocket.Chat
package rocket

import (
	"sync"

	"github.com/lnxjedi/gopherbot/bot"
	models "github.com/lnxjedi/gopherbot/connectors/rocket/models"
	api "github.com/lnxjedi/gopherbot/connectors/rocket/realtime"
)

type config struct {
	Server   string // Rocket.Chat server to connect to
	Email    string // Rocket.Chat user email
	Password string // the initial userid
}

type rocketConnector struct {
	rt      *api.Client
	user    *models.User
	running bool
	bot.Handler
	sync.Mutex
	joinChannels map[string]struct{}
	subChannels  map[string]struct{}
}

var incoming chan models.Message = make(chan models.Message, 100)

func (rc *rocketConnector) Run(stop <-chan struct{}) {
	rc.Lock()
	// This should never happen, just a bit of defensive coding
	if rc.running {
		rc.Unlock()
		return
	}
	rc.running = true
	rc.Unlock()
	rc.subscribeChannels()
loop:
	for {
		select {
		case pmsg := <-incoming:
			rc.processMessage(&pmsg)

		case <-stop:
			rc.Log(bot.Debug, "Received stop in connector")
			break loop
		}
	}
}

// processMessage creates a bot.ConnectorMessage and calls
// bot.IncomingMessage
func (rc *rocketConnector) processMessage(msg *models.Message) {
	rc.Log(bot.Debug, "DEBUG: Raw incoming msg: %v", *msg)
	rc.Log(bot.Debug, "DEBUG: Raw incoming user: %v", *msg.User)
	botMsg := &bot.ConnectorMessage{
		Protocol:      "Rocket",
		UserID:        msg.User.ID,
		ChannelID:     msg.RoomID,
		MessageText:   msg.Msg,
		MessageObject: msg,
		Client:        rc.rt,
	}
	rc.IncomingMessage(botMsg)
}

func (rc *rocketConnector) subscribeChannels() {
	rc.Lock()
	defer rc.Unlock()
	for want := range rc.joinChannels {
		if _, ok := rc.subChannels[want]; !ok {
			rc.subChannels[want] = struct{}{}
			if rid, err := rc.rt.GetChannelId(want); err == nil {
				if err := rc.rt.JoinChannel(rid); err != nil {
					rc.Log(bot.Error, "joining channel %s/%s: %v", want, rid, err)
				} else {
					schan := &models.Channel{ID: rid}
					if err := rc.rt.SubscribeToMessageStream(schan, incoming); err != nil {
						rc.Log(bot.Error, "subscribing to %s/%s: %v", want, rid, err)
					}
				}
			} else {
				rc.Log(bot.Error, "getting channel ID for %s: %v", want, err)
			}
		}
	}
	return
}

func (rc *rocketConnector) sendMessage(ch, msg string, f bot.MessageFormat) (ret bot.RetVal) {
	return bot.Ok
}
<|endoftext|>


<commit_before>package arraymap

import (
	"github.com/mkideal/pkg/container/boolslice"
)

type T interface{}

type ArrayMap struct {
	data       []T
	validFlags *boolslice.BoolSlice // validFlags.Len() = len(data)
	holes      []int                // len(holes) <= len(data)
}

func New() *ArrayMap {
	return &ArrayMap{
		data:       []T{},
		validFlags: boolslice.New(),
		holes:      []int{},
	}
}

func NewWithCap(reservedSize int) *ArrayMap {
	return &ArrayMap{
		data:       make([]T, 0, reservedSize),
		validFlags: boolslice.NewWithCap(reservedSize),
		holes:      []int{},
	}
}

func (m *ArrayMap) isValidKey(key int) bool {
	return key < len(m.data) && m.validFlags.Get(key)
}

func (m *ArrayMap) Len() int { return len(m.data) - len(m.holes) }
func (m *ArrayMap) Cap() int { return cap(m.data) }

// Get gets the value by key
func (m *ArrayMap) Get(key int) (value T, ok bool) {
	if m.isValidKey(key) {
		return m.data[key], true
	}
	return nil, false
}

// Update updates <key,value>
// Returns true if key is valid
func (m *ArrayMap) Update(key int, value T) bool {
	if m.isValidKey(key) {
		m.data[key] = value
		return true
	}
	return false
}

// Add adds a new element and returns the allocated key
func (m *ArrayMap) Add(value T) (key int) {
	if n := len(m.holes); n > 0 {
		key = m.holes[n-1]
		m.holes = m.holes[:n-1]
		m.data[key] = value
		m.validFlags.Set(key, true)
		return
	}
	key = len(m.data)
	m.data = append(m.data, value)
	m.validFlags.Push(true)
	return
}

// Remove removes the value by key
func (m *ArrayMap) Remove(key int) (value T, ok bool) {
	if m.isValidKey(key) {
		value = m.data[key]
		ok = true
		m.validFlags.Set(key, false)
		m.holes = append(m.holes, key)
	}
	return
}

// For traversal the map
func (m *ArrayMap) For(visitor func(key int, value T) (broken bool)) {
	for key, value := range m.data {
		if m.validFlags.Get(key) {
			if visitor(key, value) {
				break
			}
		}
	}
}
<commit_msg>fix arraymap NewWithCap<commit_after>package arraymap

import (
	"github.com/mkideal/pkg/container/boolslice"
)

type T interface{}

type ArrayMap struct {
	data       []T
	validFlags *boolslice.BoolSlice // validFlags.Len() = len(data)
	holes      []int                // len(holes) <= len(data)
}

func New() *ArrayMap {
	return &ArrayMap{
		data:       []T{},
		validFlags: boolslice.New(),
		holes:      []int{},
	}
}

func NewWithSize(size, cap int) *ArrayMap {
	return &ArrayMap{
		data:       make([]T, size, cap),
		validFlags: boolslice.NewWithSize(size, cap),
		holes:      []int{},
	}
}

func (m *ArrayMap) isValidKey(key int) bool {
	return key < len(m.data) && m.validFlags.Get(key)
}

func (m *ArrayMap) Len() int { return len(m.data) - len(m.holes) }
func (m *ArrayMap) Cap() int { return cap(m.data) }

// Get gets the value by key
func (m *ArrayMap) Get(key int) (value T, ok bool) {
	if m.isValidKey(key) {
		return m.data[key], true
	}
	return nil, false
}

// Update updates <key,value>
// Returns true if key is valid
func (m *ArrayMap) Update(key int, value T) bool {
	if m.isValidKey(key) {
		m.data[key] = value
		return true
	}
	return false
}

// Add adds a new element and returns the allocated key
func (m *ArrayMap) Add(value T) (key int) {
	if n := len(m.holes); n > 0 {
		key = m.holes[n-1]
		m.holes = m.holes[:n-1]
		m.data[key] = value
		m.validFlags.Set(key, true)
		return
	}
	key = len(m.data)
	m.data = append(m.data, value)
	m.validFlags.Push(true)
	return
}

// Remove removes the value by key
func (m *ArrayMap) Remove(key int) (value T, ok bool) {
	if m.isValidKey(key) {
		value = m.data[key]
		ok = true
		m.validFlags.Set(key, false)
		m.holes = append(m.holes, key)
	}
	return
}

// For traversal the map
func (m *ArrayMap) For(visitor func(key int, value T) (broken bool)) {
	for key, value := range m.data {
		if m.validFlags.Get(key) {
			if visitor(key, value) {
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package gojenkins

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// Request Methods

type Requester struct {
	Base         string
	BasicAuth    *BasicAuth
	Headers      http.Header
	Client       *http.Client
	SslVerify    bool
	LastResponse *http.Response
	Suffix       string
}

func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	r.Suffix = "api/json"
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	r.Suffix = ""
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) {
	return r.Do("POST", endpoint, payload, &responseStruct, querystring, files)
}

func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	payload := bytes.NewBuffer([]byte(xml))
	r.SetHeader("Content-Type", "application/xml")
	r.Suffix = ""
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/json")
	r.Suffix = "api/json"
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) GetXML(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/xml")
	r.Suffix = ""
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.Suffix = ""
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) SetHeader(key string, value string) *Requester {
	r.Headers.Set(key, value)
	return r
}

func (r *Requester) SetClient(client *http.Client) *Requester {
	r.Client = client
	return r
}

func (r *Requester) parseQueryString(queries map[string]string) string {
	output := ""
	delimiter := "?"
	for k, v := range queries {
		output += delimiter + k + "=" + v
		delimiter = "&"
	}
	return output
}

//Add auth on redirect if required.
func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error{
	if r.BasicAuth != nil {
		req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
	}
	return nil
}

func (r *Requester) Do(method string, endpoint string, payload io.Reader, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
	if !strings.HasSuffix(endpoint, "/") && method != "POST" {
		endpoint += "/"
	}

	fileUpload := false
	var files []string
	URL, err := url.Parse(r.Base + endpoint + r.Suffix)

	if err != nil {
		return nil, err
	}

	for _, o := range options {
		switch v := o.(type) {
		case map[string]string:

			querystring := make(url.Values)
			for key, val := range v {
				querystring.Set(key, val)
			}

			URL.RawQuery = querystring.Encode()
			break
		case []string:
			fileUpload = true
			files = v
		}
	}
	var req *http.Request

	if fileUpload {
		body := &bytes.Buffer{}
		writer := multipart.NewWriter(body)
		for _, file := range files {
			fileData, err := os.Open(file)
			if err != nil {
				Error.Println(err.Error())
				return nil, err
			}

			part, err := writer.CreateFormFile("file", filepath.Base(file))
			if err != nil {
				Error.Println(err.Error())
				return nil, err
			}
			if _, err = io.Copy(part, fileData); err != nil {
				return nil, err
			}
			defer fileData.Close()
		}
		var params map[string]string
		json.NewDecoder(payload).Decode(&params)
		for key, val := range params {
			if err = writer.WriteField(key, val); err != nil {
				return nil, err
			}
		}
		if err = writer.Close(); err != nil {
			return nil, err
		}
		req, err = http.NewRequest(method, URL.String(), body)
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", writer.FormDataContentType())
	} else {

		req, err = http.NewRequest(method, URL.String(), payload)
		if err != nil {
			return nil, err
		}
	}

	if r.BasicAuth != nil {
		req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
	}

	if r.Headers != nil {
		for k := range r.Headers {
			req.Header.Add(k, r.Headers.Get(k))
		}
	}

	r.LastResponse, err = r.Client.Do(req)

	if err != nil {
		return nil, err
	}

	errorText := r.LastResponse.Header.Get("X-Error")

	if errorText != "" {
		return nil, errors.New(errorText)
	}

	switch responseStruct.(type) {
	case *string:
		rawResponse, err := r.ReadRawResponse(responseStruct)
		if err != nil {
			return nil, err
		}
		return rawResponse, nil
	default:
		jsonResponse := r.ReadJSONResponse(responseStruct)
		return jsonResponse, nil
	}
}

func (r *Requester) ReadRawResponse(responseStruct interface{}) (*http.Response, error) {
	defer r.LastResponse.Body.Close()

	content, err := ioutil.ReadAll(r.LastResponse.Body)
	if str, ok := responseStruct.(*string); ok {
		*str = string(content)
	}

	if err != nil {
		return nil, err
	}

	return r.LastResponse, nil
}

func (r *Requester) ReadJSONResponse(responseStruct interface{}) *http.Response {
	defer r.LastResponse.Body.Close()
	json.NewDecoder(r.LastResponse.Body).Decode(responseStruct)
	return r.LastResponse
}
<commit_msg>Add support for CSRF protection.<commit_after>// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package gojenkins

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// Request Methods

type Requester struct {
	Base         string
	BasicAuth    *BasicAuth
	Headers      http.Header
	Client       *http.Client
	SslVerify    bool
	LastResponse *http.Response
	Suffix       string
}

func (r *Requester) SetCrumb() error {
	crumbData := map[string]string{}
	response, err := r.GetJSON("/crumbIssuer/api/json", &crumbData, nil)
	if err != nil {
		return err
	}

	if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" {
		r.SetHeader(crumbData["crumbRequestField"], crumbData["crumb"])
	}

	return nil
}

func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetCrumb()
	r.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	r.Suffix = "api/json"
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetCrumb()
	r.SetHeader("Content-Type", "application/x-www-form-urlencoded")
	r.Suffix = ""
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) {
	r.SetCrumb()
	return r.Do("POST", endpoint, payload, &responseStruct, querystring, files)
}

func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetCrumb()
	payload := bytes.NewBuffer([]byte(xml))
	r.SetHeader("Content-Type", "application/xml")
	r.Suffix = ""
	return r.Do("POST", endpoint, payload, &responseStruct, querystring)
}

func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/json")
	r.Suffix = "api/json"
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) GetXML(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.SetHeader("Content-Type", "application/xml")
	r.Suffix = ""
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
	r.Suffix = ""
	return r.Do("GET", endpoint, nil, responseStruct, querystring)
}

func (r *Requester) SetHeader(key string, value string) *Requester {
	r.Headers.Set(key, value)
	return r
}

func (r *Requester) SetClient(client *http.Client) *Requester {
	r.Client = client
	return r
}

func (r *Requester) parseQueryString(queries map[string]string) string {
	output := ""
	delimiter := "?"
	for k, v := range queries {
		output += delimiter + k + "=" + v
		delimiter = "&"
	}
	return output
}

//Add auth on redirect if required.
func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
	if r.BasicAuth != nil {
		req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
	}
	return nil
}

func (r *Requester) Do(method string, endpoint string, payload io.Reader, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
	if !strings.HasSuffix(endpoint, "/") && method != "POST" {
		endpoint += "/"
	}

	fileUpload := false
	var files []string
	URL, err := url.Parse(r.Base + endpoint + r.Suffix)

	if err != nil {
		return nil, err
	}

	for _, o := range options {
		switch v := o.(type) {
		case map[string]string:

			querystring := make(url.Values)
			for key, val := range v {
				querystring.Set(key, val)
			}

			URL.RawQuery = querystring.Encode()
			break
		case []string:
			fileUpload = true
			files = v
		}
	}
	var req *http.Request

	if fileUpload {
		body := &bytes.Buffer{}
		writer := multipart.NewWriter(body)
		for _, file := range files {
			fileData, err := os.Open(file)
			if err != nil {
				Error.Println(err.Error())
				return nil, err
			}

			part, err := writer.CreateFormFile("file", filepath.Base(file))
			if err != nil {
				Error.Println(err.Error())
				return nil, err
			}
			if _, err = io.Copy(part, fileData); err != nil {
				return nil, err
			}
			defer fileData.Close()
		}
		var params map[string]string
		json.NewDecoder(payload).Decode(&params)
		for key, val := range params {
			if err = writer.WriteField(key, val); err != nil {
				return nil, err
			}
		}
		if err = writer.Close(); err != nil {
			return nil, err
		}
		req, err = http.NewRequest(method, URL.String(), body)
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", writer.FormDataContentType())
	} else {

		req, err = http.NewRequest(method, URL.String(), payload)
		if err != nil {
			return nil, err
		}
	}

	if r.BasicAuth != nil {
		req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
	}

	if r.Headers != nil {
		for k := range r.Headers {
			req.Header.Add(k, r.Headers.Get(k))
		}
	}

	r.LastResponse, err = r.Client.Do(req)

	if err != nil {
		return nil, err
	}

	errorText := r.LastResponse.Header.Get("X-Error")

	if errorText != "" {
		return nil, errors.New(errorText)
	}

	switch responseStruct.(type) {
	case *string:
		rawResponse, err := r.ReadRawResponse(responseStruct)
		if err != nil {
			return nil, err
		}
		return rawResponse, nil
	default:
		jsonResponse := r.ReadJSONResponse(responseStruct)
		return jsonResponse, nil
	}
}

func (r *Requester) ReadRawResponse(responseStruct interface{}) (*http.Response, error) {
	defer r.LastResponse.Body.Close()

	content, err := ioutil.ReadAll(r.LastResponse.Body)
	if str, ok := responseStruct.(*string); ok {
		*str = string(content)
	}

	if err != nil {
		return nil, err
	}

	return r.LastResponse, nil
}

func (r *Requester) ReadJSONResponse(responseStruct interface{}) *http.Response {
	defer r.LastResponse.Body.Close()
	json.NewDecoder(r.LastResponse.Body).Decode(responseStruct)
	return r.LastResponse
}
<|endoftext|>


<commit_before>package console_test

import (
	"bytes"
	"errors"
	"io"
	"strings"
	"testing"

	"fmt"
	"math/rand"

	"github.com/eidolon/console"
	"github.com/eidolon/console/assert"
	"github.com/eidolon/console/parameters"
)

func TestNewApplication(t *testing.T) {
	application := console.NewApplication("eidolon/console", "1.2.3+testing")
	assert.True(t, application != nil, "Application should not be nil")
}

func TestApplication(t *testing.T) {
	createApplication := func(writer io.Writer) *console.Application {
		application := console.NewApplication("eidolon/console", "1.2.3.+testing")
		application.Writer = writer

		return application
	}

	createTestCommand := func(a *string, b *int) *console.Command {
		return &console.Command{
			Name: "test",
			Configure: func(definition *console.Definition) {
				definition.AddArgument(console.ArgumentDefinition{
					Value: parameters.NewStringValue(a),
					Spec:  "STRINGARG",
				})

				definition.AddOption(console.OptionDefinition{
					Value: parameters.NewIntValue(b),
					Spec:  "--int-opt=VALUE",
				})
			},
			Execute: func(input *console.Input, output *console.Output) error {
				output.Printf("STRINGARG = %s", *a)
				output.Printf("--int-opt = %v", *b)
				return nil
			},
		}
	}

	t.Run("Run()", func(t *testing.T) {
		t.Run("should return exit code 2 if no command was asked for", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should return exit code 2 if no command was found", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{"foo"}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should return exit code 100 if the help flag is set", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{"--help"}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should show application help if the help flag is set", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.Run([]string{"--help"}, []string{})

			output := writer.String()
			containsUsage := strings.Contains(output, "USAGE:")
			containsArguments := strings.Contains(output, "ARGUMENTS:")
			containsOptions := strings.Contains(output, "OPTIONS:")
			containsHelp := containsUsage && containsOptions && !containsArguments

			assert.True(t, containsHelp, "Expected help output.")
		})

		t.Run("should show command help if the help flag is set when running a command", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))
			application.Run([]string{"test", "--help"}, []string{})

			output := writer.String()
			containsUsage := strings.Contains(output, "USAGE:")
			containsArguments := strings.Contains(output, "ARGUMENTS:")
			containsOptions := strings.Contains(output, "OPTIONS:")
			containsHelp := containsUsage && containsOptions && containsArguments

			assert.True(t, containsHelp, "Expected help output.")
		})

		t.Run("should return exit code 0 if a command was found, and ran OK", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))

			code := application.Run([]string{"test", "aval", "--int-opt=384"}, []string{})

			assert.Equal(t, 0, code)
		})

		t.Run("should return exit code 101 if mapping input fails", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))

			code := application.Run([]string{"test", "aval", "--int-opt=hello"}, []string{})

			assert.Equal(t, 101, code)
		})

		t.Run("should return exit code 102 if the command execution fails", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(&console.Command{
				Name: "test",
				Execute: func(input *console.Input, output *console.Output) error {
					return errors.New("Testing errors")
				},
			})

			code := application.Run([]string{"test", "aval", "--int-opt=hello"}, []string{})

			assert.Equal(t, 102, code)
		})

		t.Run("should configure the application definition", func(t *testing.T) {
			var a string
			var b int
			var foo string

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.Configure = func(definition *console.Definition) {
				definition.AddOption(console.OptionDefinition{
					Value: parameters.NewStringValue(&foo),
					Spec:  "--foo=FOO",
				})
			}

			application.AddCommand(createTestCommand(&a, &b))
			application.Run([]string{"test", "aval", "--foo=bar"}, []string{})

			assert.Equal(t, "bar", foo)
		})

		t.Run("should work with sub-commands", func(t *testing.T) {
			message := fmt.Sprintf("sub-command: %d", rand.Int())

			subCommand := console.Command{
				Name: "subCommand",
				Execute: func(input *console.Input, output *console.Output) error {
					output.Println(message)
					return nil
				},
			}

			command := console.Command{Name: "command"}
			command.AddCommand(&subCommand)

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(&command)
			application.Run([]string{"command", "subCommand"}, []string{})

			assert.True(t, strings.Contains(writer.String(), message), "Expected sub-command to run")
		})
	})

	t.Run("AddCommands()", func(t *testing.T) {
		t.Run("should work when adding 1 command", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{
				{
					Name: "test1",
				},
			})

			assert.Equal(t, 1, len(application.Commands()))
		})

		t.Run("should work when adding no commands", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{})

			assert.Equal(t, 0, len(application.Commands()))
		})

		t.Run("should work when adding more than 1 command", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{
				{
					Name: "test1",
				},
				{
					Name: "test2",
				},
				{
					Name: "test3",
				},
			})

			assert.Equal(t, 3, len(application.Commands()))
		})
	})

	t.Run("AddCommand()", func(t *testing.T) {
		writer := bytes.Buffer{}
		application := createApplication(&writer)

		assert.Equal(t, 0, len(application.Commands()))

		application.AddCommand(&console.Command{
			Name: "test1",
		})

		assert.Equal(t, 1, len(application.Commands()))
	})
}
<commit_msg>Added a test to run a command using an alias.<commit_after>package console_test

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"testing"

	"github.com/eidolon/console"
	"github.com/eidolon/console/assert"
	"github.com/eidolon/console/parameters"
)

func TestNewApplication(t *testing.T) {
	application := console.NewApplication("eidolon/console", "1.2.3+testing")
	assert.True(t, application != nil, "Application should not be nil")
}

func TestApplication(t *testing.T) {
	createApplication := func(writer io.Writer) *console.Application {
		application := console.NewApplication("eidolon/console", "1.2.3.+testing")
		application.Writer = writer

		return application
	}

	createTestCommand := func(a *string, b *int) *console.Command {
		return &console.Command{
			Name:  "test",
			Alias: "t",
			Configure: func(definition *console.Definition) {
				definition.AddArgument(console.ArgumentDefinition{
					Value: parameters.NewStringValue(a),
					Spec:  "STRINGARG",
				})

				definition.AddOption(console.OptionDefinition{
					Value: parameters.NewIntValue(b),
					Spec:  "--int-opt=VALUE",
				})
			},
			Execute: func(input *console.Input, output *console.Output) error {
				output.Printf("STRINGARG = %s", *a)
				output.Printf("--int-opt = %v", *b)
				return nil
			},
		}
	}

	t.Run("Run()", func(t *testing.T) {
		t.Run("should return exit code 2 if no command was asked for", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should return exit code 2 if no command was found", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{"foo"}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should return exit code 100 if the help flag is set", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			code := application.Run([]string{"--help"}, []string{})

			assert.Equal(t, 100, code)
		})

		t.Run("should show application help if the help flag is set", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.Run([]string{"--help"}, []string{})

			output := writer.String()
			containsUsage := strings.Contains(output, "USAGE:")
			containsArguments := strings.Contains(output, "ARGUMENTS:")
			containsOptions := strings.Contains(output, "OPTIONS:")
			containsHelp := containsUsage && containsOptions && !containsArguments

			assert.True(t, containsHelp, "Expected help output.")
		})

		t.Run("should show command help if the help flag is set when running a command", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))
			application.Run([]string{"test", "--help"}, []string{})

			output := writer.String()
			containsUsage := strings.Contains(output, "USAGE:")
			containsArguments := strings.Contains(output, "ARGUMENTS:")
			containsOptions := strings.Contains(output, "OPTIONS:")
			containsHelp := containsUsage && containsOptions && containsArguments

			assert.True(t, containsHelp, "Expected help output.")
		})

		t.Run("should return exit code 0 if a command was found, and ran OK", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))

			code := application.Run([]string{"test", "aval", "--int-opt=384"}, []string{})

			assert.Equal(t, 0, code)
		})

		t.Run("should return exit code 0 if a command with an alias was found, and ran OK", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))

			code := application.Run([]string{"t", "aval", "--int-opt=384"}, []string{})

			assert.Equal(t, 0, code)
		})

		t.Run("should return exit code 101 if mapping input fails", func(t *testing.T) {
			var a string
			var b int

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(createTestCommand(&a, &b))

			code := application.Run([]string{"test", "aval", "--int-opt=hello"}, []string{})

			assert.Equal(t, 101, code)
		})

		t.Run("should return exit code 102 if the command execution fails", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(&console.Command{
				Name: "test",
				Execute: func(input *console.Input, output *console.Output) error {
					return errors.New("Testing errors")
				},
			})

			code := application.Run([]string{"test", "aval", "--int-opt=hello"}, []string{})

			assert.Equal(t, 102, code)
		})

		t.Run("should configure the application definition", func(t *testing.T) {
			var a string
			var b int
			var foo string

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.Configure = func(definition *console.Definition) {
				definition.AddOption(console.OptionDefinition{
					Value: parameters.NewStringValue(&foo),
					Spec:  "--foo=FOO",
				})
			}

			application.AddCommand(createTestCommand(&a, &b))
			application.Run([]string{"test", "aval", "--foo=bar"}, []string{})

			assert.Equal(t, "bar", foo)
		})

		t.Run("should work with sub-commands", func(t *testing.T) {
			message := fmt.Sprintf("sub-command: %d", rand.Int())

			subCommand := console.Command{
				Name: "subCommand",
				Execute: func(input *console.Input, output *console.Output) error {
					output.Println(message)
					return nil
				},
			}

			command := console.Command{Name: "command"}
			command.AddCommand(&subCommand)

			writer := bytes.Buffer{}
			application := createApplication(&writer)
			application.AddCommand(&command)
			application.Run([]string{"command", "subCommand"}, []string{})

			assert.True(t, strings.Contains(writer.String(), message), "Expected sub-command to run")
		})
	})

	t.Run("AddCommands()", func(t *testing.T) {
		t.Run("should work when adding 1 command", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{
				{
					Name: "test1",
				},
			})

			assert.Equal(t, 1, len(application.Commands()))
		})

		t.Run("should work when adding no commands", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{})

			assert.Equal(t, 0, len(application.Commands()))
		})

		t.Run("should work when adding more than 1 command", func(t *testing.T) {
			writer := bytes.Buffer{}
			application := createApplication(&writer)

			assert.Equal(t, 0, len(application.Commands()))

			application.AddCommands([]*console.Command{
				{
					Name: "test1",
				},
				{
					Name: "test2",
				},
				{
					Name: "test3",
				},
			})

			assert.Equal(t, 3, len(application.Commands()))
		})
	})

	t.Run("AddCommand()", func(t *testing.T) {
		writer := bytes.Buffer{}
		application := createApplication(&writer)

		assert.Equal(t, 0, len(application.Commands()))

		application.AddCommand(&console.Command{
			Name: "test1",
		})

		assert.Equal(t, 1, len(application.Commands()))
	})
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package policies

import (
	"net/http"

	"github.com/ernestio/api-gateway/models"
)

// Delete : responds to DELETE /policies/:name: by deleting an
// existing policy
func Delete(au models.User, name string) (int, []byte) {
	var err error
	var existing models.Policy

	if err = existing.FindByName(name, &existing); err != nil {
		return 404, []byte("Not found")
	}

	if err := existing.Delete(); err != nil {
		return 500, []byte("Internal server error")
	}

	return http.StatusOK, []byte("policy deleted")
}
<commit_msg>Change policy delete response<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package policies

import (
	"net/http"

	"github.com/ernestio/api-gateway/models"
)

// Delete : responds to DELETE /policies/:name: by deleting an
// existing policy
func Delete(au models.User, name string) (int, []byte) {
	var err error
	var existing models.Policy

	if err = existing.FindByName(name, &existing); err != nil {
		return 404, []byte("policy not found")
	}

	if err := existing.Delete(); err != nil {
		return 500, []byte("Internal server error")
	}

	return http.StatusOK, []byte("policy deleted")
}
<|endoftext|>


<commit_before>package apptail

import (
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"logyard/clients/messagecommon"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Instance is the NATS message sent by dea_ng to notify of new instances.
type Instance struct {
	AppGUID  string
	AppName  string
	AppSpace string
	Type     string
	Index    int
	DockerId string `json:"docker_id"`
	RootPath string
	LogFiles map[string]string
}

func (instance *Instance) Identifier() string {
	return fmt.Sprintf("%v[%v:%v]", instance.AppName, instance.Index, instance.DockerId[:ID_LENGTH])
}

// Tail begins tailing the files for this instance.
func (instance *Instance) Tail() {
	log.Infof("Tailing %v logs for %v -- %+v",
		instance.Type, instance.Identifier(), instance)

	stopCh := make(chan bool)
	logfiles := instance.getLogFiles()

	log.Infof("Determined log files: %+v", logfiles)

	for name, filename := range logfiles {
		go instance.tailFile(name, filename, stopCh)
	}

	go func() {
		DockerListener.WaitForContainer(instance.DockerId)
		log.Infof("Container for %v exited", instance.Identifier())
		close(stopCh)
	}()
}

func (instance *Instance) tailFile(name, filename string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, name, filename)
	if err != nil {
		log.Warn(err)
		return
	}

	t, err := tail.TailFile(filename, tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   true,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		LimitRate:   GetConfig().RateLimit})
	if err != nil {
		log.Warnf("Cannot tail file (%s); %s", filename, err)
		return
	}

FORLOOP:
	for {
		select {
		case line, ok := <-t.Lines:
			if !ok {
				err = t.Wait()
				break FORLOOP
			}
			instance.publishLine(pub, name, line)
		case <-stopCh:
			err = t.Stop()
			break FORLOOP
		}
	}

	if err != nil {
		log.Warn(err)
	}

	log.Infof("Completed tailing %v log for %v", name, instance.Identifier())
}

func (instance *Instance) getLogFiles() map[string]string {
	var logfiles map[string]string

	rawMode := len(instance.LogFiles) > 0
	if rawMode {
		// If the logfiles list was explicitly passed, use it as is.
		logfiles = instance.LogFiles
	} else {
		// Use $STACKATO_LOG_FILES
		logfiles = make(map[string]string)
		if env, err := GetDockerAppEnv(instance.RootPath); err != nil {
			log.Errorf("Failed to read docker image env: %v", err)
		} else {
			if s, ok := env["STACKATO_LOG_FILES"]; ok {
				parts := strings.Split(s, ":")
				if len(parts) > 7 {
					log.Warnf("$STACKATO_LOG_FILES contains more than 7 parts; using only last 7 parts")
					parts = parts[len(parts)-7 : len(parts)]
				}
				for _, f := range parts {
					parts := strings.SplitN(f, "=", 2)
					logfiles[parts[0]] = parts[1]
				}
			} else {
				log.Errorf("Expected env $STACKATO_LOG_FILES not found in docker image")
			}
		}
	}

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()
	// XXX: this delay is unfortunately required, else the publish calls
	// (instance.notify) below for warnings will get ignored.
	time.Sleep(100 * time.Millisecond)

	// Expand paths, and securely ensure they fall within the app root.
	logfilesSecure := make(map[string]string)
	for name, path := range logfiles {
		var fullpath string

		// Treat relative paths as being relative to the app directory.
		if !filepath.IsAbs(path) {
			fullpath = filepath.Join(instance.RootPath, "/app/app/", path)
		} else {
			fullpath = filepath.Join(instance.RootPath, path)
		}

		fullpath, err := filepath.Abs(fullpath)
		if err != nil {
			log.Warnf("Cannot find Abs of %v <join> %v: %v", instance.RootPath, path, err)
			instance.notify(pub, fmt.Sprintf("WARN -- Failed to find absolute path for %v", path))
			continue
		}
		fullpath, err = filepath.EvalSymlinks(fullpath)
		if err != nil {
			instance.notify(pub, fmt.Sprintf("WARN -- Ignoring missing/inaccessible path %v", path))
			continue
		}
		if !strings.HasPrefix(fullpath, instance.RootPath) {
			log.Warnf("Ignoring insecure log path %v (via %v) in instance %+v", fullpath, path, instance)
			// This user warning is exactly the same as above, lest we provide
			// a backdoor for a malicious user to list the directory tree on
			// the host.
			instance.notify(pub, fmt.Sprintf("WARN -- Ignoring missing/inaccessible path %v", path))
			continue
		}
		logfilesSecure[name] = fullpath
	}

	return logfilesSecure
}

func (instance *Instance) getReadLimit(
	pub *zmqpubsub.Publisher,
	logname string,
	filename string) (int64, error) {
	// convert MB to limit in bytes.
	filesizeLimit := GetConfig().FileSizeLimit * 1024 * 1024
	if !(filesizeLimit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	fi, err := os.Stat(filename)
	if err != nil {
		return -1, fmt.Errorf("Cannot stat file (%s); %s", filename, err)
	}
	size := fi.Size()
	limit := filesizeLimit
	if size > filesizeLimit {
		err := fmt.Errorf("Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
			logname, size, filesizeLimit)
		// Publish special error message.
		instance.publishLine(pub, logname, &tail.Line{
			Text: err.Error(),
			Time: time.Now(),
			Err:  err})
	} else {
		limit = size
	}
	return limit, nil
}

// publishLine publishes a log line corresponding to this instance.
func (instance *Instance) publishLine(pub *zmqpubsub.Publisher, logname string, line *tail.Line) {
	instance.publishLineAs(pub, instance.Type, logname, line)
}

func (instance *Instance) notify(pub *zmqpubsub.Publisher, line string) {
	instance.publishLineAs(pub, "stackato.apptail", "", tail.NewLine(line))
}

func (instance *Instance) publishLineAs(pub *zmqpubsub.Publisher, source string, logname string, line *tail.Line) {
	if line == nil {
		panic("line is nil")
	}

	msg := &Message{
		LogFilename:   logname,
		Source:        source,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		MessageCommon: messagecommon.New(line.Text, line.Time, LocalNodeId()),
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = "stackato.apptail"
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		Fatal("unable to publish: %v", err)
	}
}
<commit_msg>inform the user if no log file is being used during tailing<commit_after>package apptail

import (
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"logyard/clients/messagecommon"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Instance is the NATS message sent by dea_ng to notify of new instances.
type Instance struct {
	AppGUID  string
	AppName  string
	AppSpace string
	Type     string
	Index    int
	DockerId string `json:"docker_id"`
	RootPath string
	LogFiles map[string]string
}

func (instance *Instance) Identifier() string {
	return fmt.Sprintf("%v[%v:%v]", instance.AppName, instance.Index, instance.DockerId[:ID_LENGTH])
}

// Tail begins tailing the files for this instance.
func (instance *Instance) Tail() {
	log.Infof("Tailing %v logs for %v -- %+v",
		instance.Type, instance.Identifier(), instance)

	stopCh := make(chan bool)
	logfiles := instance.getLogFiles()

	log.Infof("Determined log files: %+v", logfiles)

	for name, filename := range logfiles {
		go instance.tailFile(name, filename, stopCh)
	}

	go func() {
		DockerListener.WaitForContainer(instance.DockerId)
		log.Infof("Container for %v exited", instance.Identifier())
		close(stopCh)
	}()
}

func (instance *Instance) tailFile(name, filename string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, name, filename)
	if err != nil {
		log.Warn(err)
		return
	}

	t, err := tail.TailFile(filename, tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   true,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		LimitRate:   GetConfig().RateLimit})
	if err != nil {
		log.Warnf("Cannot tail file (%s); %s", filename, err)
		return
	}

FORLOOP:
	for {
		select {
		case line, ok := <-t.Lines:
			if !ok {
				err = t.Wait()
				break FORLOOP
			}
			instance.publishLine(pub, name, line)
		case <-stopCh:
			err = t.Stop()
			break FORLOOP
		}
	}

	if err != nil {
		log.Warn(err)
	}

	log.Infof("Completed tailing %v log for %v", name, instance.Identifier())
}

func (instance *Instance) getLogFiles() map[string]string {
	var logfiles map[string]string

	rawMode := len(instance.LogFiles) > 0
	if rawMode {
		// If the logfiles list was explicitly passed, use it as is.
		logfiles = instance.LogFiles
	} else {
		// Use $STACKATO_LOG_FILES
		logfiles = make(map[string]string)
		if env, err := GetDockerAppEnv(instance.RootPath); err != nil {
			log.Errorf("Failed to read docker image env: %v", err)
		} else {
			if s, ok := env["STACKATO_LOG_FILES"]; ok {
				parts := strings.Split(s, ":")
				if len(parts) > 7 {
					log.Warnf("$STACKATO_LOG_FILES contains more than 7 parts; using only last 7 parts")
					parts = parts[len(parts)-7 : len(parts)]
				}
				for _, f := range parts {
					parts := strings.SplitN(f, "=", 2)
					logfiles[parts[0]] = parts[1]
				}
			} else {
				log.Errorf("Expected env $STACKATO_LOG_FILES not found in docker image")
			}
		}
	}

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()
	// XXX: this delay is unfortunately required, else the publish calls
	// (instance.notify) below for warnings will get ignored.
	time.Sleep(100 * time.Millisecond)

	// Expand paths, and securely ensure they fall within the app root.
	logfilesSecure := make(map[string]string)
	for name, path := range logfiles {
		var fullpath string

		// Treat relative paths as being relative to the app directory.
		if !filepath.IsAbs(path) {
			fullpath = filepath.Join(instance.RootPath, "/app/app/", path)
		} else {
			fullpath = filepath.Join(instance.RootPath, path)
		}

		fullpath, err := filepath.Abs(fullpath)
		if err != nil {
			log.Warnf("Cannot find Abs of %v <join> %v: %v", instance.RootPath, path, err)
			instance.notify(pub, fmt.Sprintf("WARN -- Failed to find absolute path for %v", path))
			continue
		}
		fullpath, err = filepath.EvalSymlinks(fullpath)
		if err != nil {
			instance.notify(pub, fmt.Sprintf("WARN -- Ignoring missing/inaccessible path %v", path))
			continue
		}
		if !strings.HasPrefix(fullpath, instance.RootPath) {
			log.Warnf("Ignoring insecure log path %v (via %v) in instance %+v", fullpath, path, instance)
			// This user warning is exactly the same as above, lest we provide
			// a backdoor for a malicious user to list the directory tree on
			// the host.
			instance.notify(pub, fmt.Sprintf("WARN -- Ignoring missing/inaccessible path %v", path))
			continue
		}
		logfilesSecure[name] = fullpath
	}

	if len(logfilesSecure) == 0 {
		instance.notify(pub, fmt.Sprintf("ERROR -- No valid log files detected for tailing"))
	}

	return logfilesSecure
}

func (instance *Instance) getReadLimit(
	pub *zmqpubsub.Publisher,
	logname string,
	filename string) (int64, error) {
	// convert MB to limit in bytes.
	filesizeLimit := GetConfig().FileSizeLimit * 1024 * 1024
	if !(filesizeLimit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	fi, err := os.Stat(filename)
	if err != nil {
		return -1, fmt.Errorf("Cannot stat file (%s); %s", filename, err)
	}
	size := fi.Size()
	limit := filesizeLimit
	if size > filesizeLimit {
		err := fmt.Errorf("Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
			logname, size, filesizeLimit)
		// Publish special error message.
		instance.publishLine(pub, logname, &tail.Line{
			Text: err.Error(),
			Time: time.Now(),
			Err:  err})
	} else {
		limit = size
	}
	return limit, nil
}

// publishLine publishes a log line corresponding to this instance.
func (instance *Instance) publishLine(pub *zmqpubsub.Publisher, logname string, line *tail.Line) {
	instance.publishLineAs(pub, instance.Type, logname, line)
}

func (instance *Instance) notify(pub *zmqpubsub.Publisher, line string) {
	instance.publishLineAs(pub, "stackato.apptail", "", tail.NewLine(line))
}

func (instance *Instance) publishLineAs(pub *zmqpubsub.Publisher, source string, logname string, line *tail.Line) {
	if line == nil {
		panic("line is nil")
	}

	msg := &Message{
		LogFilename:   logname,
		Source:        source,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		MessageCommon: messagecommon.New(line.Text, line.Time, LocalNodeId()),
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = "stackato.apptail"
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		Fatal("unable to publish: %v", err)
	}
}
<|endoftext|>


<commit_before>package gateway

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

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
)

var (
	errNoPeers     = errors.New("no peers")
	errUnreachable = errors.New("peer did not respond to ping")
)

// Gateway implements the modules.Gateway interface.
type Gateway struct {
	listener net.Listener
	myAddr   modules.NetAddress
	port     string

	// handlers are the RPCs that the Gateway can handle.
	//
	// initRPCs are the RPCs that the Gateway calls upon connecting to a peer.
	handlers map[rpcID]modules.RPCFunc
	initRPCs map[string]modules.RPCFunc

	// nodes is the set of all known nodes (i.e. potential peers).
	//
	// peers are the nodes that the gateway is currently connected to.
	//
	// peerTG is a special thread group for tracking peer connections, and will
	// block shutdown until all peer connections have been closed out. The peer
	// connections are put in a separate TG because of their unique
	// requirements - they have the potential to live for the lifetime of the
	// program, but also the potential to close early. Calling threads.OnStop
	// for each peer could create a huge backlog of functions that do nothing
	// (because most of the peers disconnected prior to shutdown). And they
	// can't call threads.Add because they are potentially very long running
	// and would block any threads.Flush() calls. So a second threadgroup is
	// added which handles clean-shutdown for the peers, without blocking
	// threads.Flush() calls.
	nodes  map[modules.NetAddress]struct{}
	peers  map[modules.NetAddress]*peer
	peerTG siasync.ThreadGroup

	// Utilities.
	log        *persist.Logger
	mu         sync.RWMutex
	persistDir string
	threads    siasync.ThreadGroup
}

// Address returns the NetAddress of the Gateway.
func (g *Gateway) Address() modules.NetAddress {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.myAddr
}

// Close saves the state of the Gateway and stops its listener process.
func (g *Gateway) Close() error {
	if err := g.threads.Stop(); err != nil {
		return err
	}
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.saveSync()
}

// New returns an initialized Gateway.
func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) {
	// Create the directory if it doesn't exist.
	err := os.MkdirAll(persistDir, 0700)
	if err != nil {
		return nil, err
	}

	g := &Gateway{
		handlers: make(map[rpcID]modules.RPCFunc),
		initRPCs: make(map[string]modules.RPCFunc),

		peers: make(map[modules.NetAddress]*peer),
		nodes: make(map[modules.NetAddress]struct{}),

		persistDir: persistDir,
	}

	// Create the logger.
	g.log, err = persist.NewFileLogger(filepath.Join(g.persistDir, logFile))
	if err != nil {
		return nil, err
	}
	// Establish the closing of the logger.
	g.threads.AfterStop(func() {
		if err := g.log.Close(); err != nil {
			// The logger may or may not be working here, so use a println
			// instead.
			fmt.Println("Failed to close the gateway logger:", err)
		}
	})

	// Establish that the peerTG must complete shutdown before the primary
	// thread group completes shutdown.
	g.threads.OnStop(func() {
		err = g.peerTG.Stop()
		if err != nil {
			g.log.Println("ERROR: peerTG experienced errors while shutting down:", err)
		}
	})

	// Register RPCs.
	g.RegisterRPC("ShareNodes", g.shareNodes)
	g.RegisterConnectCall("ShareNodes", g.requestNodes)
	// Establish the de-registration of the RPCs.
	g.threads.OnStop(func() {
		g.UnregisterRPC("ShareNodes")
		g.UnregisterConnectCall("ShareNodes")
	})

	// Load the old node list. If it doesn't exist, no problem, but if it does,
	// we want to know about any errors preventing us from loading it.
	if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) {
		return nil, loadErr
	}

	// Add the bootstrap peers to the node list.
	if bootstrap {
		for _, addr := range modules.BootstrapPeers {
			err := g.addNode(addr)
			if err != nil && err != errNodeExists {
				g.log.Printf("WARN: failed to add the bootstrap node '%v': %v", addr, err)
			}
		}
	}

	// Create the listener which will listen for new connections from peers.
	permanentListenClosedChan := make(chan struct{})
	g.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	// Automatically close the listener when g.threads.Stop() is called.
	g.threads.OnStop(func() {
		err := g.listener.Close()
		if err != nil {
			g.log.Println("WARN: closing the listener failed:", err)
		}
		<-permanentListenClosedChan
	})
	// Set the address and port of the gateway.
	_, g.port, err = net.SplitHostPort(g.listener.Addr().String())
	if err != nil {
		return nil, err
	}
	// Set myAddr equal to the address returned by the listener. It will be
	// overwritten by threadedLearnHostname later on.
	g.myAddr = modules.NetAddress(g.listener.Addr().String())

	// Spawn the peer connection listener.
	go g.permanentListen(permanentListenClosedChan)

	// Spawn the peer manager and provide tools for ensuring clean shutdown.
	peerManagerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-peerManagerClosedChan
	})
	go g.permanentPeerManager(peerManagerClosedChan)

	// Spawn the node manager and provide tools for ensuring clean shudown.
	nodeManagerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-nodeManagerClosedChan
	})
	go g.permanentNodeManager(nodeManagerClosedChan)

	// Spawn the node purger and provide tools for ensuring clean shutdown.
	nodePurgerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-nodePurgerClosedChan
	})
	go g.permanentNodePurger(nodePurgerClosedChan)

	// Spawn threads to take care of port forwarding and hostname discovery.
	go g.threadedForwardPort(g.port)
	go g.threadedLearnHostname()

	g.log.Println("INFO: gateway created, started logging")
	return g, nil
}

// enforce that Gateway satisfies the modules.Gateway interface
var _ modules.Gateway = (*Gateway)(nil)
<commit_msg>add a docstring for the gateway package<commit_after>// package gateway connects a Sia node to the Sia flood network. The flood
// network is used to propagate blocks and transactions. The gateway is the
// primary avenue that a node uses to hear about transactions and blocks, and
// is the primary avenue used to tell the network about blocks that you have
// mined or about transactions that you have created.
package gateway

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

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
)

var (
	errNoPeers     = errors.New("no peers")
	errUnreachable = errors.New("peer did not respond to ping")
)

// Gateway implements the modules.Gateway interface.
type Gateway struct {
	listener net.Listener
	myAddr   modules.NetAddress
	port     string

	// handlers are the RPCs that the Gateway can handle.
	//
	// initRPCs are the RPCs that the Gateway calls upon connecting to a peer.
	handlers map[rpcID]modules.RPCFunc
	initRPCs map[string]modules.RPCFunc

	// nodes is the set of all known nodes (i.e. potential peers).
	//
	// peers are the nodes that the gateway is currently connected to.
	//
	// peerTG is a special thread group for tracking peer connections, and will
	// block shutdown until all peer connections have been closed out. The peer
	// connections are put in a separate TG because of their unique
	// requirements - they have the potential to live for the lifetime of the
	// program, but also the potential to close early. Calling threads.OnStop
	// for each peer could create a huge backlog of functions that do nothing
	// (because most of the peers disconnected prior to shutdown). And they
	// can't call threads.Add because they are potentially very long running
	// and would block any threads.Flush() calls. So a second threadgroup is
	// added which handles clean-shutdown for the peers, without blocking
	// threads.Flush() calls.
	nodes  map[modules.NetAddress]struct{}
	peers  map[modules.NetAddress]*peer
	peerTG siasync.ThreadGroup

	// Utilities.
	log        *persist.Logger
	mu         sync.RWMutex
	persistDir string
	threads    siasync.ThreadGroup
}

// Address returns the NetAddress of the Gateway.
func (g *Gateway) Address() modules.NetAddress {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.myAddr
}

// Close saves the state of the Gateway and stops its listener process.
func (g *Gateway) Close() error {
	if err := g.threads.Stop(); err != nil {
		return err
	}
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.saveSync()
}

// New returns an initialized Gateway.
func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) {
	// Create the directory if it doesn't exist.
	err := os.MkdirAll(persistDir, 0700)
	if err != nil {
		return nil, err
	}

	g := &Gateway{
		handlers: make(map[rpcID]modules.RPCFunc),
		initRPCs: make(map[string]modules.RPCFunc),

		peers: make(map[modules.NetAddress]*peer),
		nodes: make(map[modules.NetAddress]struct{}),

		persistDir: persistDir,
	}

	// Create the logger.
	g.log, err = persist.NewFileLogger(filepath.Join(g.persistDir, logFile))
	if err != nil {
		return nil, err
	}
	// Establish the closing of the logger.
	g.threads.AfterStop(func() {
		if err := g.log.Close(); err != nil {
			// The logger may or may not be working here, so use a println
			// instead.
			fmt.Println("Failed to close the gateway logger:", err)
		}
	})

	// Establish that the peerTG must complete shutdown before the primary
	// thread group completes shutdown.
	g.threads.OnStop(func() {
		err = g.peerTG.Stop()
		if err != nil {
			g.log.Println("ERROR: peerTG experienced errors while shutting down:", err)
		}
	})

	// Register RPCs.
	g.RegisterRPC("ShareNodes", g.shareNodes)
	g.RegisterConnectCall("ShareNodes", g.requestNodes)
	// Establish the de-registration of the RPCs.
	g.threads.OnStop(func() {
		g.UnregisterRPC("ShareNodes")
		g.UnregisterConnectCall("ShareNodes")
	})

	// Load the old node list. If it doesn't exist, no problem, but if it does,
	// we want to know about any errors preventing us from loading it.
	if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) {
		return nil, loadErr
	}

	// Add the bootstrap peers to the node list.
	if bootstrap {
		for _, addr := range modules.BootstrapPeers {
			err := g.addNode(addr)
			if err != nil && err != errNodeExists {
				g.log.Printf("WARN: failed to add the bootstrap node '%v': %v", addr, err)
			}
		}
	}

	// Create the listener which will listen for new connections from peers.
	permanentListenClosedChan := make(chan struct{})
	g.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	// Automatically close the listener when g.threads.Stop() is called.
	g.threads.OnStop(func() {
		err := g.listener.Close()
		if err != nil {
			g.log.Println("WARN: closing the listener failed:", err)
		}
		<-permanentListenClosedChan
	})
	// Set the address and port of the gateway.
	_, g.port, err = net.SplitHostPort(g.listener.Addr().String())
	if err != nil {
		return nil, err
	}
	// Set myAddr equal to the address returned by the listener. It will be
	// overwritten by threadedLearnHostname later on.
	g.myAddr = modules.NetAddress(g.listener.Addr().String())

	// Spawn the peer connection listener.
	go g.permanentListen(permanentListenClosedChan)

	// Spawn the peer manager and provide tools for ensuring clean shutdown.
	peerManagerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-peerManagerClosedChan
	})
	go g.permanentPeerManager(peerManagerClosedChan)

	// Spawn the node manager and provide tools for ensuring clean shudown.
	nodeManagerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-nodeManagerClosedChan
	})
	go g.permanentNodeManager(nodeManagerClosedChan)

	// Spawn the node purger and provide tools for ensuring clean shutdown.
	nodePurgerClosedChan := make(chan struct{})
	g.threads.OnStop(func() {
		<-nodePurgerClosedChan
	})
	go g.permanentNodePurger(nodePurgerClosedChan)

	// Spawn threads to take care of port forwarding and hostname discovery.
	go g.threadedForwardPort(g.port)
	go g.threadedLearnHostname()

	g.log.Println("INFO: gateway created, started logging")
	return g, nil
}

// enforce that Gateway satisfies the modules.Gateway interface
var _ modules.Gateway = (*Gateway)(nil)
<|endoftext|>


<commit_before>// Blinken by Kim Tore Jensen <https://github.com/ambientsound/wirelight>.
//
// This program sends LED updates to a LEDServer using Google Protobuf messages.

package main

import (
	"bufio"
	"fmt"
	"image"
	"image/color"
	"net"
	"os"
	"time"

	flag "github.com/ogier/pflag"
)

var (
	addr   = flag.String("address", "blinkt:1230", "LEDServer address")
	freq   = flag.Int("freq", 24, "Updates per second")
	render = flag.Int64("render", 30, "Render strip every N led update")
)

func init() {
	flag.Parse()
}

func cycleTime(freq int) time.Duration {
	return (1 * time.Second) / time.Duration(freq)
}

func main() {
	fmt.Printf("Sending UDP datagrams to %s.\n", *addr)

	sock, err := net.Dial("udp", *addr)
	if err != nil {
		fmt.Printf("while dialing LEDServer at %s: %s\n", *addr, err)
		os.Exit(1)
	}

	writer := bufio.NewWriter(sock)
	strip := NewStrip(writer, 60, 1, uint64(*render))
	rect := image.Rectangle{
		Min: image.Point{0, 0},
		Max: image.Point{60, 1},
	}
	canvas := image.NewRGBA(rect)

	go strip.Loop(canvas, *freq)
	northernLights(canvas)
}

func fill(canvas *image.RGBA, col color.Color) {
	b := canvas.Bounds()
	for x := b.Min.X; x < b.Max.X; x++ {
		for y := b.Min.Y; y < b.Max.Y; y++ {
			canvas.Set(x, y, col)
		}
	}
}
<commit_msg>Install 240 leds.<commit_after>// Blinken by Kim Tore Jensen <https://github.com/ambientsound/wirelight>.
//
// This program sends LED updates to a LEDServer using Google Protobuf messages.

package main

import (
	"bufio"
	"fmt"
	"image"
	"image/color"
	"net"
	"os"
	"time"

	flag "github.com/ogier/pflag"
)

var (
	addr   = flag.String("address", "blinkt:1230", "LEDServer address")
	freq   = flag.Int("freq", 8, "Updates per second")
	render = flag.Int64("render", 240, "Render strip every N led update")
)

func init() {
	flag.Parse()
}

func cycleTime(freq int) time.Duration {
	return (1 * time.Second) / time.Duration(freq)
}

func main() {
	fmt.Printf("Sending UDP datagrams to %s.\n", *addr)

	sock, err := net.Dial("udp", *addr)
	if err != nil {
		fmt.Printf("while dialing LEDServer at %s: %s\n", *addr, err)
		os.Exit(1)
	}

	writer := bufio.NewWriter(sock)
	strip := NewStrip(writer, 240, 1, uint64(*render))
	rect := image.Rectangle{
		Min: image.Point{0, 0},
		Max: image.Point{240, 1},
	}
	canvas := image.NewRGBA(rect)

	go strip.Loop(canvas, *freq)
	northernLights(canvas)
}

func fill(canvas *image.RGBA, col color.Color) {
	b := canvas.Bounds()
	for x := b.Min.X; x < b.Max.X; x++ {
		for y := b.Min.Y; y < b.Max.Y; y++ {
			canvas.Set(x, y, col)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blob_test

import (
	"errors"
	"testing"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/kv/mock"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

func TestKv(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type kvStoreTest struct {
	kvStore mock_kv.MockStore
	store   blob.Store
}

func (t *kvStoreTest) SetUp(i *TestInfo) {
	t.kvStore = mock_kv.NewMockStore(i.MockController, "kvStore")
	t.store = blob.NewKVStoreBlobStore(t.kvStore, "blob:")
}

////////////////////////////////////////////////////////////////////////
// Store
////////////////////////////////////////////////////////////////////////

type KvBasedStore_StoreTest struct {
	kvStoreTest

	data  []byte
	score blob.Score
	err   error
}

func init() { RegisterTestSuite(&KvBasedStore_StoreTest{}) }

func (t *KvBasedStore_StoreTest) callStore() {
	t.score, t.err = t.store.Store(t.data)
}

func (t *KvBasedStore_StoreTest) CallsContains() {
	t.data = []byte("hello")
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Contains
	ExpectCall(t.kvStore, "Contains")(expectedKey).
		WillOnce(oglemock.Return(false, errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_StoreTest) ContainsReturnsError() {
	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Contains")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_StoreTest) ContainsSaysYes() {
	t.data = []byte("hello")

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(true, nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.score, DeepEquals(blob.ComputeScore(t.data)))
}

func (t *KvBasedStore_StoreTest) CallsSet() {
	t.data = []byte("hello")
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(expectedKey, DeepEquals(t.data)).
		WillOnce(oglemock.Return(errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_StoreTest) SetReturnsError() {
	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(Any(), Any()).
		WillOnce(oglemock.Return(errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Set")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_StoreTest) SetSaysOkay() {
	t.data = []byte("hello")

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(Any(), Any()).
		WillOnce(oglemock.Return(nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.score, DeepEquals(blob.ComputeScore(t.data)))
}

////////////////////////////////////////////////////////////////////////
// Load
////////////////////////////////////////////////////////////////////////

type KvBasedStore_LoadTest struct {
	kvStoreTest

	score blob.Score
	data  []byte
	err   error
}

func init() { RegisterTestSuite(&KvBasedStore_LoadTest{}) }

func (t *KvBasedStore_LoadTest) callStore() {
	t.data, t.err = t.store.Load(t.score)
}

func (t *KvBasedStore_LoadTest) CallsGet() {
	t.score = blob.ComputeScore([]byte("hello"))
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Get
	ExpectCall(t.kvStore, "Get")(expectedKey).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_LoadTest) GetReturnsError() {
	// Get
	ExpectCall(t.kvStore, "Get")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Get")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_LoadTest) GetSucceeds() {
	// Get
	returnedData := []byte{0xde, 0xad}
	ExpectCall(t.kvStore, "Get")(Any()).
		WillOnce(oglemock.Return(returnedData, nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.data, DeepEquals(returnedData))
}
<commit_msg>Fixed test build errors.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blob_test

import (
	"errors"
	"testing"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/kv/mock"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

func TestKv(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type kvStoreTest struct {
	kvStore mock_kv.MockStore
	store   blob.Store
}

func (t *kvStoreTest) SetUp(i *TestInfo) {
	t.kvStore = mock_kv.NewMockStore(i.MockController, "kvStore")
	t.store = blob.NewKVStoreBlobStore(t.kvStore, "blob:", 1<<25, 1)
}

////////////////////////////////////////////////////////////////////////
// Store
////////////////////////////////////////////////////////////////////////

type KvBasedStore_StoreTest struct {
	kvStoreTest

	data  []byte
	score blob.Score
	err   error
}

func init() { RegisterTestSuite(&KvBasedStore_StoreTest{}) }

func (t *KvBasedStore_StoreTest) callStore() {
	t.score, t.err = t.store.Store(t.data)
}

func (t *KvBasedStore_StoreTest) CallsContains() {
	t.data = []byte("hello")
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Contains
	ExpectCall(t.kvStore, "Contains")(expectedKey).
		WillOnce(oglemock.Return(false, errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_StoreTest) ContainsReturnsError() {
	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Contains")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_StoreTest) ContainsSaysYes() {
	t.data = []byte("hello")

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(true, nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.score, DeepEquals(blob.ComputeScore(t.data)))
}

func (t *KvBasedStore_StoreTest) CallsSet() {
	t.data = []byte("hello")
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(expectedKey, DeepEquals(t.data)).
		WillOnce(oglemock.Return(errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_StoreTest) SetReturnsError() {
	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(Any(), Any()).
		WillOnce(oglemock.Return(errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Set")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_StoreTest) SetSaysOkay() {
	t.data = []byte("hello")

	// Contains
	ExpectCall(t.kvStore, "Contains")(Any()).
		WillOnce(oglemock.Return(false, nil))

	// Set
	ExpectCall(t.kvStore, "Set")(Any(), Any()).
		WillOnce(oglemock.Return(nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.score, DeepEquals(blob.ComputeScore(t.data)))
}

////////////////////////////////////////////////////////////////////////
// Load
////////////////////////////////////////////////////////////////////////

type KvBasedStore_LoadTest struct {
	kvStoreTest

	score blob.Score
	data  []byte
	err   error
}

func init() { RegisterTestSuite(&KvBasedStore_LoadTest{}) }

func (t *KvBasedStore_LoadTest) callStore() {
	t.data, t.err = t.store.Load(t.score)
}

func (t *KvBasedStore_LoadTest) CallsGet() {
	t.score = blob.ComputeScore([]byte("hello"))
	expectedKey := "blob:aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

	// Get
	ExpectCall(t.kvStore, "Get")(expectedKey).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_LoadTest) GetReturnsError() {
	// Get
	ExpectCall(t.kvStore, "Get")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callStore()

	ExpectThat(t.err, Error(HasSubstr("Get")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *KvBasedStore_LoadTest) GetSucceeds() {
	// Get
	returnedData := []byte{0xde, 0xad}
	ExpectCall(t.kvStore, "Get")(Any()).
		WillOnce(oglemock.Return(returnedData, nil))

	// Call
	t.callStore()

	AssertEq(nil, t.err)
	ExpectThat(t.data, DeepEquals(returnedData))
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blob_test

import (
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/kv/mock"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestKv(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type kvBasedStoreTest struct {
	kvBasedStore mock_kv.MockStore
	store   blob.Store
}

func (t *kvBasedStoreTest) SetUp(i *TestInfo) {
	t.kvBasedStore = mock_kv.NewMockStore(i.MockController, "kvBasedStore")
	t.store = blob.NewKvBasedBlobStore(t.kvBasedStore)
}

////////////////////////////////////////////////////////////////////////
// Store
////////////////////////////////////////////////////////////////////////

type KvBasedStore_StoreTest struct {
	kvBasedStoreTest
}

func init() { RegisterTestSuite(&KvBasedStore_StoreTest{}) }

func (t *KvBasedStore_StoreTest) CallsContains() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) ContainsReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) ContainsSaysYes() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) CallsSet() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) SetReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) SetSaysOkay() {
	ExpectEq("TODO", "")
}

////////////////////////////////////////////////////////////////////////
// Load
////////////////////////////////////////////////////////////////////////

type KvBasedStore_LoadTest struct {
	kvBasedStoreTest
}

func init() { RegisterTestSuite(&KvBasedStore_LoadTest{}) }

func (t *KvBasedStore_LoadTest) CallsGet() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_LoadTest) GetReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_LoadTest) GetSucceeds() {
	ExpectEq("TODO", "")
}
<commit_msg>KvBasedStore_StoreTest.CallsContains<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blob_test

import (
	"errors"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/kv/mock"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestKv(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type kvBasedStoreTest struct {
	kvBasedStore mock_kv.MockStore
	store   blob.Store
}

func (t *kvBasedStoreTest) SetUp(i *TestInfo) {
	t.kvBasedStore = mock_kv.NewMockStore(i.MockController, "kvBasedStore")
	t.store = blob.NewKvBasedBlobStore(t.kvBasedStore)
}

////////////////////////////////////////////////////////////////////////
// Store
////////////////////////////////////////////////////////////////////////

type KvBasedStore_StoreTest struct {
	kvBasedStoreTest

	data []byte
	score blob.Score
	err error
}

func init() { RegisterTestSuite(&KvBasedStore_StoreTest{}) }

func (t *KvBasedStore_StoreTest) callStore() {
	t.score, t.err = t.store.Store(t.data)
}

func (t *KvBasedStore_StoreTest) CallsContains() {
	t.data = []byte("hello")
	expectedScore := []byte("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d")

	// Contains
	ExpectCall(t.kvBasedStore, "Contains")(DeepEquals(expectedScore)).
		WillOnce(oglemock.Return(false, errors.New("")))

	// Call
	t.callStore()
}

func (t *KvBasedStore_StoreTest) ContainsReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) ContainsSaysYes() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) CallsSet() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) SetReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_StoreTest) SetSaysOkay() {
	ExpectEq("TODO", "")
}

////////////////////////////////////////////////////////////////////////
// Load
////////////////////////////////////////////////////////////////////////

type KvBasedStore_LoadTest struct {
	kvBasedStoreTest
}

func init() { RegisterTestSuite(&KvBasedStore_LoadTest{}) }

func (t *KvBasedStore_LoadTest) CallsGet() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_LoadTest) GetReturnsError() {
	ExpectEq("TODO", "")
}

func (t *KvBasedStore_LoadTest) GetSucceeds() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package mantle

import (
	"github.com/goibibo/mantle/backends"
)

type Mantle interface {
	Get(key string) string
	Set(key string, value interface{}) bool
	Delete(keys ...interface{}) int
	Setex(key string, duration int, value interface{}) bool
	MGet(keys ...interface{}) []string
	MSet(keyValMap map[string]interface{}) bool
	Expire(key string, duration int) bool
	Execute(cmd string, args ...interface{}) (interface{}, error)
}

//this struct is exported
type Orm struct {
	//redis|memcache|cassandra
	Driver string
	//arrays of ip:port,ip:port
	HostAndPorts []string
	//pool size
	Capacity int
	//any other options thats needed for creating a connection
	Options map[string]string
}

func (o *Orm) New() Mantle {
	poolSettings := mantle.PoolSettings{
		HostAndPorts: o.HostAndPorts,
		Capacity:     o.Capacity,
		MaxCapacity:  o.Capacity,
		Options:      o.Options}

	if o.Driver == "memcache" {
		return RedisConns(poolSettings)
	} else {
		return RedisConns(poolSettings)
	}
}

func RedisConns(settings mantle.PoolSettings) *mantle.Redis {
	redis := &mantle.Redis{}
	redis.Configure(settings)
	return redis
}
<commit_msg>override mantle to get a redis client<commit_after>package mantle

import (
	"github.com/goibibo/mantle/backends"
)

//only strings are supported
type Mantle interface {
	Get(key string) string
	Set(key string, value interface{}) bool
	Delete(keys ...interface{}) int
	Setex(key string, duration int, value interface{}) bool
	MGet(keys ...interface{}) []string
	MSet(keyValMap map[string]interface{}) bool
	Expire(key string, duration int) bool
	Execute(cmd string, args ...interface{}) (interface{}, error)
}

//helper func
func redisConns(settings mantle.PoolSettings) *mantle.Redis {
	redis := &mantle.Redis{}
	redis.Configure(settings)
	return redis
}

//generic pool settings
func getSettings(o *Orm) mantle.PoolSettings {
	return mantle.PoolSettings{
		HostAndPorts: o.HostAndPorts,
		Capacity:     o.Capacity,
		MaxCapacity:  o.Capacity,
		Options:      o.Options}

}

//this struct is exported
type Orm struct {
	//redis|memcache|cassandra
	Driver string
	//arrays of ip:port,ip:port
	HostAndPorts []string
	//pool size
	Capacity int
	//any other options thats needed for creating a connection
	Options map[string]string
}

//mantle is a wrapper for many nosql dbs
func (o *Orm) New() Mantle {
	settings := getSettings(o)
	if o.Driver == "memcache" {
		return redisConns(settings)
	} else {
		return redisConns(settings)
	}
}

//override mantle and get a redis client
func (o *Orm) GetRedisConn() (*mantle.RedisConn, error) {
	settings := getSettings(o)
	redisPool := redisConns(settings)
	return redisPool.GetClient()
}
<|endoftext|>